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