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