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