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