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