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