lib/websocket: Websocket now subclass of BufferedIStream, OStream and Pollable
[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 var platform = mainmodule.target_platform
544 if platform != null and not platform.supports_libunwind then
545 ost = "none"
546 else
547 ost = "nitstack"
548 end
549 modelbuilder.toolcontext.opt_stacktrace.value = ost
550 end
551
552 if ost == "nitstack" or ost == "libunwind" then
553 v.add_decl("#define UNW_LOCAL_ONLY")
554 v.add_decl("#include <libunwind.h>")
555 if ost == "nitstack" then
556 v.add_decl("#include \"c_functions_hash.h\"")
557 end
558 end
559 v.add_decl("int glob_argc;")
560 v.add_decl("char **glob_argv;")
561 v.add_decl("val *glob_sys;")
562
563 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
564 for tag in count_type_test_tags do
565 v.add_decl("long count_type_test_resolved_{tag};")
566 v.add_decl("long count_type_test_unresolved_{tag};")
567 v.add_decl("long count_type_test_skipped_{tag};")
568 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
569 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
570 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
571 end
572 end
573
574 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
575 v.add_decl("long count_invoke_by_tables;")
576 v.add_decl("long count_invoke_by_direct;")
577 v.add_decl("long count_invoke_by_inline;")
578 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
579 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
580 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
581 end
582
583 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
584 v.add_decl("long count_attr_reads = 0;")
585 v.add_decl("long count_isset_checks = 0;")
586 v.compiler.header.add_decl("extern long count_attr_reads;")
587 v.compiler.header.add_decl("extern long count_isset_checks;")
588 end
589
590 v.add_decl("void sig_handler(int signo)\{")
591 v.add_decl("printf(\"Caught signal : %s\\n\", strsignal(signo));")
592 v.add_decl("show_backtrace(signo);")
593 v.add_decl("\}")
594
595 v.add_decl("void show_backtrace (int signo) \{")
596 if ost == "nitstack" or ost == "libunwind" then
597 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
598 v.add_decl("unw_cursor_t cursor;")
599 v.add_decl("if(opt==NULL)\{")
600 v.add_decl("unw_context_t uc;")
601 v.add_decl("unw_word_t ip;")
602 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
603 v.add_decl("unw_getcontext(&uc);")
604 v.add_decl("unw_init_local(&cursor, &uc);")
605 v.add_decl("printf(\"-------------------------------------------------\\n\");")
606 v.add_decl("printf(\"-- Stack Trace ------------------------------\\n\");")
607 v.add_decl("printf(\"-------------------------------------------------\\n\");")
608 v.add_decl("while (unw_step(&cursor) > 0) \{")
609 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
610 if ost == "nitstack" then
611 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
612 v.add_decl(" if (recv != NULL)\{")
613 v.add_decl(" printf(\"` %s\\n\", recv);")
614 v.add_decl(" \}else\{")
615 v.add_decl(" printf(\"` %s\\n\", procname);")
616 v.add_decl(" \}")
617 else
618 v.add_decl(" printf(\"` %s \\n\",procname);")
619 end
620 v.add_decl("\}")
621 v.add_decl("printf(\"-------------------------------------------------\\n\");")
622 v.add_decl("free(procname);")
623 v.add_decl("\}")
624 end
625 v.add_decl("exit(signo);")
626 v.add_decl("\}")
627
628 v.add_decl("int main(int argc, char** argv) \{")
629
630 v.add("signal(SIGABRT, sig_handler);")
631 v.add("signal(SIGFPE, sig_handler);")
632 v.add("signal(SIGILL, sig_handler);")
633 v.add("signal(SIGINT, sig_handler);")
634 v.add("signal(SIGTERM, sig_handler);")
635 v.add("signal(SIGSEGV, sig_handler);")
636 v.add("signal(SIGPIPE, sig_handler);")
637
638 v.add("glob_argc = argc; glob_argv = argv;")
639 v.add("initialize_gc_option();")
640 var main_type = mainmodule.sys_type
641 if main_type != null then
642 var mainmodule = v.compiler.mainmodule
643 var glob_sys = v.init_instance(main_type)
644 v.add("glob_sys = {glob_sys};")
645 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
646 if main_init != null then
647 v.send(main_init, [glob_sys])
648 end
649 var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass)
650 if main_method != null then
651 v.send(main_method, [glob_sys])
652 end
653 end
654
655 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
656 v.add_decl("long count_type_test_resolved_total = 0;")
657 v.add_decl("long count_type_test_unresolved_total = 0;")
658 v.add_decl("long count_type_test_skipped_total = 0;")
659 v.add_decl("long count_type_test_total_total = 0;")
660 for tag in count_type_test_tags do
661 v.add_decl("long count_type_test_total_{tag};")
662 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
663 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
664 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
665 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
666 v.add("count_type_test_total_total += count_type_test_total_{tag};")
667 end
668 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
669 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
670 var tags = count_type_test_tags.to_a
671 tags.add("total")
672 for tag in tags do
673 v.add("printf(\"{tag}\");")
674 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
675 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
676 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
677 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
678 end
679 end
680
681 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
682 v.add_decl("long count_invoke_total;")
683 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
684 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
685 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
686 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
687 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
688 end
689
690 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
691 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
692 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
693 end
694
695 v.add("return 0;")
696 v.add("\}")
697 end
698
699 # List of additional files required to compile (FFI)
700 var extern_bodies = new Array[ExternFile]
701
702 # List of source files to copy over to the compile dir
703 var files_to_copy = new Array[String]
704
705 # This is used to avoid adding an extern file more than once
706 private var seen_extern = new ArraySet[String]
707
708 # Generate code that initialize the attributes on a new instance
709 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
710 do
711 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
712 self.mainmodule.linearize_mclassdefs(cds)
713 for cd in cds do
714 var n = self.modelbuilder.mclassdef2nclassdef[cd]
715 for npropdef in n.n_propdefs do
716 if npropdef isa AAttrPropdef then
717 npropdef.init_expr(v, recv)
718 end
719 end
720 end
721 end
722
723 # Generate code that check if an attribute is correctly initialized
724 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
725 do
726 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
727 self.mainmodule.linearize_mclassdefs(cds)
728 for cd in cds do
729 var n = self.modelbuilder.mclassdef2nclassdef[cd]
730 for npropdef in n.n_propdefs do
731 if npropdef isa AAttrPropdef then
732 npropdef.check_expr(v, recv)
733 end
734 end
735 end
736 end
737
738 # stats
739
740 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
741 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
742 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
743 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
744
745 protected fun init_count_type_test_tags: HashMap[String, Int]
746 do
747 var res = new HashMap[String, Int]
748 for tag in count_type_test_tags do
749 res[tag] = 0
750 end
751 return res
752 end
753
754 # Display stats about compilation process
755 # Metrics used:
756 # * type tests against resolved types (`x isa Collection[Animal]`)
757 # * type tests against unresolved types (`x isa Collection[E]`)
758 # * type tests skipped
759 # * type tests total
760 # *
761 fun display_stats
762 do
763 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
764 print "# static count_type_test"
765 print "\tresolved:\tunresolved\tskipped\ttotal"
766 var count_type_test_total = init_count_type_test_tags
767 count_type_test_resolved["total"] = 0
768 count_type_test_unresolved["total"] = 0
769 count_type_test_skipped["total"] = 0
770 count_type_test_total["total"] = 0
771 for tag in count_type_test_tags do
772 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
773 count_type_test_resolved["total"] += count_type_test_resolved[tag]
774 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
775 count_type_test_skipped["total"] += count_type_test_skipped[tag]
776 count_type_test_total["total"] += count_type_test_total[tag]
777 end
778 var count_type_test = count_type_test_total["total"]
779 var tags = count_type_test_tags.to_a
780 tags.add("total")
781 for tag in tags do
782 printn tag
783 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
784 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
785 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
786 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
787 print ""
788 end
789 end
790 end
791
792 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
793
794 # Division facility
795 # Avoid division by zero by returning the string "n/a"
796 fun div(a,b:Int):String
797 do
798 if b == 0 then return "n/a"
799 return ((a*10000/b).to_f / 100.0).to_precision(2)
800 end
801 end
802
803 # A file unit (may be more than one file if
804 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
805 class CodeFile
806 var name: String
807 var writers = new Array[CodeWriter]
808 var required_declarations = new HashSet[String]
809 end
810
811 # Where to store generated lines
812 class CodeWriter
813 var file: CodeFile
814 var lines: List[String] = new List[String]
815 var decl_lines: List[String] = new List[String]
816
817 # Add a line in the main part of the generated C
818 fun add(s: String) do self.lines.add(s)
819
820 # Add a line in the
821 # (used for local or global declaration)
822 fun add_decl(s: String) do self.decl_lines.add(s)
823
824 init(file: CodeFile)
825 do
826 self.file = file
827 file.writers.add(self)
828 end
829 end
830
831 # A visitor on the AST of property definition that generate the C code.
832 abstract class AbstractCompilerVisitor
833
834 type COMPILER: AbstractCompiler
835
836 # The associated compiler
837 var compiler: COMPILER
838
839 # The current visited AST node
840 var current_node: nullable ANode writable = null
841
842 # The current `Frame`
843 var frame: nullable Frame writable
844
845 # Alias for self.compiler.mainmodule.object_type
846 fun object_type: MClassType do return self.compiler.mainmodule.object_type
847
848 # Alias for self.compiler.mainmodule.bool_type
849 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
850
851 var writer: CodeWriter
852
853 init(compiler: COMPILER)
854 do
855 self.compiler = compiler
856 self.writer = new CodeWriter(compiler.files.last)
857 end
858
859 # Force to get the primitive class named `name` or abort
860 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
861
862 # Force to get the primitive property named `name` in the instance `recv` or abort
863 fun get_property(name: String, recv: MType): MMethod
864 do
865 assert recv isa MClassType
866 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
867 end
868
869 fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
870 do
871 return self.send(callsite.mproperty, args)
872 end
873
874 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
875
876 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
877
878 # Transform varargs, in raw arguments, into a single argument of type `Array`
879 # Note: this method modify the given `args`
880 # If there is no vararg, then `args` is not modified.
881 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
882 do
883 var recv = args.first
884 var vararg_rank = msignature.vararg_rank
885 if vararg_rank >= 0 then
886 assert args.length >= msignature.arity + 1 # because of self
887 var rawargs = args
888 args = new Array[RuntimeVariable]
889
890 args.add(rawargs.first) # recv
891
892 for i in [0..vararg_rank[ do
893 args.add(rawargs[i+1])
894 end
895
896 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
897 var vararg = new Array[RuntimeVariable]
898 for i in [vararg_rank..vararg_lastrank] do
899 vararg.add(rawargs[i+1])
900 end
901
902 var elttype = msignature.mparameters[vararg_rank].mtype
903 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
904
905 for i in [vararg_lastrank+1..rawargs.length-1[ do
906 args.add(rawargs[i+1])
907 end
908 rawargs.clear
909 rawargs.add_all(args)
910 end
911 end
912
913 # Type handling
914
915 # Anchor a type to the main module and the current receiver
916 fun anchor(mtype: MType): MType
917 do
918 if not mtype.need_anchor then return mtype
919 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
920 end
921
922 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
923 do
924 if not mtype.need_anchor then return mtype
925 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
926 end
927
928 # Unsafely cast a value to a new type
929 # ie the result share the same C variable but my have a different mcasttype
930 # NOTE: if the adaptation is useless then `value` is returned as it.
931 # ENSURE: `result.name == value.name`
932 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
933 do
934 mtype = self.anchor(mtype)
935 var valmtype = value.mcasttype
936 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
937 return value
938 end
939
940 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
941 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
942 return res
943 else
944 var res = new RuntimeVariable(value.name, valmtype, mtype)
945 return res
946 end
947 end
948
949 # Generate a super call from a method definition
950 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
951
952 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
953
954 # Box or unbox a value to another type iff a C type conversion is needed
955 # ENSURE: `result.mtype.ctype == mtype.ctype`
956 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
957
958 # Generate a polymorphic subtype test
959 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
960
961 # Generate the code required to dynamically check if 2 objects share the same runtime type
962 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
963
964 # Generate a Nit "is" for two runtime_variables
965 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
966
967 # Sends
968
969 # Generate a static call on a method definition
970 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
971
972 # Generate a polymorphic send for the method `m` and the arguments `args`
973 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
974
975 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
976 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
977 do
978 assert t isa MClassType
979 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
980 return self.call(propdef, t, args)
981 end
982
983 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
984 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
985 do
986 assert t isa MClassType
987 m = m.lookup_next_definition(self.compiler.mainmodule, t)
988 return self.call(m, t, args)
989 end
990
991 # Attributes handling
992
993 # Generate a polymorphic attribute is_set test
994 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
995
996 # Generate a polymorphic attribute read
997 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
998
999 # Generate a polymorphic attribute write
1000 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1001
1002 # Checks
1003
1004 # Add a check and an abort for a null reciever if needed
1005 fun check_recv_notnull(recv: RuntimeVariable)
1006 do
1007 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
1008
1009 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1010 if maybenull then
1011 self.add("if (unlikely({recv} == NULL)) \{")
1012 self.add_abort("Receiver is null")
1013 self.add("\}")
1014 end
1015 end
1016
1017 # Names handling
1018
1019 private var names: HashSet[String] = new HashSet[String]
1020 private var last: Int = 0
1021
1022 # Return a new name based on `s` and unique in the visitor
1023 fun get_name(s: String): String
1024 do
1025 if not self.names.has(s) then
1026 self.names.add(s)
1027 return s
1028 end
1029 var i = self.last + 1
1030 loop
1031 var s2 = s + i.to_s
1032 if not self.names.has(s2) then
1033 self.last = i
1034 self.names.add(s2)
1035 return s2
1036 end
1037 i = i + 1
1038 end
1039 end
1040
1041 # Return an unique and stable identifier associated with an escapemark
1042 fun escapemark_name(e: nullable EscapeMark): String
1043 do
1044 assert e != null
1045 if escapemark_names.has_key(e) then return escapemark_names[e]
1046 var name = e.name
1047 if name == null then name = "label"
1048 name = get_name(name)
1049 escapemark_names[e] = name
1050 return name
1051 end
1052
1053 private var escapemark_names = new HashMap[EscapeMark, String]
1054
1055 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1056 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1057 fun class_name_string(value: RuntimeVariable): String is abstract
1058
1059 # Variables handling
1060
1061 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
1062
1063 # Return the local runtime_variable associated to a Nit local variable
1064 fun variable(variable: Variable): RuntimeVariable
1065 do
1066 if self.variables.has_key(variable) then
1067 return self.variables[variable]
1068 else
1069 var name = self.get_name("var_{variable.name}")
1070 var mtype = variable.declared_type.as(not null)
1071 mtype = self.anchor(mtype)
1072 var res = new RuntimeVariable(name, mtype, mtype)
1073 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1074 self.variables[variable] = res
1075 return res
1076 end
1077 end
1078
1079 # Return a new uninitialized local runtime_variable
1080 fun new_var(mtype: MType): RuntimeVariable
1081 do
1082 mtype = self.anchor(mtype)
1083 var name = self.get_name("var")
1084 var res = new RuntimeVariable(name, mtype, mtype)
1085 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1086 return res
1087 end
1088
1089 # Return a new uninitialized named runtime_variable
1090 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1091 do
1092 mtype = self.anchor(mtype)
1093 var res = new RuntimeVariable(name, mtype, mtype)
1094 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1095 return res
1096 end
1097
1098 # Correctly assign a left and a right value
1099 # Boxing and unboxing is performed if required
1100 fun assign(left, right: RuntimeVariable)
1101 do
1102 right = self.autobox(right, left.mtype)
1103 self.add("{left} = {right};")
1104 end
1105
1106 # Generate instances
1107
1108 # Generate a alloc-instance + init-attributes
1109 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1110
1111 # Generate an integer value
1112 fun int_instance(value: Int): RuntimeVariable
1113 do
1114 var res = self.new_var(self.get_class("Int").mclass_type)
1115 self.add("{res} = {value};")
1116 return res
1117 end
1118
1119 # Generate a string value
1120 fun string_instance(string: String): RuntimeVariable
1121 do
1122 var mtype = self.get_class("String").mclass_type
1123 var name = self.get_name("varonce")
1124 self.add_decl("static {mtype.ctype} {name};")
1125 var res = self.new_var(mtype)
1126 self.add("if ({name}) \{")
1127 self.add("{res} = {name};")
1128 self.add("\} else \{")
1129 var native_mtype = self.get_class("NativeString").mclass_type
1130 var nat = self.new_var(native_mtype)
1131 self.add("{nat} = \"{string.escape_to_c}\";")
1132 var length = self.int_instance(string.length)
1133 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1134 self.add("{name} = {res};")
1135 self.add("\}")
1136 return res
1137 end
1138
1139 # Generate an array value
1140 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1141
1142 # Get an instance of a array for a vararg
1143 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1144
1145 # Code generation
1146
1147 # Add a line in the main part of the generated C
1148 fun add(s: String) do self.writer.lines.add(s)
1149
1150 # Add a line in the
1151 # (used for local or global declaration)
1152 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1153
1154 # Request the presence of a global declaration
1155 fun require_declaration(key: String)
1156 do
1157 var reqs = self.writer.file.required_declarations
1158 if reqs.has(key) then return
1159 reqs.add(key)
1160 var node = current_node
1161 if node != null then compiler.requirers_of_declarations[key] = node
1162 end
1163
1164 # Add a declaration in the local-header
1165 # The declaration is ensured to be present once
1166 fun declare_once(s: String)
1167 do
1168 self.compiler.provide_declaration(s, s)
1169 self.require_declaration(s)
1170 end
1171
1172 # look for a needed .h and .c file for a given .nit source-file
1173 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1174 fun add_extern(file: String)
1175 do
1176 file = file.strip_extension(".nit")
1177 var 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 tryfile = file + "_nit.h"
1183 if tryfile.file_exists then
1184 self.declare_once("#include \"{tryfile.basename("")}\"")
1185 self.compiler.files_to_copy.add(tryfile)
1186 end
1187
1188 if self.compiler.seen_extern.has(file) then return
1189 self.compiler.seen_extern.add(file)
1190 tryfile = file + ".nit.c"
1191 if not tryfile.file_exists then
1192 tryfile = file + "_nit.c"
1193 if not tryfile.file_exists then return
1194 end
1195 var f = new ExternCFile(tryfile.basename(""), "")
1196 self.compiler.extern_bodies.add(f)
1197 self.compiler.files_to_copy.add(tryfile)
1198 end
1199
1200 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1201 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1202 do
1203 var res = new_var(mtype)
1204 self.add("{res} = {cexpr};")
1205 return res
1206 end
1207
1208 # Generate generic abort
1209 # used by aborts, asserts, casts, etc.
1210 fun add_abort(message: String)
1211 do
1212 self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");")
1213 add_raw_abort
1214 end
1215
1216 fun add_raw_abort
1217 do
1218 if self.current_node != null and self.current_node.location.file != null then
1219 self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1220 else
1221 self.add("fprintf(stderr, \"\\n\");")
1222 end
1223 self.add("show_backtrace(1);")
1224 end
1225
1226 # Add a dynamic cast
1227 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1228 do
1229 var res = self.type_test(value, mtype, tag)
1230 self.add("if (unlikely(!{res})) \{")
1231 var cn = self.class_name_string(value)
1232 self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1233 self.add_raw_abort
1234 self.add("\}")
1235 end
1236
1237 # Generate a return with the value `s`
1238 fun ret(s: RuntimeVariable)
1239 do
1240 self.assign(self.frame.returnvar.as(not null), s)
1241 self.add("goto {self.frame.returnlabel.as(not null)};")
1242 end
1243
1244 # Compile a statement (if any)
1245 fun stmt(nexpr: nullable AExpr)
1246 do
1247 if nexpr == null then return
1248 var old = self.current_node
1249 self.current_node = nexpr
1250 nexpr.stmt(self)
1251 self.current_node = old
1252 end
1253
1254 # Compile an expression an return its result
1255 # `mtype` is the expected return type, pass null if no specific type is expected.
1256 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1257 do
1258 var old = self.current_node
1259 self.current_node = nexpr
1260 var res = nexpr.expr(self).as(not null)
1261 if mtype != null then
1262 mtype = self.anchor(mtype)
1263 res = self.autobox(res, mtype)
1264 end
1265 res = autoadapt(res, nexpr.mtype.as(not null))
1266 var implicit_cast_to = nexpr.implicit_cast_to
1267 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1268 add_cast(res, implicit_cast_to, "auto")
1269 res = autoadapt(res, implicit_cast_to)
1270 end
1271 self.current_node = old
1272 return res
1273 end
1274
1275 # Alias for `self.expr(nexpr, self.bool_type)`
1276 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1277
1278 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1279 fun debug(message: String)
1280 do
1281 var node = self.current_node
1282 if node == null then
1283 print "?: {message}"
1284 else
1285 node.debug(message)
1286 end
1287 self.add("/* DEBUG: {message} */")
1288 end
1289 end
1290
1291 # A C function associated to a Nit method
1292 # Because of customization, a given Nit method can be compiler more that once
1293 abstract class AbstractRuntimeFunction
1294
1295 type COMPILER: AbstractCompiler
1296 type VISITOR: AbstractCompilerVisitor
1297
1298 # The associated Nit method
1299 var mmethoddef: MMethodDef
1300
1301 # The mangled c name of the runtime_function
1302 # Subclasses should redefine `build_c_name` instead
1303 fun c_name: String
1304 do
1305 var res = self.c_name_cache
1306 if res != null then return res
1307 res = self.build_c_name
1308 self.c_name_cache = res
1309 return res
1310 end
1311
1312 # Non cached version of `c_name`
1313 protected fun build_c_name: String is abstract
1314
1315 protected var c_name_cache: nullable String writable = null
1316
1317 # Implements a call of the runtime_function
1318 # May inline the body or generate a C function call
1319 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1320
1321 # Generate the code for the `AbstractRuntimeFunction`
1322 # Warning: compile more than once compilation makes CC unhappy
1323 fun compile_to_c(compiler: COMPILER) is abstract
1324 end
1325
1326 # A runtime variable hold a runtime value in C.
1327 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1328 #
1329 # 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.
1330 class RuntimeVariable
1331 # The name of the variable in the C code
1332 var name: String
1333
1334 # The static type of the variable (as declard in C)
1335 var mtype: MType
1336
1337 # The current casted type of the variable (as known in Nit)
1338 var mcasttype: MType writable
1339
1340 # If the variable exaclty a mcasttype?
1341 # false (usual value) means that the variable is a mcasttype or a subtype.
1342 var is_exact: Bool writable = false
1343
1344 init(name: String, mtype: MType, mcasttype: MType)
1345 do
1346 self.name = name
1347 self.mtype = mtype
1348 self.mcasttype = mcasttype
1349 assert not mtype.need_anchor
1350 assert not mcasttype.need_anchor
1351 end
1352
1353 redef fun to_s do return name
1354
1355 redef fun inspect
1356 do
1357 var exact_str
1358 if self.is_exact then
1359 exact_str = " exact"
1360 else
1361 exact_str = ""
1362 end
1363 var type_str
1364 if self.mtype == self.mcasttype then
1365 type_str = "{mtype}{exact_str}"
1366 else
1367 type_str = "{mtype}({mcasttype}{exact_str})"
1368 end
1369 return "<{name}:{type_str}>"
1370 end
1371 end
1372
1373 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1374 class Frame
1375
1376 type VISITOR: AbstractCompilerVisitor
1377
1378 # The associated visitor
1379 var visitor: VISITOR
1380
1381 # The executed property.
1382 # A Method in case of a call, an attribute in case of a default initialization.
1383 var mpropdef: MPropDef
1384
1385 # The static type of the receiver
1386 var receiver: MClassType
1387
1388 # Arguments of the method (the first is the receiver)
1389 var arguments: Array[RuntimeVariable]
1390
1391 # The runtime_variable associated to the return (in a function)
1392 var returnvar: nullable RuntimeVariable writable = null
1393
1394 # The label at the end of the property
1395 var returnlabel: nullable String writable = null
1396 end
1397
1398 redef class MType
1399 # Return the C type associated to a given Nit static type
1400 fun ctype: String do return "val*"
1401
1402 fun ctypename: String do return "val"
1403
1404 # Return the name of the C structure associated to a Nit live type
1405 fun c_name: String is abstract
1406 protected var c_name_cache: nullable String protected writable
1407 end
1408
1409 redef class MClassType
1410 redef fun c_name
1411 do
1412 var res = self.c_name_cache
1413 if res != null then return res
1414 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1415 self.c_name_cache = res
1416 return res
1417 end
1418
1419 redef fun ctype: String
1420 do
1421 if mclass.name == "Int" then
1422 return "long"
1423 else if mclass.name == "Bool" then
1424 return "short int"
1425 else if mclass.name == "Char" then
1426 return "char"
1427 else if mclass.name == "Float" then
1428 return "double"
1429 else if mclass.name == "NativeString" then
1430 return "char*"
1431 else if mclass.name == "NativeArray" then
1432 return "val*"
1433 else if mclass.kind == extern_kind then
1434 return "void*"
1435 else
1436 return "val*"
1437 end
1438 end
1439
1440 redef fun ctypename: String
1441 do
1442 if mclass.name == "Int" then
1443 return "l"
1444 else if mclass.name == "Bool" then
1445 return "s"
1446 else if mclass.name == "Char" then
1447 return "c"
1448 else if mclass.name == "Float" then
1449 return "d"
1450 else if mclass.name == "NativeString" then
1451 return "str"
1452 else if mclass.name == "NativeArray" then
1453 #return "{self.arguments.first.ctype}*"
1454 return "val"
1455 else if mclass.kind == extern_kind then
1456 return "ptr"
1457 else
1458 return "val"
1459 end
1460 end
1461 end
1462
1463 redef class MGenericType
1464 redef fun c_name
1465 do
1466 var res = self.c_name_cache
1467 if res != null then return res
1468 res = super
1469 for t in self.arguments do
1470 res = res + t.c_name
1471 end
1472 self.c_name_cache = res
1473 return res
1474 end
1475 end
1476
1477 redef class MParameterType
1478 redef fun c_name
1479 do
1480 var res = self.c_name_cache
1481 if res != null then return res
1482 res = "{self.mclass.c_name}_FT{self.rank}"
1483 self.c_name_cache = res
1484 return res
1485 end
1486 end
1487
1488 redef class MVirtualType
1489 redef fun c_name
1490 do
1491 var res = self.c_name_cache
1492 if res != null then return res
1493 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1494 self.c_name_cache = res
1495 return res
1496 end
1497 end
1498
1499 redef class MNullableType
1500 redef fun c_name
1501 do
1502 var res = self.c_name_cache
1503 if res != null then return res
1504 res = "nullable_{self.mtype.c_name}"
1505 self.c_name_cache = res
1506 return res
1507 end
1508 end
1509
1510 redef class MClass
1511 # Return the name of the C structure associated to a Nit class
1512 fun c_name: String do
1513 var res = self.c_name_cache
1514 if res != null then return res
1515 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1516 self.c_name_cache = res
1517 return res
1518 end
1519 private var c_name_cache: nullable String
1520 end
1521
1522 redef class MProperty
1523 fun c_name: String do
1524 var res = self.c_name_cache
1525 if res != null then return res
1526 res = "{self.intro.c_name}"
1527 self.c_name_cache = res
1528 return res
1529 end
1530 private var c_name_cache: nullable String
1531 end
1532
1533 redef class MPropDef
1534 type VISITOR: AbstractCompilerVisitor
1535
1536 private var c_name_cache: nullable String
1537
1538 # The mangled name associated to the property
1539 fun c_name: String
1540 do
1541 var res = self.c_name_cache
1542 if res != null then return res
1543 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1544 self.c_name_cache = res
1545 return res
1546 end
1547 end
1548
1549 redef class MMethodDef
1550 # Can the body be inlined?
1551 fun can_inline(v: VISITOR): Bool
1552 do
1553 var modelbuilder = v.compiler.modelbuilder
1554 if modelbuilder.mpropdef2npropdef.has_key(self) then
1555 var npropdef = modelbuilder.mpropdef2npropdef[self]
1556 return npropdef.can_inline
1557 else if self.mproperty.name == "init" then
1558 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1559 return true
1560 else
1561 abort
1562 end
1563 end
1564
1565 # Inline the body in another visitor
1566 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1567 do
1568 var modelbuilder = v.compiler.modelbuilder
1569 if modelbuilder.mpropdef2npropdef.has_key(self) then
1570 var npropdef = modelbuilder.mpropdef2npropdef[self]
1571 var oldnode = v.current_node
1572 v.current_node = npropdef
1573 self.compile_parameter_check(v, arguments)
1574 npropdef.compile_to_c(v, self, arguments)
1575 v.current_node = oldnode
1576 else if self.mproperty.name == "init" then
1577 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1578 var oldnode = v.current_node
1579 v.current_node = nclassdef
1580 self.compile_parameter_check(v, arguments)
1581 nclassdef.compile_to_c(v, self, arguments)
1582 v.current_node = oldnode
1583 else
1584 abort
1585 end
1586 return null
1587 end
1588
1589 # Generate type checks in the C code to check covariant parameters
1590 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1591 do
1592 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1593
1594 for i in [0..msignature.arity[ do
1595 # skip test for vararg since the array is instantiated with the correct polymorphic type
1596 if msignature.vararg_rank == i then continue
1597
1598 # skip if the cast is not required
1599 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1600 if not origmtype.need_anchor then continue
1601
1602 # get the parameter type
1603 var mtype = self.msignature.mparameters[i].mtype
1604
1605 # generate the cast
1606 # note that v decides if and how to implements the cast
1607 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1608 v.add_cast(arguments[i+1], mtype, "covariance")
1609 end
1610 end
1611 end
1612
1613 # Node visit
1614
1615 redef class APropdef
1616 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1617 do
1618 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1619 debug("Not yet implemented")
1620 end
1621
1622 fun can_inline: Bool do return true
1623 end
1624
1625 redef class AConcreteMethPropdef
1626 redef fun compile_to_c(v, mpropdef, arguments)
1627 do
1628 for i in [0..mpropdef.msignature.arity[ do
1629 var variable = self.n_signature.n_params[i].variable.as(not null)
1630 v.assign(v.variable(variable), arguments[i+1])
1631 end
1632 # Call the implicit super-init
1633 var auto_super_inits = self.auto_super_inits
1634 if auto_super_inits != null then
1635 var args = [arguments.first]
1636 for auto_super_init in auto_super_inits do
1637 args.clear
1638 for i in [0..auto_super_init.msignature.arity+1[ do
1639 args.add(arguments[i])
1640 end
1641 v.compile_callsite(auto_super_init, args)
1642 end
1643 end
1644 v.stmt(self.n_block)
1645 end
1646
1647 redef fun can_inline
1648 do
1649 if self.auto_super_inits != null then return false
1650 var nblock = self.n_block
1651 if nblock == null then return true
1652 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1653 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1654 return false
1655 end
1656 end
1657
1658 redef class AInternMethPropdef
1659 redef fun compile_to_c(v, mpropdef, arguments)
1660 do
1661 var pname = mpropdef.mproperty.name
1662 var cname = mpropdef.mclassdef.mclass.name
1663 var ret = mpropdef.msignature.return_mtype
1664 if ret != null then
1665 ret = v.resolve_for(ret, arguments.first)
1666 end
1667 if pname != "==" and pname != "!=" then
1668 v.adapt_signature(mpropdef, arguments)
1669 end
1670 if cname == "Int" then
1671 if pname == "output" then
1672 v.add("printf(\"%ld\\n\", {arguments.first});")
1673 return
1674 else if pname == "object_id" then
1675 v.ret(arguments.first)
1676 return
1677 else if pname == "+" then
1678 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1679 return
1680 else if pname == "-" then
1681 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1682 return
1683 else if pname == "unary -" then
1684 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1685 return
1686 else if pname == "*" then
1687 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1688 return
1689 else if pname == "/" then
1690 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1691 return
1692 else if pname == "%" then
1693 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1694 return
1695 else if pname == "lshift" then
1696 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1697 return
1698 else if pname == "rshift" then
1699 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1700 return
1701 else if pname == "==" then
1702 v.ret(v.equal_test(arguments[0], arguments[1]))
1703 return
1704 else if pname == "!=" then
1705 var res = v.equal_test(arguments[0], arguments[1])
1706 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1713 return
1714 else if pname == "<=" then
1715 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1716 return
1717 else if pname == ">=" then
1718 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1719 return
1720 else if pname == "to_f" then
1721 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1722 return
1723 else if pname == "ascii" then
1724 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1725 return
1726 end
1727 else if cname == "Char" then
1728 if pname == "output" then
1729 v.add("printf(\"%c\", {arguments.first});")
1730 return
1731 else if pname == "object_id" then
1732 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1733 return
1734 else if pname == "successor" then
1735 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1736 return
1737 else if pname == "predecessor" then
1738 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1739 return
1740 else if pname == "==" then
1741 v.ret(v.equal_test(arguments[0], arguments[1]))
1742 return
1743 else if pname == "!=" then
1744 var res = v.equal_test(arguments[0], arguments[1])
1745 v.ret(v.new_expr("!{res}", ret.as(not null)))
1746 return
1747 else if pname == "<" then
1748 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1749 return
1750 else if pname == ">" 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.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1755 return
1756 else if pname == ">=" then
1757 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1758 return
1759 else if pname == "to_i" then
1760 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1761 return
1762 else if pname == "ascii" then
1763 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1764 return
1765 end
1766 else if cname == "Bool" then
1767 if pname == "output" then
1768 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1769 return
1770 else if pname == "object_id" then
1771 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1772 return
1773 else if pname == "==" then
1774 v.ret(v.equal_test(arguments[0], arguments[1]))
1775 return
1776 else if pname == "!=" then
1777 var res = v.equal_test(arguments[0], arguments[1])
1778 v.ret(v.new_expr("!{res}", ret.as(not null)))
1779 return
1780 end
1781 else if cname == "Float" then
1782 if pname == "output" then
1783 v.add("printf(\"%f\\n\", {arguments.first});")
1784 return
1785 else if pname == "object_id" then
1786 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1787 return
1788 else if pname == "+" then
1789 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1790 return
1791 else if pname == "-" then
1792 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1793 return
1794 else if pname == "unary -" then
1795 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1796 return
1797 else if pname == "succ" then
1798 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1799 return
1800 else if pname == "prec" then
1801 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1802 return
1803 else if pname == "*" then
1804 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1805 return
1806 else if pname == "/" then
1807 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1808 return
1809 else if pname == "==" then
1810 v.ret(v.equal_test(arguments[0], arguments[1]))
1811 return
1812 else if pname == "!=" then
1813 var res = v.equal_test(arguments[0], arguments[1])
1814 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1824 return
1825 else if pname == ">=" then
1826 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1827 return
1828 else if pname == "to_i" then
1829 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1830 return
1831 end
1832 else if cname == "NativeString" then
1833 if pname == "[]" then
1834 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1835 return
1836 else if pname == "[]=" then
1837 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1838 return
1839 else if pname == "copy_to" then
1840 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1841 return
1842 else if pname == "atoi" then
1843 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1844 return
1845 end
1846 else if cname == "NativeArray" then
1847 v.native_array_def(pname, ret, arguments)
1848 return
1849 end
1850 if pname == "exit" then
1851 v.add("exit({arguments[1]});")
1852 return
1853 else if pname == "sys" then
1854 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1855 return
1856 else if pname == "calloc_string" then
1857 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1858 return
1859 else if pname == "calloc_array" then
1860 v.calloc_array(ret.as(not null), arguments)
1861 return
1862 else if pname == "object_id" then
1863 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1864 return
1865 else if pname == "is_same_type" then
1866 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1867 return
1868 else if pname == "is_same_instance" then
1869 v.ret(v.equal_test(arguments[0], arguments[1]))
1870 return
1871 else if pname == "output_class_name" then
1872 var nat = v.class_name_string(arguments.first)
1873 v.add("printf(\"%s\\n\", {nat});")
1874 return
1875 else if pname == "native_class_name" then
1876 var nat = v.class_name_string(arguments.first)
1877 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1878 return
1879 else if pname == "force_garbage_collection" then
1880 v.add("nit_gcollect();")
1881 return
1882 else if pname == "native_argc" then
1883 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1884 return
1885 else if pname == "native_argv" then
1886 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1887 return
1888 end
1889 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1890 debug("Not implemented {mpropdef}")
1891 end
1892 end
1893
1894 redef class AExternMethPropdef
1895 redef fun compile_to_c(v, mpropdef, arguments)
1896 do
1897 var externname
1898 var nextern = self.n_extern
1899 if nextern == null then
1900 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1901 v.add("show_backtrace(1);")
1902 return
1903 end
1904 externname = nextern.text.substring(1, nextern.text.length-2)
1905 if location.file != null then
1906 var file = location.file.filename
1907 v.add_extern(file)
1908 end
1909 var res: nullable RuntimeVariable = null
1910 var ret = mpropdef.msignature.return_mtype
1911 if ret != null then
1912 ret = v.resolve_for(ret, arguments.first)
1913 res = v.new_var(ret)
1914 end
1915 v.adapt_signature(mpropdef, arguments)
1916
1917 if res == null then
1918 v.add("{externname}({arguments.join(", ")});")
1919 else
1920 v.add("{res} = {externname}({arguments.join(", ")});")
1921 v.ret(res)
1922 end
1923 end
1924 end
1925
1926 redef class AExternInitPropdef
1927 redef fun compile_to_c(v, mpropdef, arguments)
1928 do
1929 var externname
1930 var nextern = self.n_extern
1931 if nextern == null then
1932 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1933 v.add("show_backtrace(1);")
1934 return
1935 end
1936 externname = nextern.text.substring(1, nextern.text.length-2)
1937 if location.file != null then
1938 var file = location.file.filename
1939 v.add_extern(file)
1940 end
1941 v.adapt_signature(mpropdef, arguments)
1942 var ret = arguments.first.mtype
1943 var res = v.new_var(ret)
1944
1945 arguments.shift
1946
1947 v.add("{res} = {externname}({arguments.join(", ")});")
1948 v.ret(res)
1949 end
1950 end
1951
1952 redef class AAttrPropdef
1953 redef fun compile_to_c(v, mpropdef, arguments)
1954 do
1955 if arguments.length == 1 then
1956 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1957 v.assign(v.frame.returnvar.as(not null), res)
1958 else
1959 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1960 end
1961 end
1962
1963 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1964 do
1965 var nexpr = self.n_expr
1966 if nexpr != null then
1967 var oldnode = v.current_node
1968 v.current_node = self
1969 var old_frame = v.frame
1970 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1971 v.frame = frame
1972 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1973 v.write_attribute(self.mpropdef.mproperty, recv, value)
1974 v.frame = old_frame
1975 v.current_node = oldnode
1976 end
1977 end
1978
1979 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1980 do
1981 var nexpr = self.n_expr
1982 if nexpr != null then return
1983
1984 var oldnode = v.current_node
1985 v.current_node = self
1986 var old_frame = v.frame
1987 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1988 v.frame = frame
1989 # Force read to check the initialization
1990 v.read_attribute(self.mpropdef.mproperty, recv)
1991 v.frame = old_frame
1992 v.current_node = oldnode
1993 end
1994 end
1995
1996 redef class AClassdef
1997 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1998 do
1999 if mpropdef == self.mfree_init then
2000 var super_inits = self.super_inits
2001 if super_inits != null then
2002 assert arguments.length == 1
2003 for su in super_inits do
2004 v.send(su, arguments)
2005 end
2006 return
2007 end
2008 var recv = arguments.first
2009 var i = 1
2010 # Collect undefined attributes
2011 for npropdef in self.n_propdefs do
2012 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
2013 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
2014 i += 1
2015 end
2016 end
2017 else
2018 abort
2019 end
2020 end
2021 end
2022
2023 redef class ADeferredMethPropdef
2024 redef fun compile_to_c(v, mpropdef, arguments) do
2025 var cn = v.class_name_string(arguments.first)
2026 v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2027 v.add_raw_abort
2028 end
2029 redef fun can_inline do return true
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
2697
2698 # Create a tool context to handle options and paths
2699 var toolcontext = new ToolContext
2700
2701 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
2702 toolcontext.option_context.add_option(opt_mixins)
2703
2704 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit\nCompiles Nit programs."
2705
2706 # We do not add other options, so process them now!
2707 toolcontext.process_options(args)
2708
2709 # We need a model to collect stufs
2710 var model = new Model
2711 # An a model builder to parse files
2712 var modelbuilder = new ModelBuilder(model, toolcontext)
2713
2714 var arguments = toolcontext.option_context.rest
2715 if arguments.length > 1 then
2716 print "Too much arguments: {arguments.join(" ")}"
2717 print toolcontext.tooldescription
2718 exit 1
2719 end
2720 var progname = arguments.first
2721
2722 # Here we load an process all modules passed on the command line
2723 var mmodules = modelbuilder.parse([progname])
2724 mmodules.add_all modelbuilder.parse(opt_mixins.value)
2725
2726 if mmodules.is_empty then return
2727 modelbuilder.run_phases
2728
2729 var mainmodule
2730 if mmodules.length == 1 then
2731 mainmodule = mmodules.first
2732 else
2733 mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location)
2734 mainmodule.set_imported_mmodules(mmodules)
2735 end
2736
2737 toolcontext.run_global_phases(mmodules)