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