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