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