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