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