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