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