abstract_compiler: add `AModule::collect_linker_libs` to be refined in compiler_ffi
[nit.git] / src / abstract_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract compiler
18 module abstract_compiler
19
20 import literal
21 import typing
22 import auto_super_init
23 import frontend
24 import platform
25 import c_tools
26
27 # Add compiling options
28 redef class ToolContext
29 # --output
30 var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
31 # --no-cc
32 var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
33 # --cc-paths
34 var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
35 # --make-flags
36 var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
37 # --compile-dir
38 var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir")
39 # --hardening
40 var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
41 # --no-shortcut-range
42 var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
43 # --no-check-covariance
44 var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
45 # --no-check-initialization
46 var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization")
47 # --no-check-assert
48 var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
49 # --no-check-autocast
50 var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
51 # --no-check-other
52 var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
53 # --typing-test-metrics
54 var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
55 # --no-stacktrace
56 var opt_no_stacktrace: OptionBool = new OptionBool("Disables libunwind and generation of C stack traces (can be problematic when compiling to targets such as Android or NaCl)", "--no-stacktrace")
57 # --stack-trace-C-to-Nit-name-binding
58 var opt_stacktrace: OptionBool = new OptionBool("Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime", "--nit-stacktrace")
59
60 redef init
61 do
62 super
63 self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
64 self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
65 self.option_context.add_option(self.opt_typing_test_metrics)
66 self.option_context.add_option(self.opt_stacktrace)
67 self.option_context.add_option(self.opt_no_stacktrace)
68 end
69 end
70
71 redef class ModelBuilder
72 redef init(model, toolcontext)
73 do
74 if toolcontext.opt_no_stacktrace.value and toolcontext.opt_stacktrace.value then
75 print "Cannot use --nit-stacktrace when --no-stacktrace is activated"
76 exit(1)
77 end
78
79 super
80 end
81
82 # The compilation directory
83 var compile_dir: String
84
85 # Simple indirection to `Toolchain::write_and_make`
86 protected fun write_and_make(compiler: AbstractCompiler)
87 do
88 var platform = compiler.mainmodule.target_platform
89 var toolchain
90 if platform == null then
91 toolchain = new MakefileToolchain(toolcontext)
92 else
93 toolchain = platform.toolchain(toolcontext)
94 end
95 compile_dir = toolchain.compile_dir
96 toolchain.write_and_make compiler
97 end
98 end
99
100 redef class Platform
101 fun toolchain(toolcontext: ToolContext): Toolchain is abstract
102 end
103
104 class Toolchain
105 var toolcontext: ToolContext
106
107 fun compile_dir: String
108 do
109 var compile_dir = toolcontext.opt_compile_dir.value
110 if compile_dir == null then compile_dir = ".nit_compile"
111 return compile_dir
112 end
113
114 fun write_and_make(compiler: AbstractCompiler) is abstract
115 end
116
117 class MakefileToolchain
118 super Toolchain
119 # The list of directories to search for included C headers (-I for C compilers)
120 # The list is initially set with :
121 # * the toolcontext --cc-path option
122 # * the NIT_CC_PATH environment variable
123 # * some heuristics including the NIT_DIR environment variable and the progname of the process
124 # Path can be added (or removed) by the client
125 var cc_paths = new Array[String]
126
127 protected fun gather_cc_paths
128 do
129 # Look for the the Nit clib path
130 var path_env = "NIT_DIR".environ
131 if not path_env.is_empty then
132 var libname = "{path_env}/clib"
133 if libname.file_exists then cc_paths.add(libname)
134 end
135
136 var libname = "{sys.program_name.dirname}/../clib"
137 if libname.file_exists then cc_paths.add(libname.simplify_path)
138
139 if cc_paths.is_empty then
140 toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
141 end
142
143 # Add user defined cc_paths
144 cc_paths.append(toolcontext.opt_cc_path.value)
145
146 path_env = "NIT_CC_PATH".environ
147 if not path_env.is_empty then
148 cc_paths.append(path_env.split_with(':'))
149 end
150 end
151
152 redef fun write_and_make(compiler)
153 do
154 gather_cc_paths
155
156 var mainmodule = compiler.mainmodule
157 var compile_dir = compile_dir
158
159 # Generate the .h and .c files
160 # A single C file regroups many compiled rumtime functions
161 # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
162 var time0 = get_time
163 self.toolcontext.info("*** WRITING C ***", 1)
164
165 compile_dir.mkdir
166
167 var cfiles = new Array[String]
168 write_files(compiler, compile_dir, cfiles)
169
170 # Generate the Makefile
171
172 write_makefile(compiler, compile_dir, cfiles)
173
174 var time1 = get_time
175 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
176
177 # Execute the Makefile
178
179 if self.toolcontext.opt_no_cc.value then return
180
181 time0 = time1
182 self.toolcontext.info("*** COMPILING C ***", 1)
183
184 compile_c_code(compiler, compile_dir)
185
186 time1 = get_time
187 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
188 end
189
190 fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
191 do
192 if self.toolcontext.opt_stacktrace.value then compiler.build_c_to_nit_bindings
193
194 # Add gc_choser.h to aditionnal bodies
195 var gc_chooser = new ExternCFile("gc_chooser.c", "-DWITH_LIBGC")
196 compiler.extern_bodies.add(gc_chooser)
197 compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.c"
198 compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
199
200 # FFI
201 var m2m = toolcontext.modelbuilder.mmodule2nmodule
202 for m in compiler.mainmodule.in_importation.greaters do if m2m.keys.has(m) then
203 var amodule = m2m[m]
204 compiler.finalize_ffi_for_module(amodule)
205 end
206
207 # Copy original .[ch] files to compile_dir
208 for src in compiler.files_to_copy do
209 var basename = src.basename("")
210 var dst = "{compile_dir}/{basename}"
211 src.file_copy_to dst
212 end
213
214 var hfilename = compiler.header.file.name + ".h"
215 var hfilepath = "{compile_dir}/{hfilename}"
216 var h = new OFStream.open(hfilepath)
217 for l in compiler.header.decl_lines do
218 h.write l
219 h.write "\n"
220 end
221 for l in compiler.header.lines do
222 h.write l
223 h.write "\n"
224 end
225 h.close
226
227 for f in compiler.files do
228 var i = 0
229 var hfile: nullable OFStream = null
230 var count = 0
231 var cfilename = "{f.name}.0.h"
232 var cfilepath = "{compile_dir}/{cfilename}"
233 hfile = new OFStream.open(cfilepath)
234 hfile.write "#include \"{hfilename}\"\n"
235 for key in f.required_declarations do
236 if not compiler.provided_declarations.has_key(key) then
237 var node = compiler.requirers_of_declarations.get_or_null(key)
238 if node != null then
239 node.debug "No provided declaration for {key}"
240 else
241 print "No provided declaration for {key}"
242 end
243 abort
244 end
245 hfile.write compiler.provided_declarations[key]
246 hfile.write "\n"
247 end
248 hfile.close
249 var file: nullable OFStream = null
250 for vis in f.writers do
251 if vis == compiler.header then continue
252 var total_lines = vis.lines.length + vis.decl_lines.length
253 if total_lines == 0 then continue
254 count += total_lines
255 if file == null or count > 10000 then
256 i += 1
257 if file != null then file.close
258 cfilename = "{f.name}.{i}.c"
259 cfilepath = "{compile_dir}/{cfilename}"
260 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
261 cfiles.add(cfilename)
262 file = new OFStream.open(cfilepath)
263 file.write "#include \"{f.name}.0.h\"\n"
264 count = total_lines
265 end
266 for l in vis.decl_lines do
267 file.write l
268 file.write "\n"
269 end
270 for l in vis.lines do
271 file.write l
272 file.write "\n"
273 end
274 end
275 if file != null then file.close
276 end
277
278 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
279 end
280
281 fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
282 do
283 var mainmodule = compiler.mainmodule
284
285 var outname = self.toolcontext.opt_output.value
286 if outname == null then
287 outname = "{mainmodule.name}"
288 end
289
290 var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
291 var outpath = orig_dir.join_path(outname).simplify_path
292 var makename = "{mainmodule.name}.mk"
293 var makepath = "{compile_dir}/{makename}"
294 var makefile = new OFStream.open(makepath)
295
296 var cc_includes = ""
297 for p in cc_paths do
298 cc_includes += " -I \"" + p + "\""
299 end
300
301 var linker_options = new HashSet[String]
302 var m2m = toolcontext.modelbuilder.mmodule2nmodule
303 for m in mainmodule.in_importation.greaters do if m2m.keys.has(m) then
304 var amod = m2m[m]
305 var libs = amod.collect_linker_libs
306 if libs != null then linker_options.add_all(libs)
307 end
308
309 if not toolcontext.opt_no_stacktrace.value then linker_options.add("-lunwind")
310
311 makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc {linker_options.join(" ")}\n\n")
312 makefile.write("all: {outpath}\n\n")
313
314 var ofiles = new Array[String]
315 var dep_rules = new Array[String]
316 # Compile each generated file
317 for f in cfiles do
318 var o = f.strip_extension(".c") + ".o"
319 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -D NONITCNI -c -o {o} {f}\n\n")
320 ofiles.add(o)
321 dep_rules.add(o)
322 end
323
324 # Compile each required extern body into a specific .o
325 for f in compiler.extern_bodies do
326 var o = f.makefile_rule_name
327 var ff = f.filename.basename("")
328 makefile.write("{o}: {ff}\n")
329 makefile.write("\t{f.makefile_rule_content}\n\n")
330 dep_rules.add(f.makefile_rule_name)
331 ofiles.add(o)
332 end
333
334 # Link edition
335 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n")
336 # Clean
337 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
338 makefile.close
339 self.toolcontext.info("Generated makefile: {makepath}", 2)
340 end
341
342 fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
343 do
344 var makename = "{compiler.mainmodule.name}.mk" # FIXME duplicated from write_makefile
345
346 var makeflags = self.toolcontext.opt_make_flags.value
347 if makeflags == null then makeflags = ""
348 self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
349
350 var res
351 if self.toolcontext.verbose_level >= 3 then
352 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
353 else
354 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
355 end
356 if res != 0 then
357 toolcontext.error(null, "make failed! Error code: {res}.")
358 end
359 end
360 end
361
362 # Singleton that store the knowledge about the compilation process
363 abstract class AbstractCompiler
364 type VISITOR: AbstractCompilerVisitor
365
366 # Table corresponding c_names to nit names (methods)
367 var names = new HashMap[String, String]
368
369 # The main module of the program currently compiled
370 # Is assigned during the separate compilation
371 var mainmodule: MModule writable
372
373 # The real main module of the program
374 var realmainmodule: MModule
375
376 # The modeulbuilder used to know the model and the AST
377 var modelbuilder: ModelBuilder protected writable
378
379 # Is hardening asked? (see --hardening)
380 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
381
382 init(mainmodule: MModule, modelbuilder: ModelBuilder)
383 do
384 self.mainmodule = mainmodule
385 self.realmainmodule = mainmodule
386 self.modelbuilder = modelbuilder
387 end
388
389 # Force the creation of a new file
390 # The point is to avoid contamination between must-be-compiled-separately files
391 fun new_file(name: String): CodeFile
392 do
393 var f = new CodeFile(name)
394 self.files.add(f)
395 return f
396 end
397
398 # The list of all associated files
399 # Used to generate .c files
400 var files: List[CodeFile] = new List[CodeFile]
401
402 # Initialize a visitor specific for a compiler engine
403 fun new_visitor: VISITOR is abstract
404
405 # Where global declaration are stored (the main .h)
406 var header: CodeWriter writable
407
408 # Provide a declaration that can be requested (before or latter) by a visitor
409 fun provide_declaration(key: String, s: String)
410 do
411 if self.provided_declarations.has_key(key) then
412 assert self.provided_declarations[key] == s
413 end
414 self.provided_declarations[key] = s
415 end
416
417 private var provided_declarations = new HashMap[String, String]
418
419 private var requirers_of_declarations = new HashMap[String, ANode]
420
421 # Builds the .c and .h files to be used when generating a Stack Trace
422 # Binds the generated C function names to Nit function names
423 fun build_c_to_nit_bindings
424 do
425 var compile_dir = modelbuilder.compile_dir
426
427 var stream = new OFStream.open("{compile_dir}/C_fun_names")
428 stream.write("%\{\n#include \"c_functions_hash.h\"\n%\}\n")
429 stream.write("%define lookup-function-name get_nit_name\n")
430 stream.write("struct C_Nit_Names;\n")
431 stream.write("%%\n")
432 stream.write("####\n")
433 for i in names.keys do
434 stream.write(i)
435 stream.write(",\t\"")
436 stream.write(names[i])
437 stream.write("\"\n")
438 end
439 stream.write("####\n")
440 stream.write("%%\n")
441 stream.close
442
443 stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
444 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
445 stream.write("const struct C_Nit_Names* get_nit_name(register const char *str, register unsigned int len);\n")
446 stream.close
447
448 var x = new Process("gperf","{compile_dir}/C_fun_names","-t","-7","--output-file={compile_dir}/c_functions_hash.c","-C")
449 x.wait
450
451 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
452 end
453
454 # Compile C headers
455 # This method call compile_header_strucs method that has to be refined
456 fun compile_header do
457 var v = self.header
458 var toolctx = modelbuilder.toolcontext
459 self.header.add_decl("#include <stdlib.h>")
460 self.header.add_decl("#include <stdio.h>")
461 self.header.add_decl("#include <string.h>")
462 self.header.add_decl("#include \"gc_chooser.h\"")
463
464 compile_header_structs
465 compile_nitni_structs
466
467 # Signal handler function prototype
468 self.header.add_decl("void show_backtrace(int);")
469
470 # Global variable used by intern methods
471 self.header.add_decl("extern int glob_argc;")
472 self.header.add_decl("extern char **glob_argv;")
473 self.header.add_decl("extern val *glob_sys;")
474 end
475
476 # Declaration of structures for live Nit types
477 protected fun compile_header_structs is abstract
478
479 # Declaration of structures for nitni undelying the FFI
480 protected fun compile_nitni_structs is abstract
481
482 # Generate the main C function.
483 # This function:
484 # * allocate the Sys object if it exists
485 # * call init if is exists
486 # * call main if it exists
487 fun compile_main_function
488 do
489 var v = self.new_visitor
490 if modelbuilder.toolcontext.opt_stacktrace.value then
491 v.add_decl("#include \"c_functions_hash.h\"")
492 end
493 v.add_decl("#include <signal.h>")
494 if not modelbuilder.toolcontext.opt_no_stacktrace.value then
495 v.add_decl("#define UNW_LOCAL_ONLY")
496 v.add_decl("#include <libunwind.h>")
497 end
498 v.add_decl("int glob_argc;")
499 v.add_decl("char **glob_argv;")
500 v.add_decl("val *glob_sys;")
501
502 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
503 for tag in count_type_test_tags do
504 v.add_decl("long count_type_test_resolved_{tag};")
505 v.add_decl("long count_type_test_unresolved_{tag};")
506 v.add_decl("long count_type_test_skipped_{tag};")
507 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
508 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
509 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
510 end
511 end
512
513 v.add_decl("void sig_handler(int signo)\{")
514 v.add_decl("printf(\"Caught signal : %s\\n\", strsignal(signo));")
515 v.add_decl("show_backtrace(signo);")
516 v.add_decl("\}")
517
518 v.add_decl("void show_backtrace (int signo) \{")
519 if not modelbuilder.toolcontext.opt_no_stacktrace.value then
520 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
521 v.add_decl("unw_cursor_t cursor;")
522 v.add_decl("if(opt==NULL)\{")
523 v.add_decl("unw_context_t uc;")
524 v.add_decl("unw_word_t ip;")
525 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
526 v.add_decl("unw_getcontext(&uc);")
527 v.add_decl("unw_init_local(&cursor, &uc);")
528 v.add_decl("printf(\"-------------------------------------------------\\n\");")
529 v.add_decl("printf(\"-- Stack Trace ------------------------------\\n\");")
530 v.add_decl("printf(\"-------------------------------------------------\\n\");")
531 v.add_decl("while (unw_step(&cursor) > 0) \{")
532 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
533 if modelbuilder.toolcontext.opt_stacktrace.value then
534 v.add_decl(" const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));")
535 v.add_decl(" if (recv != 0)\{")
536 v.add_decl(" printf(\"` %s\\n\", recv->nit_name);")
537 v.add_decl(" \}else\{")
538 v.add_decl(" printf(\"` %s\\n\", procname);")
539 v.add_decl(" \}")
540 else
541 v.add_decl(" printf(\"` %s \\n\",procname);")
542 end
543 v.add_decl("\}")
544 v.add_decl("printf(\"-------------------------------------------------\\n\");")
545 v.add_decl("free(procname);")
546 v.add_decl("\}")
547 end
548 v.add_decl("exit(signo);")
549 v.add_decl("\}")
550
551 v.add_decl("int main(int argc, char** argv) \{")
552
553 v.add("signal(SIGABRT, sig_handler);")
554 v.add("signal(SIGFPE, sig_handler);")
555 v.add("signal(SIGILL, sig_handler);")
556 v.add("signal(SIGINT, sig_handler);")
557 v.add("signal(SIGTERM, sig_handler);")
558 v.add("signal(SIGSEGV, sig_handler);")
559 v.add("signal(SIGPIPE, sig_handler);")
560
561 v.add("glob_argc = argc; glob_argv = argv;")
562 v.add("initialize_gc_option();")
563 var main_type = mainmodule.sys_type
564 if main_type != null then
565 var mainmodule = v.compiler.mainmodule
566 var glob_sys = v.init_instance(main_type)
567 v.add("glob_sys = {glob_sys};")
568 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
569 if main_init != null then
570 v.send(main_init, [glob_sys])
571 end
572 var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass)
573 if main_method != null then
574 v.send(main_method, [glob_sys])
575 end
576 end
577
578 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
579 v.add_decl("long count_type_test_resolved_total = 0;")
580 v.add_decl("long count_type_test_unresolved_total = 0;")
581 v.add_decl("long count_type_test_skipped_total = 0;")
582 v.add_decl("long count_type_test_total_total = 0;")
583 for tag in count_type_test_tags do
584 v.add_decl("long count_type_test_total_{tag};")
585 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
586 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
587 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
588 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
589 v.add("count_type_test_total_total += count_type_test_total_{tag};")
590 end
591 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
592 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
593 var tags = count_type_test_tags.to_a
594 tags.add("total")
595 for tag in tags do
596 v.add("printf(\"{tag}\");")
597 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
598 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
599 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
600 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
601 end
602 end
603
604 v.add("return 0;")
605 v.add("\}")
606 end
607
608 # List of additional files required to compile (FFI)
609 var extern_bodies = new Array[ExternFile]
610
611 # List of source files to copy over to the compile dir
612 var files_to_copy = new Array[String]
613
614 # This is used to avoid adding an extern file more than once
615 private var seen_extern = new ArraySet[String]
616
617 # Generate code that initialize the attributes on a new instance
618 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
619 do
620 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
621 self.mainmodule.linearize_mclassdefs(cds)
622 for cd in cds do
623 var n = self.modelbuilder.mclassdef2nclassdef[cd]
624 for npropdef in n.n_propdefs do
625 if npropdef isa AAttrPropdef then
626 npropdef.init_expr(v, recv)
627 end
628 end
629 end
630 end
631
632 # Generate code that check if an attribute is correctly initialized
633 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
634 do
635 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
636 self.mainmodule.linearize_mclassdefs(cds)
637 for cd in cds do
638 var n = self.modelbuilder.mclassdef2nclassdef[cd]
639 for npropdef in n.n_propdefs do
640 if npropdef isa AAttrPropdef then
641 npropdef.check_expr(v, recv)
642 end
643 end
644 end
645 end
646
647 # stats
648
649 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
650 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
651 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
652 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
653
654 protected fun init_count_type_test_tags: HashMap[String, Int]
655 do
656 var res = new HashMap[String, Int]
657 for tag in count_type_test_tags do
658 res[tag] = 0
659 end
660 return res
661 end
662
663 # Display stats about compilation process
664 # Metrics used:
665 # * type tests against resolved types (`x isa Collection[Animal]`)
666 # * type tests against unresolved types (`x isa Collection[E]`)
667 # * type tests skipped
668 # * type tests total
669 # *
670 fun display_stats
671 do
672 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
673 print "# static count_type_test"
674 print "\tresolved:\tunresolved\tskipped\ttotal"
675 var count_type_test_total = init_count_type_test_tags
676 count_type_test_resolved["total"] = 0
677 count_type_test_unresolved["total"] = 0
678 count_type_test_skipped["total"] = 0
679 count_type_test_total["total"] = 0
680 for tag in count_type_test_tags do
681 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
682 count_type_test_resolved["total"] += count_type_test_resolved[tag]
683 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
684 count_type_test_skipped["total"] += count_type_test_skipped[tag]
685 count_type_test_total["total"] += count_type_test_total[tag]
686 end
687 var count_type_test = count_type_test_total["total"]
688 var tags = count_type_test_tags.to_a
689 tags.add("total")
690 for tag in tags do
691 printn tag
692 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
693 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
694 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
695 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
696 print ""
697 end
698 end
699 end
700
701 # Division facility
702 # Avoid division by zero by returning the string "n/a"
703 fun div(a,b:Int):String
704 do
705 if b == 0 then return "n/a"
706 return ((a*10000/b).to_f / 100.0).to_precision(2)
707 end
708
709 fun finalize_ffi_for_module(nmodule: AModule)
710 do
711 var visitor = new_visitor
712 nmodule.finalize_ffi(visitor, modelbuilder)
713 nmodule.finalize_nitni(visitor)
714 end
715 end
716
717 # A file unit (may be more than one file if
718 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
719 class CodeFile
720 var name: String
721 var writers = new Array[CodeWriter]
722 var required_declarations = new HashSet[String]
723 end
724
725 # Where to store generated lines
726 class CodeWriter
727 var file: CodeFile
728 var lines: List[String] = new List[String]
729 var decl_lines: List[String] = new List[String]
730
731 # Add a line in the main part of the generated C
732 fun add(s: String) do self.lines.add(s)
733
734 # Add a line in the
735 # (used for local or global declaration)
736 fun add_decl(s: String) do self.decl_lines.add(s)
737
738 init(file: CodeFile)
739 do
740 self.file = file
741 file.writers.add(self)
742 end
743 end
744
745 # A visitor on the AST of property definition that generate the C code.
746 abstract class AbstractCompilerVisitor
747
748 type COMPILER: AbstractCompiler
749
750 # The associated compiler
751 var compiler: COMPILER
752
753 # The current visited AST node
754 var current_node: nullable ANode writable = null
755
756 # The current `Frame`
757 var frame: nullable Frame writable
758
759 # Alias for self.compiler.mainmodule.object_type
760 fun object_type: MClassType do return self.compiler.mainmodule.object_type
761
762 # Alias for self.compiler.mainmodule.bool_type
763 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
764
765 var writer: CodeWriter
766
767 init(compiler: COMPILER)
768 do
769 self.compiler = compiler
770 self.writer = new CodeWriter(compiler.files.last)
771 end
772
773 # Force to get the primitive class named `name` or abort
774 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
775
776 # Force to get the primitive property named `name` in the instance `recv` or abort
777 fun get_property(name: String, recv: MType): MMethod
778 do
779 assert recv isa MClassType
780 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
781 end
782
783 fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
784 do
785 return self.send(callsite.mproperty, args)
786 end
787
788 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
789
790 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
791
792 # Transform varargs, in raw arguments, into a single argument of type `Array`
793 # Note: this method modify the given `args`
794 # If there is no vararg, then `args` is not modified.
795 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
796 do
797 var recv = args.first
798 var vararg_rank = msignature.vararg_rank
799 if vararg_rank >= 0 then
800 assert args.length >= msignature.arity + 1 # because of self
801 var rawargs = args
802 args = new Array[RuntimeVariable]
803
804 args.add(rawargs.first) # recv
805
806 for i in [0..vararg_rank[ do
807 args.add(rawargs[i+1])
808 end
809
810 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
811 var vararg = new Array[RuntimeVariable]
812 for i in [vararg_rank..vararg_lastrank] do
813 vararg.add(rawargs[i+1])
814 end
815
816 var elttype = msignature.mparameters[vararg_rank].mtype
817 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
818
819 for i in [vararg_lastrank+1..rawargs.length-1[ do
820 args.add(rawargs[i+1])
821 end
822 rawargs.clear
823 rawargs.add_all(args)
824 end
825 end
826
827 # Type handling
828
829 # Anchor a type to the main module and the current receiver
830 fun anchor(mtype: MType): MType
831 do
832 if not mtype.need_anchor then return mtype
833 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
834 end
835
836 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
837 do
838 if not mtype.need_anchor then return mtype
839 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
840 end
841
842 # Unsafely cast a value to a new type
843 # ie the result share the same C variable but my have a different mcasttype
844 # NOTE: if the adaptation is useless then `value` is returned as it.
845 # ENSURE: `result.name == value.name`
846 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
847 do
848 mtype = self.anchor(mtype)
849 var valmtype = value.mcasttype
850 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
851 return value
852 end
853
854 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
855 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
856 return res
857 else
858 var res = new RuntimeVariable(value.name, valmtype, mtype)
859 return res
860 end
861 end
862
863 # Generate a super call from a method definition
864 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
865
866 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
867
868 # Box or unbox a value to another type iff a C type conversion is needed
869 # ENSURE: `result.mtype.ctype == mtype.ctype`
870 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
871
872 # Generate a polymorphic subtype test
873 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
874
875 # Generate the code required to dynamically check if 2 objects share the same runtime type
876 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
877
878 # Generate a Nit "is" for two runtime_variables
879 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
880
881 # Sends
882
883 # Generate a static call on a method definition
884 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
885
886 # Generate a polymorphic send for the method `m` and the arguments `args`
887 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
888
889 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
890 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
891 do
892 assert t isa MClassType
893 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
894 return self.call(propdef, t, args)
895 end
896
897 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
898 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
899 do
900 assert t isa MClassType
901 m = m.lookup_next_definition(self.compiler.mainmodule, t)
902 return self.call(m, t, args)
903 end
904
905 # Attributes handling
906
907 # Generate a polymorphic attribute is_set test
908 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
909
910 # Generate a polymorphic attribute read
911 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
912
913 # Generate a polymorphic attribute write
914 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
915
916 # Checks
917
918 # Add a check and an abort for a null reciever if needed
919 fun check_recv_notnull(recv: RuntimeVariable)
920 do
921 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
922
923 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
924 if maybenull then
925 self.add("if ({recv} == NULL) \{")
926 self.add_abort("Receiver is null")
927 self.add("\}")
928 end
929 end
930
931 # Names handling
932
933 private var names: HashSet[String] = new HashSet[String]
934 private var last: Int = 0
935
936 # Return a new name based on `s` and unique in the visitor
937 fun get_name(s: String): String
938 do
939 if not self.names.has(s) then
940 self.names.add(s)
941 return s
942 end
943 var i = self.last + 1
944 loop
945 var s2 = s + i.to_s
946 if not self.names.has(s2) then
947 self.last = i
948 self.names.add(s2)
949 return s2
950 end
951 i = i + 1
952 end
953 end
954
955 # Return an unique and stable identifier associated with an escapemark
956 fun escapemark_name(e: nullable EscapeMark): String
957 do
958 assert e != null
959 if escapemark_names.has_key(e) then return escapemark_names[e]
960 var name = e.name
961 if name == null then name = "label"
962 name = get_name(name)
963 escapemark_names[e] = name
964 return name
965 end
966
967 private var escapemark_names = new HashMap[EscapeMark, String]
968
969 # Return a "const char*" variable associated to the classname of the dynamic type of an object
970 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
971 fun class_name_string(value: RuntimeVariable): String is abstract
972
973 # Variables handling
974
975 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
976
977 # Return the local runtime_variable associated to a Nit local variable
978 fun variable(variable: Variable): RuntimeVariable
979 do
980 if self.variables.has_key(variable) then
981 return self.variables[variable]
982 else
983 var name = self.get_name("var_{variable.name}")
984 var mtype = variable.declared_type.as(not null)
985 mtype = self.anchor(mtype)
986 var res = new RuntimeVariable(name, mtype, mtype)
987 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
988 self.variables[variable] = res
989 return res
990 end
991 end
992
993 # Return a new uninitialized local runtime_variable
994 fun new_var(mtype: MType): RuntimeVariable
995 do
996 mtype = self.anchor(mtype)
997 var name = self.get_name("var")
998 var res = new RuntimeVariable(name, mtype, mtype)
999 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1000 return res
1001 end
1002
1003 # Return a new uninitialized named runtime_variable
1004 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1005 do
1006 mtype = self.anchor(mtype)
1007 var res = new RuntimeVariable(name, mtype, mtype)
1008 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1009 return res
1010 end
1011
1012 # Correctly assign a left and a right value
1013 # Boxing and unboxing is performed if required
1014 fun assign(left, right: RuntimeVariable)
1015 do
1016 right = self.autobox(right, left.mtype)
1017 self.add("{left} = {right};")
1018 end
1019
1020 # Generate instances
1021
1022 # Generate a alloc-instance + init-attributes
1023 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1024
1025 # Generate an integer value
1026 fun int_instance(value: Int): RuntimeVariable
1027 do
1028 var res = self.new_var(self.get_class("Int").mclass_type)
1029 self.add("{res} = {value};")
1030 return res
1031 end
1032
1033 # Generate a string value
1034 fun string_instance(string: String): RuntimeVariable
1035 do
1036 var mtype = self.get_class("String").mclass_type
1037 var name = self.get_name("varonce")
1038 self.add_decl("static {mtype.ctype} {name};")
1039 var res = self.new_var(mtype)
1040 self.add("if ({name}) \{")
1041 self.add("{res} = {name};")
1042 self.add("\} else \{")
1043 var native_mtype = self.get_class("NativeString").mclass_type
1044 var nat = self.new_var(native_mtype)
1045 self.add("{nat} = \"{string.escape_to_c}\";")
1046 var length = self.int_instance(string.length)
1047 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1048 self.add("{name} = {res};")
1049 self.add("\}")
1050 return res
1051 end
1052
1053 # Generate an array value
1054 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1055
1056 # Get an instance of a array for a vararg
1057 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1058
1059 # Code generation
1060
1061 # Add a line in the main part of the generated C
1062 fun add(s: String) do self.writer.lines.add(s)
1063
1064 # Add a line in the
1065 # (used for local or global declaration)
1066 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1067
1068 # Request the presence of a global declaration
1069 fun require_declaration(key: String)
1070 do
1071 var reqs = self.writer.file.required_declarations
1072 if reqs.has(key) then return
1073 reqs.add(key)
1074 var node = current_node
1075 if node != null then compiler.requirers_of_declarations[key] = node
1076 end
1077
1078 # Add a declaration in the local-header
1079 # The declaration is ensured to be present once
1080 fun declare_once(s: String)
1081 do
1082 self.compiler.provide_declaration(s, s)
1083 self.require_declaration(s)
1084 end
1085
1086 # look for a needed .h and .c file for a given .nit source-file
1087 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1088 fun add_extern(file: String)
1089 do
1090 file = file.strip_extension(".nit")
1091 var tryfile = file + ".nit.h"
1092 if tryfile.file_exists then
1093 self.declare_once("#include \"{tryfile.basename("")}\"")
1094 self.compiler.files_to_copy.add(tryfile)
1095 end
1096 tryfile = file + "_nit.h"
1097 if tryfile.file_exists then
1098 self.declare_once("#include \"{tryfile.basename("")}\"")
1099 self.compiler.files_to_copy.add(tryfile)
1100 end
1101
1102 if self.compiler.seen_extern.has(file) then return
1103 self.compiler.seen_extern.add(file)
1104 tryfile = file + ".nit.c"
1105 if not tryfile.file_exists then
1106 tryfile = file + "_nit.c"
1107 if not tryfile.file_exists then return
1108 end
1109 var f = new ExternCFile(tryfile.basename(""), "")
1110 self.compiler.extern_bodies.add(f)
1111 self.compiler.files_to_copy.add(tryfile)
1112 end
1113
1114 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1115 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1116 do
1117 var res = new_var(mtype)
1118 self.add("{res} = {cexpr};")
1119 return res
1120 end
1121
1122 # Generate generic abort
1123 # used by aborts, asserts, casts, etc.
1124 fun add_abort(message: String)
1125 do
1126 self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");")
1127 add_raw_abort
1128 end
1129
1130 fun add_raw_abort
1131 do
1132 if self.current_node != null and self.current_node.location.file != null then
1133 self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1134 else
1135 self.add("fprintf(stderr, \"\\n\");")
1136 end
1137 self.add("show_backtrace(1);")
1138 end
1139
1140 # Add a dynamic cast
1141 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1142 do
1143 var res = self.type_test(value, mtype, tag)
1144 self.add("if (!{res}) \{")
1145 var cn = self.class_name_string(value)
1146 self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1147 self.add_raw_abort
1148 self.add("\}")
1149 end
1150
1151 # Generate a return with the value `s`
1152 fun ret(s: RuntimeVariable)
1153 do
1154 self.assign(self.frame.returnvar.as(not null), s)
1155 self.add("goto {self.frame.returnlabel.as(not null)};")
1156 end
1157
1158 # Compile a statement (if any)
1159 fun stmt(nexpr: nullable AExpr)
1160 do
1161 if nexpr == null then return
1162 var old = self.current_node
1163 self.current_node = nexpr
1164 nexpr.stmt(self)
1165 self.current_node = old
1166 end
1167
1168 # Compile an expression an return its result
1169 # `mtype` is the expected return type, pass null if no specific type is expected.
1170 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1171 do
1172 var old = self.current_node
1173 self.current_node = nexpr
1174 var res = nexpr.expr(self).as(not null)
1175 if mtype != null then
1176 mtype = self.anchor(mtype)
1177 res = self.autobox(res, mtype)
1178 end
1179 res = autoadapt(res, nexpr.mtype.as(not null))
1180 var implicit_cast_to = nexpr.implicit_cast_to
1181 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1182 add_cast(res, implicit_cast_to, "auto")
1183 res = autoadapt(res, implicit_cast_to)
1184 end
1185 self.current_node = old
1186 return res
1187 end
1188
1189 # Alias for `self.expr(nexpr, self.bool_type)`
1190 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1191
1192 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1193 fun debug(message: String)
1194 do
1195 var node = self.current_node
1196 if node == null then
1197 print "?: {message}"
1198 else
1199 node.debug(message)
1200 end
1201 self.add("/* DEBUG: {message} */")
1202 end
1203 end
1204
1205 # A C function associated to a Nit method
1206 # Because of customization, a given Nit method can be compiler more that once
1207 abstract class AbstractRuntimeFunction
1208
1209 type COMPILER: AbstractCompiler
1210 type VISITOR: AbstractCompilerVisitor
1211
1212 # The associated Nit method
1213 var mmethoddef: MMethodDef
1214
1215 # The mangled c name of the runtime_function
1216 # Subclasses should redefine `build_c_name` instead
1217 fun c_name: String
1218 do
1219 var res = self.c_name_cache
1220 if res != null then return res
1221 res = self.build_c_name
1222 self.c_name_cache = res
1223 return res
1224 end
1225
1226 # Non cached version of `c_name`
1227 protected fun build_c_name: String is abstract
1228
1229 protected var c_name_cache: nullable String writable = null
1230
1231 # Implements a call of the runtime_function
1232 # May inline the body or generate a C function call
1233 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1234
1235 # Generate the code for the `AbstractRuntimeFunction`
1236 # Warning: compile more than once compilation makes CC unhappy
1237 fun compile_to_c(compiler: COMPILER) is abstract
1238 end
1239
1240 # A runtime variable hold a runtime value in C.
1241 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1242 #
1243 # The tricky point is that a single C variable can be associated to more than one `RuntimeVariable` because the static knowledge of the type of an expression can vary in the C code.
1244 class RuntimeVariable
1245 # The name of the variable in the C code
1246 var name: String
1247
1248 # The static type of the variable (as declard in C)
1249 var mtype: MType
1250
1251 # The current casted type of the variable (as known in Nit)
1252 var mcasttype: MType writable
1253
1254 # If the variable exaclty a mcasttype?
1255 # false (usual value) means that the variable is a mcasttype or a subtype.
1256 var is_exact: Bool writable = false
1257
1258 init(name: String, mtype: MType, mcasttype: MType)
1259 do
1260 self.name = name
1261 self.mtype = mtype
1262 self.mcasttype = mcasttype
1263 assert not mtype.need_anchor
1264 assert not mcasttype.need_anchor
1265 end
1266
1267 redef fun to_s do return name
1268
1269 redef fun inspect
1270 do
1271 var exact_str
1272 if self.is_exact then
1273 exact_str = " exact"
1274 else
1275 exact_str = ""
1276 end
1277 var type_str
1278 if self.mtype == self.mcasttype then
1279 type_str = "{mtype}{exact_str}"
1280 else
1281 type_str = "{mtype}({mcasttype}{exact_str})"
1282 end
1283 return "<{name}:{type_str}>"
1284 end
1285 end
1286
1287 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1288 class Frame
1289
1290 type VISITOR: AbstractCompilerVisitor
1291
1292 # The associated visitor
1293 var visitor: VISITOR
1294
1295 # The executed property.
1296 # A Method in case of a call, an attribute in case of a default initialization.
1297 var mpropdef: MPropDef
1298
1299 # The static type of the receiver
1300 var receiver: MClassType
1301
1302 # Arguments of the method (the first is the receiver)
1303 var arguments: Array[RuntimeVariable]
1304
1305 # The runtime_variable associated to the return (in a function)
1306 var returnvar: nullable RuntimeVariable writable = null
1307
1308 # The label at the end of the property
1309 var returnlabel: nullable String writable = null
1310 end
1311
1312 redef class MType
1313 # Return the C type associated to a given Nit static type
1314 fun ctype: String do return "val*"
1315
1316 fun ctypename: String do return "val"
1317
1318 # Return the name of the C structure associated to a Nit live type
1319 fun c_name: String is abstract
1320 protected var c_name_cache: nullable String protected writable
1321 end
1322
1323 redef class MClassType
1324 redef fun c_name
1325 do
1326 var res = self.c_name_cache
1327 if res != null then return res
1328 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1329 self.c_name_cache = res
1330 return res
1331 end
1332
1333 redef fun ctype: String
1334 do
1335 if mclass.name == "Int" then
1336 return "long"
1337 else if mclass.name == "Bool" then
1338 return "short int"
1339 else if mclass.name == "Char" then
1340 return "char"
1341 else if mclass.name == "Float" then
1342 return "double"
1343 else if mclass.name == "NativeString" then
1344 return "char*"
1345 else if mclass.name == "NativeArray" then
1346 return "val*"
1347 else if mclass.kind == extern_kind then
1348 return "void*"
1349 else
1350 return "val*"
1351 end
1352 end
1353
1354 redef fun ctypename: String
1355 do
1356 if mclass.name == "Int" then
1357 return "l"
1358 else if mclass.name == "Bool" then
1359 return "s"
1360 else if mclass.name == "Char" then
1361 return "c"
1362 else if mclass.name == "Float" then
1363 return "d"
1364 else if mclass.name == "NativeString" then
1365 return "str"
1366 else if mclass.name == "NativeArray" then
1367 #return "{self.arguments.first.ctype}*"
1368 return "val"
1369 else if mclass.kind == extern_kind then
1370 return "ptr"
1371 else
1372 return "val"
1373 end
1374 end
1375 end
1376
1377 redef class MGenericType
1378 redef fun c_name
1379 do
1380 var res = self.c_name_cache
1381 if res != null then return res
1382 res = super
1383 for t in self.arguments do
1384 res = res + t.c_name
1385 end
1386 self.c_name_cache = res
1387 return res
1388 end
1389 end
1390
1391 redef class MParameterType
1392 redef fun c_name
1393 do
1394 var res = self.c_name_cache
1395 if res != null then return res
1396 res = "{self.mclass.c_name}_FT{self.rank}"
1397 self.c_name_cache = res
1398 return res
1399 end
1400 end
1401
1402 redef class MVirtualType
1403 redef fun c_name
1404 do
1405 var res = self.c_name_cache
1406 if res != null then return res
1407 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1408 self.c_name_cache = res
1409 return res
1410 end
1411 end
1412
1413 redef class MNullableType
1414 redef fun c_name
1415 do
1416 var res = self.c_name_cache
1417 if res != null then return res
1418 res = "nullable_{self.mtype.c_name}"
1419 self.c_name_cache = res
1420 return res
1421 end
1422 end
1423
1424 redef class MClass
1425 # Return the name of the C structure associated to a Nit class
1426 fun c_name: String do
1427 var res = self.c_name_cache
1428 if res != null then return res
1429 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1430 self.c_name_cache = res
1431 return res
1432 end
1433 private var c_name_cache: nullable String
1434 end
1435
1436 redef class MProperty
1437 fun c_name: String do
1438 var res = self.c_name_cache
1439 if res != null then return res
1440 res = "{self.intro.c_name}"
1441 self.c_name_cache = res
1442 return res
1443 end
1444 private var c_name_cache: nullable String
1445 end
1446
1447 redef class MPropDef
1448 type VISITOR: AbstractCompilerVisitor
1449
1450 private var c_name_cache: nullable String
1451
1452 # The mangled name associated to the property
1453 fun c_name: String
1454 do
1455 var res = self.c_name_cache
1456 if res != null then return res
1457 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1458 self.c_name_cache = res
1459 return res
1460 end
1461 end
1462
1463 redef class MMethodDef
1464 # Can the body be inlined?
1465 fun can_inline(v: VISITOR): Bool
1466 do
1467 var modelbuilder = v.compiler.modelbuilder
1468 if modelbuilder.mpropdef2npropdef.has_key(self) then
1469 var npropdef = modelbuilder.mpropdef2npropdef[self]
1470 return npropdef.can_inline
1471 else if self.mproperty.name == "init" then
1472 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1473 return true
1474 else
1475 abort
1476 end
1477 end
1478
1479 # Inline the body in another visitor
1480 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1481 do
1482 var modelbuilder = v.compiler.modelbuilder
1483 if modelbuilder.mpropdef2npropdef.has_key(self) then
1484 var npropdef = modelbuilder.mpropdef2npropdef[self]
1485 var oldnode = v.current_node
1486 v.current_node = npropdef
1487 self.compile_parameter_check(v, arguments)
1488 npropdef.compile_to_c(v, self, arguments)
1489 v.current_node = oldnode
1490 else if self.mproperty.name == "init" then
1491 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1492 var oldnode = v.current_node
1493 v.current_node = nclassdef
1494 self.compile_parameter_check(v, arguments)
1495 nclassdef.compile_to_c(v, self, arguments)
1496 v.current_node = oldnode
1497 else
1498 abort
1499 end
1500 return null
1501 end
1502
1503 # Generate type checks in the C code to check covariant parameters
1504 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1505 do
1506 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1507
1508 for i in [0..msignature.arity[ do
1509 # skip test for vararg since the array is instantiated with the correct polymorphic type
1510 if msignature.vararg_rank == i then continue
1511
1512 # skip if the cast is not required
1513 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1514 if not origmtype.need_anchor then continue
1515
1516 # get the parameter type
1517 var mtype = self.msignature.mparameters[i].mtype
1518
1519 # generate the cast
1520 # note that v decides if and how to implements the cast
1521 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1522 v.add_cast(arguments[i+1], mtype, "covariance")
1523 end
1524 end
1525 end
1526
1527 # Node visit
1528
1529 redef class APropdef
1530 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1531 do
1532 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1533 debug("Not yet implemented")
1534 end
1535
1536 fun can_inline: Bool do return true
1537 end
1538
1539 redef class AConcreteMethPropdef
1540 redef fun compile_to_c(v, mpropdef, arguments)
1541 do
1542 for i in [0..mpropdef.msignature.arity[ do
1543 var variable = self.n_signature.n_params[i].variable.as(not null)
1544 v.assign(v.variable(variable), arguments[i+1])
1545 end
1546 # Call the implicit super-init
1547 var auto_super_inits = self.auto_super_inits
1548 if auto_super_inits != null then
1549 var args = [arguments.first]
1550 for auto_super_init in auto_super_inits do
1551 args.clear
1552 for i in [0..auto_super_init.msignature.arity+1[ do
1553 args.add(arguments[i])
1554 end
1555 v.compile_callsite(auto_super_init, args)
1556 end
1557 end
1558 v.stmt(self.n_block)
1559 end
1560
1561 redef fun can_inline
1562 do
1563 if self.auto_super_inits != null then return false
1564 var nblock = self.n_block
1565 if nblock == null then return true
1566 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1567 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1568 return false
1569 end
1570 end
1571
1572 redef class AInternMethPropdef
1573 redef fun compile_to_c(v, mpropdef, arguments)
1574 do
1575 var pname = mpropdef.mproperty.name
1576 var cname = mpropdef.mclassdef.mclass.name
1577 var ret = mpropdef.msignature.return_mtype
1578 if ret != null then
1579 ret = v.resolve_for(ret, arguments.first)
1580 end
1581 if pname != "==" and pname != "!=" then
1582 v.adapt_signature(mpropdef, arguments)
1583 end
1584 if cname == "Int" then
1585 if pname == "output" then
1586 v.add("printf(\"%ld\\n\", {arguments.first});")
1587 return
1588 else if pname == "object_id" then
1589 v.ret(arguments.first)
1590 return
1591 else if pname == "+" then
1592 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1593 return
1594 else if pname == "-" then
1595 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1596 return
1597 else if pname == "unary -" then
1598 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1599 return
1600 else if pname == "succ" then
1601 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1602 return
1603 else if pname == "prec" then
1604 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1605 return
1606 else if pname == "*" then
1607 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1608 return
1609 else if pname == "/" then
1610 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1611 return
1612 else if pname == "%" then
1613 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1614 return
1615 else if pname == "lshift" then
1616 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1617 return
1618 else if pname == "rshift" then
1619 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1620 return
1621 else if pname == "==" then
1622 v.ret(v.equal_test(arguments[0], arguments[1]))
1623 return
1624 else if pname == "!=" then
1625 var res = v.equal_test(arguments[0], arguments[1])
1626 v.ret(v.new_expr("!{res}", ret.as(not null)))
1627 return
1628 else if pname == "<" then
1629 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1630 return
1631 else if pname == ">" then
1632 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1633 return
1634 else if pname == "<=" then
1635 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1636 return
1637 else if pname == ">=" then
1638 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1639 return
1640 else if pname == "to_f" then
1641 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1642 return
1643 else if pname == "ascii" then
1644 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1645 return
1646 end
1647 else if cname == "Char" then
1648 if pname == "output" then
1649 v.add("printf(\"%c\", {arguments.first});")
1650 return
1651 else if pname == "object_id" then
1652 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1653 return
1654 else if pname == "+" then
1655 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1656 return
1657 else if pname == "-" then
1658 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1659 return
1660 else if pname == "==" then
1661 v.ret(v.equal_test(arguments[0], arguments[1]))
1662 return
1663 else if pname == "!=" then
1664 var res = v.equal_test(arguments[0], arguments[1])
1665 v.ret(v.new_expr("!{res}", ret.as(not null)))
1666 return
1667 else if pname == "succ" then
1668 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1669 return
1670 else if pname == "prec" then
1671 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1672 return
1673 else if pname == "<" then
1674 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1675 return
1676 else if pname == ">" then
1677 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1678 return
1679 else if pname == "<=" then
1680 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1681 return
1682 else if pname == ">=" then
1683 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1684 return
1685 else if pname == "to_i" then
1686 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1687 return
1688 else if pname == "ascii" then
1689 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1690 return
1691 end
1692 else if cname == "Bool" then
1693 if pname == "output" then
1694 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1695 return
1696 else if pname == "object_id" then
1697 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1698 return
1699 else if pname == "==" then
1700 v.ret(v.equal_test(arguments[0], arguments[1]))
1701 return
1702 else if pname == "!=" then
1703 var res = v.equal_test(arguments[0], arguments[1])
1704 v.ret(v.new_expr("!{res}", ret.as(not null)))
1705 return
1706 end
1707 else if cname == "Float" then
1708 if pname == "output" then
1709 v.add("printf(\"%f\\n\", {arguments.first});")
1710 return
1711 else if pname == "object_id" then
1712 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1713 return
1714 else if pname == "+" then
1715 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1716 return
1717 else if pname == "-" then
1718 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1719 return
1720 else if pname == "unary -" then
1721 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1722 return
1723 else if pname == "succ" then
1724 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1725 return
1726 else if pname == "prec" then
1727 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1728 return
1729 else if pname == "*" then
1730 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1731 return
1732 else if pname == "/" then
1733 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1734 return
1735 else if pname == "==" then
1736 v.ret(v.equal_test(arguments[0], arguments[1]))
1737 return
1738 else if pname == "!=" then
1739 var res = v.equal_test(arguments[0], arguments[1])
1740 v.ret(v.new_expr("!{res}", ret.as(not null)))
1741 return
1742 else if pname == "<" then
1743 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1744 return
1745 else if pname == ">" then
1746 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1747 return
1748 else if pname == "<=" then
1749 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1750 return
1751 else if pname == ">=" then
1752 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1753 return
1754 else if pname == "to_i" then
1755 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1756 return
1757 end
1758 else if cname == "NativeString" then
1759 if pname == "[]" then
1760 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1761 return
1762 else if pname == "[]=" then
1763 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1764 return
1765 else if pname == "copy_to" then
1766 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1767 return
1768 else if pname == "atoi" then
1769 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1770 return
1771 end
1772 else if cname == "NativeArray" then
1773 v.native_array_def(pname, ret, arguments)
1774 return
1775 end
1776 if pname == "exit" then
1777 v.add("exit({arguments[1]});")
1778 return
1779 else if pname == "sys" then
1780 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1781 return
1782 else if pname == "calloc_string" then
1783 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1784 return
1785 else if pname == "calloc_array" then
1786 v.calloc_array(ret.as(not null), arguments)
1787 return
1788 else if pname == "object_id" then
1789 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1790 return
1791 else if pname == "is_same_type" then
1792 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1793 return
1794 else if pname == "is_same_instance" then
1795 v.ret(v.equal_test(arguments[0], arguments[1]))
1796 return
1797 else if pname == "output_class_name" then
1798 var nat = v.class_name_string(arguments.first)
1799 v.add("printf(\"%s\\n\", {nat});")
1800 return
1801 else if pname == "native_class_name" then
1802 var nat = v.class_name_string(arguments.first)
1803 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1804 return
1805 else if pname == "force_garbage_collection" then
1806 v.add("nit_gcollect();")
1807 return
1808 else if pname == "native_argc" then
1809 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1810 return
1811 else if pname == "native_argv" then
1812 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1813 return
1814 end
1815 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1816 debug("Not implemented {mpropdef}")
1817 end
1818 end
1819
1820 redef class AExternMethPropdef
1821 redef fun compile_to_c(v, mpropdef, arguments)
1822 do
1823 var externname
1824 var nextern = self.n_extern
1825 if nextern == null then
1826 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1827 v.add("show_backtrace(1);")
1828 return
1829 end
1830 externname = nextern.text.substring(1, nextern.text.length-2)
1831 if location.file != null then
1832 var file = location.file.filename
1833 v.add_extern(file)
1834 end
1835 var res: nullable RuntimeVariable = null
1836 var ret = mpropdef.msignature.return_mtype
1837 if ret != null then
1838 ret = v.resolve_for(ret, arguments.first)
1839 res = v.new_var(ret)
1840 end
1841 v.adapt_signature(mpropdef, arguments)
1842
1843 if res == null then
1844 v.add("{externname}({arguments.join(", ")});")
1845 else
1846 v.add("{res} = {externname}({arguments.join(", ")});")
1847 v.ret(res)
1848 end
1849 end
1850 end
1851
1852 redef class AExternInitPropdef
1853 redef fun compile_to_c(v, mpropdef, arguments)
1854 do
1855 var externname
1856 var nextern = self.n_extern
1857 if nextern == null then
1858 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1859 v.add("show_backtrace(1);")
1860 return
1861 end
1862 externname = nextern.text.substring(1, nextern.text.length-2)
1863 if location.file != null then
1864 var file = location.file.filename
1865 v.add_extern(file)
1866 end
1867 v.adapt_signature(mpropdef, arguments)
1868 var ret = arguments.first.mtype
1869 var res = v.new_var(ret)
1870
1871 arguments.shift
1872
1873 v.add("{res} = {externname}({arguments.join(", ")});")
1874 v.ret(res)
1875 end
1876 end
1877
1878 redef class AAttrPropdef
1879 redef fun compile_to_c(v, mpropdef, arguments)
1880 do
1881 if arguments.length == 1 then
1882 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1883 v.assign(v.frame.returnvar.as(not null), res)
1884 else
1885 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1886 end
1887 end
1888
1889 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1890 do
1891 var nexpr = self.n_expr
1892 if nexpr != null then
1893 var oldnode = v.current_node
1894 v.current_node = self
1895 var old_frame = v.frame
1896 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1897 v.frame = frame
1898 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1899 v.write_attribute(self.mpropdef.mproperty, recv, value)
1900 v.frame = old_frame
1901 v.current_node = oldnode
1902 end
1903 end
1904
1905 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1906 do
1907 var nexpr = self.n_expr
1908 if nexpr != null then return
1909
1910 var oldnode = v.current_node
1911 v.current_node = self
1912 var old_frame = v.frame
1913 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1914 v.frame = frame
1915 # Force read to check the initialization
1916 v.read_attribute(self.mpropdef.mproperty, recv)
1917 v.frame = old_frame
1918 v.current_node = oldnode
1919 end
1920 end
1921
1922 redef class AClassdef
1923 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1924 do
1925 if mpropdef == self.mfree_init then
1926 var super_inits = self.super_inits
1927 if super_inits != null then
1928 assert arguments.length == 1
1929 for su in super_inits do
1930 v.send(su, arguments)
1931 end
1932 return
1933 end
1934 var recv = arguments.first
1935 var i = 1
1936 # Collect undefined attributes
1937 for npropdef in self.n_propdefs do
1938 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
1939 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
1940 i += 1
1941 end
1942 end
1943 else
1944 abort
1945 end
1946 end
1947 end
1948
1949 redef class ADeferredMethPropdef
1950 redef fun compile_to_c(v, mpropdef, arguments) do
1951 var cn = v.class_name_string(arguments.first)
1952 v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1953 v.add_raw_abort
1954 end
1955 redef fun can_inline do return true
1956 end
1957
1958 redef class AExpr
1959 # Try to compile self as an expression
1960 # Do not call this method directly, use `v.expr` instead
1961 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
1962 do
1963 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
1964 var mtype = self.mtype
1965 if mtype == null then
1966 return null
1967 else
1968 var res = v.new_var(mtype)
1969 v.add("/* {res} = NOT YET {class_name} */")
1970 return res
1971 end
1972 end
1973
1974 # Try to compile self as a statement
1975 # Do not call this method directly, use `v.stmt` instead
1976 private fun stmt(v: AbstractCompilerVisitor)
1977 do
1978 var res = expr(v)
1979 if res != null then v.add("{res};")
1980 end
1981 end
1982
1983 redef class ABlockExpr
1984 redef fun stmt(v)
1985 do
1986 for e in self.n_expr do v.stmt(e)
1987 end
1988 redef fun expr(v)
1989 do
1990 var last = self.n_expr.last
1991 for e in self.n_expr do
1992 if e == last then break
1993 v.stmt(e)
1994 end
1995 return v.expr(last, null)
1996 end
1997 end
1998
1999 redef class AVardeclExpr
2000 redef fun stmt(v)
2001 do
2002 var variable = self.variable.as(not null)
2003 var ne = self.n_expr
2004 if ne != null then
2005 var i = v.expr(ne, variable.declared_type)
2006 v.assign(v.variable(variable), i)
2007 end
2008 end
2009 end
2010
2011 redef class AVarExpr
2012 redef fun expr(v)
2013 do
2014 var res = v.variable(self.variable.as(not null))
2015 var mtype = self.mtype.as(not null)
2016 return v.autoadapt(res, mtype)
2017 end
2018 end
2019
2020 redef class AVarAssignExpr
2021 redef fun stmt(v)
2022 do
2023 var variable = self.variable.as(not null)
2024 var i = v.expr(self.n_value, variable.declared_type)
2025 v.assign(v.variable(variable), i)
2026 end
2027 redef fun expr(v)
2028 do
2029 var variable = self.variable.as(not null)
2030 var i = v.expr(self.n_value, variable.declared_type)
2031 v.assign(v.variable(variable), i)
2032 return i
2033 end
2034 end
2035
2036 redef class AVarReassignExpr
2037 redef fun stmt(v)
2038 do
2039 var variable = self.variable.as(not null)
2040 var vari = v.variable(variable)
2041 var value = v.expr(self.n_value, variable.declared_type)
2042 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2043 assert res != null
2044 v.assign(v.variable(variable), res)
2045 end
2046 end
2047
2048 redef class ASelfExpr
2049 redef fun expr(v) do return v.frame.arguments.first
2050 end
2051
2052 redef class AContinueExpr
2053 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2054 end
2055
2056 redef class ABreakExpr
2057 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2058 end
2059
2060 redef class AReturnExpr
2061 redef fun stmt(v)
2062 do
2063 var nexpr = self.n_expr
2064 if nexpr != null then
2065 var returnvar = v.frame.returnvar.as(not null)
2066 var i = v.expr(nexpr, returnvar.mtype)
2067 v.assign(returnvar, i)
2068 end
2069 v.add("goto {v.frame.returnlabel.as(not null)};")
2070 end
2071 end
2072
2073 redef class AAbortExpr
2074 redef fun stmt(v) do v.add_abort("Aborted")
2075 end
2076
2077 redef class AIfExpr
2078 redef fun stmt(v)
2079 do
2080 var cond = v.expr_bool(self.n_expr)
2081 v.add("if ({cond})\{")
2082 v.stmt(self.n_then)
2083 v.add("\} else \{")
2084 v.stmt(self.n_else)
2085 v.add("\}")
2086 end
2087
2088 redef fun expr(v)
2089 do
2090 var res = v.new_var(self.mtype.as(not null))
2091 var cond = v.expr_bool(self.n_expr)
2092 v.add("if ({cond})\{")
2093 v.assign(res, v.expr(self.n_then.as(not null), null))
2094 v.add("\} else \{")
2095 v.assign(res, v.expr(self.n_else.as(not null), null))
2096 v.add("\}")
2097 return res
2098 end
2099 end
2100
2101 redef class AIfexprExpr
2102 redef fun expr(v)
2103 do
2104 var res = v.new_var(self.mtype.as(not null))
2105 var cond = v.expr_bool(self.n_expr)
2106 v.add("if ({cond})\{")
2107 v.assign(res, v.expr(self.n_then, null))
2108 v.add("\} else \{")
2109 v.assign(res, v.expr(self.n_else, null))
2110 v.add("\}")
2111 return res
2112 end
2113 end
2114
2115 redef class ADoExpr
2116 redef fun stmt(v)
2117 do
2118 v.stmt(self.n_block)
2119 var escapemark = self.escapemark
2120 if escapemark != null then
2121 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2122 end
2123 end
2124 end
2125
2126 redef class AWhileExpr
2127 redef fun stmt(v)
2128 do
2129 v.add("for(;;) \{")
2130 var cond = v.expr_bool(self.n_expr)
2131 v.add("if (!{cond}) break;")
2132 v.stmt(self.n_block)
2133 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2134 v.add("\}")
2135 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2136 end
2137 end
2138
2139 redef class ALoopExpr
2140 redef fun stmt(v)
2141 do
2142 v.add("for(;;) \{")
2143 v.stmt(self.n_block)
2144 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2145 v.add("\}")
2146 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2147 end
2148 end
2149
2150 redef class AForExpr
2151 redef fun stmt(v)
2152 do
2153 # Shortcut on explicit range
2154 # Avoid the instantiation of the range and the iterator
2155 var nexpr = self.n_expr
2156 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2157 var from = v.expr(nexpr.n_expr, null)
2158 var to = v.expr(nexpr.n_expr2, null)
2159 var variable = v.variable(variables.first)
2160
2161 v.assign(variable, from)
2162 v.add("for(;;) \{ /* shortcut range */")
2163
2164 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2165 assert ok != null
2166 v.add("if(!{ok}) break;")
2167
2168 v.stmt(self.n_block)
2169
2170 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2171 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2172 assert succ != null
2173 v.assign(variable, succ)
2174 v.add("\}")
2175 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2176 return
2177 end
2178
2179 var cl = v.expr(self.n_expr, null)
2180 var it_meth = self.method_iterator
2181 assert it_meth != null
2182 var it = v.compile_callsite(it_meth, [cl])
2183 assert it != null
2184 v.add("for(;;) \{")
2185 var isok_meth = self.method_is_ok
2186 assert isok_meth != null
2187 var ok = v.compile_callsite(isok_meth, [it])
2188 assert ok != null
2189 v.add("if(!{ok}) break;")
2190 if self.variables.length == 1 then
2191 var item_meth = self.method_item
2192 assert item_meth != null
2193 var i = v.compile_callsite(item_meth, [it])
2194 assert i != null
2195 v.assign(v.variable(variables.first), i)
2196 else if self.variables.length == 2 then
2197 var key_meth = self.method_key
2198 assert key_meth != null
2199 var i = v.compile_callsite(key_meth, [it])
2200 assert i != null
2201 v.assign(v.variable(variables[0]), i)
2202 var item_meth = self.method_item
2203 assert item_meth != null
2204 i = v.compile_callsite(item_meth, [it])
2205 assert i != null
2206 v.assign(v.variable(variables[1]), i)
2207 else
2208 abort
2209 end
2210 v.stmt(self.n_block)
2211 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2212 var next_meth = self.method_next
2213 assert next_meth != null
2214 v.compile_callsite(next_meth, [it])
2215 v.add("\}")
2216 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2217 end
2218 end
2219
2220 redef class AAssertExpr
2221 redef fun stmt(v)
2222 do
2223 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2224
2225 var cond = v.expr_bool(self.n_expr)
2226 v.add("if (!{cond}) \{")
2227 v.stmt(self.n_else)
2228 var nid = self.n_id
2229 if nid != null then
2230 v.add_abort("Assert '{nid.text}' failed")
2231 else
2232 v.add_abort("Assert failed")
2233 end
2234 v.add("\}")
2235 end
2236 end
2237
2238 redef class AOrExpr
2239 redef fun expr(v)
2240 do
2241 var res = v.new_var(self.mtype.as(not null))
2242 var i1 = v.expr_bool(self.n_expr)
2243 v.add("if ({i1}) \{")
2244 v.add("{res} = 1;")
2245 v.add("\} else \{")
2246 var i2 = v.expr_bool(self.n_expr2)
2247 v.add("{res} = {i2};")
2248 v.add("\}")
2249 return res
2250 end
2251 end
2252
2253 redef class AImpliesExpr
2254 redef fun expr(v)
2255 do
2256 var res = v.new_var(self.mtype.as(not null))
2257 var i1 = v.expr_bool(self.n_expr)
2258 v.add("if (!{i1}) \{")
2259 v.add("{res} = 1;")
2260 v.add("\} else \{")
2261 var i2 = v.expr_bool(self.n_expr2)
2262 v.add("{res} = {i2};")
2263 v.add("\}")
2264 return res
2265 end
2266 end
2267
2268 redef class AAndExpr
2269 redef fun expr(v)
2270 do
2271 var res = v.new_var(self.mtype.as(not null))
2272 var i1 = v.expr_bool(self.n_expr)
2273 v.add("if (!{i1}) \{")
2274 v.add("{res} = 0;")
2275 v.add("\} else \{")
2276 var i2 = v.expr_bool(self.n_expr2)
2277 v.add("{res} = {i2};")
2278 v.add("\}")
2279 return res
2280 end
2281 end
2282
2283 redef class ANotExpr
2284 redef fun expr(v)
2285 do
2286 var cond = v.expr_bool(self.n_expr)
2287 return v.new_expr("!{cond}", self.mtype.as(not null))
2288 end
2289 end
2290
2291 redef class AOrElseExpr
2292 redef fun expr(v)
2293 do
2294 var res = v.new_var(self.mtype.as(not null))
2295 var i1 = v.expr(self.n_expr, null)
2296 v.add("if ({i1}!=NULL) \{")
2297 v.assign(res, i1)
2298 v.add("\} else \{")
2299 var i2 = v.expr(self.n_expr2, null)
2300 v.assign(res, i2)
2301 v.add("\}")
2302 return res
2303 end
2304 end
2305
2306 redef class AIntExpr
2307 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2308 end
2309
2310 redef class AFloatExpr
2311 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2312 end
2313
2314 redef class ACharExpr
2315 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2316 end
2317
2318 redef class AArrayExpr
2319 redef fun expr(v)
2320 do
2321 var mtype = self.mtype.as(MClassType).arguments.first
2322 var array = new Array[RuntimeVariable]
2323 for nexpr in self.n_exprs.n_exprs do
2324 var i = v.expr(nexpr, mtype)
2325 array.add(i)
2326 end
2327 return v.array_instance(array, mtype)
2328 end
2329 end
2330
2331 redef class AStringFormExpr
2332 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2333 end
2334
2335 redef class ASuperstringExpr
2336 redef fun expr(v)
2337 do
2338 var array = new Array[RuntimeVariable]
2339 for ne in self.n_exprs do
2340 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2341 var i = v.expr(ne, null)
2342 array.add(i)
2343 end
2344 var a = v.array_instance(array, v.object_type)
2345 var res = v.send(v.get_property("to_s", a.mtype), [a])
2346 return res
2347 end
2348 end
2349
2350 redef class ACrangeExpr
2351 redef fun expr(v)
2352 do
2353 var i1 = v.expr(self.n_expr, null)
2354 var i2 = v.expr(self.n_expr2, null)
2355 var mtype = self.mtype.as(MClassType)
2356 var res = v.init_instance(mtype)
2357 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2358 return res
2359 end
2360 end
2361
2362 redef class AOrangeExpr
2363 redef fun expr(v)
2364 do
2365 var i1 = v.expr(self.n_expr, null)
2366 var i2 = v.expr(self.n_expr2, null)
2367 var mtype = self.mtype.as(MClassType)
2368 var res = v.init_instance(mtype)
2369 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2370 return res
2371 end
2372 end
2373
2374 redef class ATrueExpr
2375 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2376 end
2377
2378 redef class AFalseExpr
2379 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2380 end
2381
2382 redef class ANullExpr
2383 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2384 end
2385
2386 redef class AIsaExpr
2387 redef fun expr(v)
2388 do
2389 var i = v.expr(self.n_expr, null)
2390 return v.type_test(i, self.cast_type.as(not null), "isa")
2391 end
2392 end
2393
2394 redef class AAsCastExpr
2395 redef fun expr(v)
2396 do
2397 var i = v.expr(self.n_expr, null)
2398 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2399
2400 v.add_cast(i, self.mtype.as(not null), "as")
2401 return i
2402 end
2403 end
2404
2405 redef class AAsNotnullExpr
2406 redef fun expr(v)
2407 do
2408 var i = v.expr(self.n_expr, null)
2409 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2410
2411 v.add("if ({i} == NULL) \{")
2412 v.add_abort("Cast failed")
2413 v.add("\}")
2414 return i
2415 end
2416 end
2417
2418 redef class AParExpr
2419 redef fun expr(v) do return v.expr(self.n_expr, null)
2420 end
2421
2422 redef class AOnceExpr
2423 redef fun expr(v)
2424 do
2425 var mtype = self.mtype.as(not null)
2426 var name = v.get_name("varonce")
2427 var guard = v.get_name(name + "_guard")
2428 v.add_decl("static {mtype.ctype} {name};")
2429 v.add_decl("static int {guard};")
2430 var res = v.new_var(mtype)
2431 v.add("if ({guard}) \{")
2432 v.add("{res} = {name};")
2433 v.add("\} else \{")
2434 var i = v.expr(self.n_expr, mtype)
2435 v.add("{res} = {i};")
2436 v.add("{name} = {res};")
2437 v.add("{guard} = 1;")
2438 v.add("\}")
2439 return res
2440 end
2441 end
2442
2443 redef class ASendExpr
2444 redef fun expr(v)
2445 do
2446 var recv = v.expr(self.n_expr, null)
2447 var args = [recv]
2448 for a in self.raw_arguments.as(not null) do
2449 args.add(v.expr(a, null))
2450 end
2451 return v.compile_callsite(self.callsite.as(not null), args)
2452 end
2453 end
2454
2455 redef class ASendReassignFormExpr
2456 redef fun stmt(v)
2457 do
2458 var recv = v.expr(self.n_expr, null)
2459 var args = [recv]
2460 for a in self.raw_arguments.as(not null) do
2461 args.add(v.expr(a, null))
2462 end
2463 var value = v.expr(self.n_value, null)
2464
2465 var left = v.compile_callsite(self.callsite.as(not null), args)
2466 assert left != null
2467
2468 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2469 assert res != null
2470
2471 args.add(res)
2472 v.compile_callsite(self.write_callsite.as(not null), args)
2473 end
2474 end
2475
2476 redef class ASuperExpr
2477 redef fun expr(v)
2478 do
2479 var recv = v.frame.arguments.first
2480 var args = [recv]
2481 for a in self.n_args.n_exprs do
2482 args.add(v.expr(a, null))
2483 end
2484
2485 var callsite = self.callsite
2486 if callsite != null then
2487 # Add additionnals arguments for the super init call
2488 if args.length == 1 then
2489 for i in [0..callsite.mproperty.intro.msignature.arity[ do
2490 args.add(v.frame.arguments[i+1])
2491 end
2492 end
2493 # Super init call
2494 var res = v.compile_callsite(callsite, args)
2495 return res
2496 end
2497
2498 if args.length == 1 then
2499 args = v.frame.arguments
2500 end
2501
2502 # stantard call-next-method
2503 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2504 end
2505 end
2506
2507 redef class ANewExpr
2508 redef fun expr(v)
2509 do
2510 var mtype = self.mtype.as(MClassType)
2511 var recv
2512 var ctype = mtype.ctype
2513 if ctype == "val*" then
2514 recv = v.init_instance(mtype)
2515 else if ctype == "void*" then
2516 recv = v.new_expr("NULL/*special!*/", mtype)
2517 else
2518 debug("cannot new {mtype}")
2519 abort
2520 end
2521 var args = [recv]
2522 for a in self.n_args.n_exprs do
2523 args.add(v.expr(a, null))
2524 end
2525 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2526 if res2 != null then
2527 #self.debug("got {res2} from {mproperty}. drop {recv}")
2528 return res2
2529 end
2530 return recv
2531 end
2532 end
2533
2534 redef class AAttrExpr
2535 redef fun expr(v)
2536 do
2537 var recv = v.expr(self.n_expr, null)
2538 var mproperty = self.mproperty.as(not null)
2539 return v.read_attribute(mproperty, recv)
2540 end
2541 end
2542
2543 redef class AAttrAssignExpr
2544 redef fun stmt(v)
2545 do
2546 var recv = v.expr(self.n_expr, null)
2547 var i = v.expr(self.n_value, null)
2548 var mproperty = self.mproperty.as(not null)
2549 v.write_attribute(mproperty, recv, i)
2550 end
2551 end
2552
2553 redef class AAttrReassignExpr
2554 redef fun stmt(v)
2555 do
2556 var recv = v.expr(self.n_expr, null)
2557 var value = v.expr(self.n_value, null)
2558 var mproperty = self.mproperty.as(not null)
2559 var attr = v.read_attribute(mproperty, recv)
2560 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2561 assert res != null
2562 v.write_attribute(mproperty, recv, res)
2563 end
2564 end
2565
2566 redef class AIssetAttrExpr
2567 redef fun expr(v)
2568 do
2569 var recv = v.expr(self.n_expr, null)
2570 var mproperty = self.mproperty.as(not null)
2571 return v.isset_attribute(mproperty, recv)
2572 end
2573 end
2574
2575 redef class ADebugTypeExpr
2576 redef fun stmt(v)
2577 do
2578 # do nothing
2579 end
2580 end
2581
2582 # Utils
2583
2584 redef class Array[E]
2585 # Return a new `Array` with the elements only contened in self and not in `o`
2586 fun -(o: Array[E]): Array[E] do
2587 var res = new Array[E]
2588 for e in self do if not o.has(e) then res.add(e)
2589 return res
2590 end
2591 end
2592
2593 redef class MModule
2594 # All `MProperty` associated to all `MClassDef` of `mclass`
2595 fun properties(mclass: MClass): Set[MProperty] do
2596 if not self.properties_cache.has_key(mclass) then
2597 var properties = new HashSet[MProperty]
2598 var parents = new Array[MClass]
2599 if self.flatten_mclass_hierarchy.has(mclass) then
2600 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2601 end
2602 for parent in parents do
2603 properties.add_all(self.properties(parent))
2604 end
2605 for mclassdef in mclass.mclassdefs do
2606 for mprop in mclassdef.intro_mproperties do
2607 properties.add(mprop)
2608 end
2609 end
2610 self.properties_cache[mclass] = properties
2611 end
2612 return properties_cache[mclass]
2613 end
2614 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2615 end
2616
2617 redef class AModule
2618 # Does this module use the legacy native interface?
2619 fun uses_legacy_ni: Bool is abstract
2620
2621 # Write FFI results to file
2622 fun finalize_ffi(v: AbstractCompilerVisitor, modelbuilder: ModelBuilder) do end
2623
2624 # Write nitni results to file
2625 fun finalize_nitni(v: AbstractCompilerVisitor) do end
2626
2627 # Give requided addinional system libraries (as given to LD_LIBS)
2628 # Note: can return null instead of an empty set
2629 fun collect_linker_libs: nullable Set[String] do return null
2630 end