tests: adds sav file for pep8analysis_web
[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 calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
897
898 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
899
900 # Transform varargs, in raw arguments, into a single argument of type `Array`
901 # Note: this method modify the given `args`
902 # If there is no vararg, then `args` is not modified.
903 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
904 do
905 var recv = args.first
906 var vararg_rank = msignature.vararg_rank
907 if vararg_rank >= 0 then
908 assert args.length >= msignature.arity + 1 # because of self
909 var rawargs = args
910 args = new Array[RuntimeVariable]
911
912 args.add(rawargs.first) # recv
913
914 for i in [0..vararg_rank[ do
915 args.add(rawargs[i+1])
916 end
917
918 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
919 var vararg = new Array[RuntimeVariable]
920 for i in [vararg_rank..vararg_lastrank] do
921 vararg.add(rawargs[i+1])
922 end
923
924 var elttype = msignature.mparameters[vararg_rank].mtype
925 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
926
927 for i in [vararg_lastrank+1..rawargs.length-1[ do
928 args.add(rawargs[i+1])
929 end
930 rawargs.clear
931 rawargs.add_all(args)
932 end
933 end
934
935 # Type handling
936
937 # Anchor a type to the main module and the current receiver
938 fun anchor(mtype: MType): MType
939 do
940 if not mtype.need_anchor then return mtype
941 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
942 end
943
944 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
945 do
946 if not mtype.need_anchor then return mtype
947 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
948 end
949
950 # Unsafely cast a value to a new type
951 # ie the result share the same C variable but my have a different mcasttype
952 # NOTE: if the adaptation is useless then `value` is returned as it.
953 # ENSURE: `result.name == value.name`
954 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
955 do
956 mtype = self.anchor(mtype)
957 var valmtype = value.mcasttype
958 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
959 return value
960 end
961
962 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
963 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
964 return res
965 else
966 var res = new RuntimeVariable(value.name, valmtype, mtype)
967 return res
968 end
969 end
970
971 # Generate a super call from a method definition
972 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
973
974 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
975
976 # Box or unbox a value to another type iff a C type conversion is needed
977 # ENSURE: `result.mtype.ctype == mtype.ctype`
978 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
979
980 # Generate a polymorphic subtype test
981 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
982
983 # Generate the code required to dynamically check if 2 objects share the same runtime type
984 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
985
986 # Generate a Nit "is" for two runtime_variables
987 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
988
989 # Sends
990
991 # Generate a static call on a method definition
992 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
993
994 # Generate a polymorphic send for the method `m` and the arguments `args`
995 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
996
997 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
998 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
999 do
1000 assert t isa MClassType
1001 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1002 return self.call(propdef, t, args)
1003 end
1004
1005 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1006 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1007 do
1008 assert t isa MClassType
1009 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1010 return self.call(m, t, args)
1011 end
1012
1013 # Attributes handling
1014
1015 # Generate a polymorphic attribute is_set test
1016 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1017
1018 # Generate a polymorphic attribute read
1019 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1020
1021 # Generate a polymorphic attribute write
1022 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1023
1024 # Checks
1025
1026 # Add a check and an abort for a null reciever if needed
1027 fun check_recv_notnull(recv: RuntimeVariable)
1028 do
1029 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
1030
1031 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1032 if maybenull then
1033 self.add("if (unlikely({recv} == NULL)) \{")
1034 self.add_abort("Receiver is null")
1035 self.add("\}")
1036 end
1037 end
1038
1039 # Names handling
1040
1041 private var names: HashSet[String] = new HashSet[String]
1042 private var last: Int = 0
1043
1044 # Return a new name based on `s` and unique in the visitor
1045 fun get_name(s: String): String
1046 do
1047 if not self.names.has(s) then
1048 self.names.add(s)
1049 return s
1050 end
1051 var i = self.last + 1
1052 loop
1053 var s2 = s + i.to_s
1054 if not self.names.has(s2) then
1055 self.last = i
1056 self.names.add(s2)
1057 return s2
1058 end
1059 i = i + 1
1060 end
1061 end
1062
1063 # Return an unique and stable identifier associated with an escapemark
1064 fun escapemark_name(e: nullable EscapeMark): String
1065 do
1066 assert e != null
1067 if escapemark_names.has_key(e) then return escapemark_names[e]
1068 var name = e.name
1069 if name == null then name = "label"
1070 name = get_name(name)
1071 escapemark_names[e] = name
1072 return name
1073 end
1074
1075 private var escapemark_names = new HashMap[EscapeMark, String]
1076
1077 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1078 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1079 fun class_name_string(value: RuntimeVariable): String is abstract
1080
1081 # Variables handling
1082
1083 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
1084
1085 # Return the local runtime_variable associated to a Nit local variable
1086 fun variable(variable: Variable): RuntimeVariable
1087 do
1088 if self.variables.has_key(variable) then
1089 return self.variables[variable]
1090 else
1091 var name = self.get_name("var_{variable.name}")
1092 var mtype = variable.declared_type.as(not null)
1093 mtype = self.anchor(mtype)
1094 var res = new RuntimeVariable(name, mtype, mtype)
1095 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1096 self.variables[variable] = res
1097 return res
1098 end
1099 end
1100
1101 # Return a new uninitialized local runtime_variable
1102 fun new_var(mtype: MType): RuntimeVariable
1103 do
1104 mtype = self.anchor(mtype)
1105 var name = self.get_name("var")
1106 var res = new RuntimeVariable(name, mtype, mtype)
1107 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1108 return res
1109 end
1110
1111 # Return a new uninitialized named runtime_variable
1112 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1113 do
1114 mtype = self.anchor(mtype)
1115 var res = new RuntimeVariable(name, mtype, mtype)
1116 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1117 return res
1118 end
1119
1120 # Correctly assign a left and a right value
1121 # Boxing and unboxing is performed if required
1122 fun assign(left, right: RuntimeVariable)
1123 do
1124 right = self.autobox(right, left.mtype)
1125 self.add("{left} = {right};")
1126 end
1127
1128 # Generate instances
1129
1130 # Generate a alloc-instance + init-attributes
1131 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1132
1133 # Generate an integer value
1134 fun int_instance(value: Int): RuntimeVariable
1135 do
1136 var res = self.new_var(self.get_class("Int").mclass_type)
1137 self.add("{res} = {value};")
1138 return res
1139 end
1140
1141 # Generate a string value
1142 fun string_instance(string: String): RuntimeVariable
1143 do
1144 var mtype = self.get_class("String").mclass_type
1145 var name = self.get_name("varonce")
1146 self.add_decl("static {mtype.ctype} {name};")
1147 var res = self.new_var(mtype)
1148 self.add("if ({name}) \{")
1149 self.add("{res} = {name};")
1150 self.add("\} else \{")
1151 var native_mtype = self.get_class("NativeString").mclass_type
1152 var nat = self.new_var(native_mtype)
1153 self.add("{nat} = \"{string.escape_to_c}\";")
1154 var length = self.int_instance(string.length)
1155 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1156 self.add("{name} = {res};")
1157 self.add("\}")
1158 return res
1159 end
1160
1161 # Generate an array value
1162 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1163
1164 # Get an instance of a array for a vararg
1165 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1166
1167 # Code generation
1168
1169 # Add a line in the main part of the generated C
1170 fun add(s: String) do self.writer.lines.add(s)
1171
1172 # Add a line in the
1173 # (used for local or global declaration)
1174 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1175
1176 # Request the presence of a global declaration
1177 fun require_declaration(key: String)
1178 do
1179 var reqs = self.writer.file.required_declarations
1180 if reqs.has(key) then return
1181 reqs.add(key)
1182 var node = current_node
1183 if node != null then compiler.requirers_of_declarations[key] = node
1184 end
1185
1186 # Add a declaration in the local-header
1187 # The declaration is ensured to be present once
1188 fun declare_once(s: String)
1189 do
1190 self.compiler.provide_declaration(s, s)
1191 self.require_declaration(s)
1192 end
1193
1194 # look for a needed .h and .c file for a given .nit source-file
1195 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1196 fun add_extern(file: String)
1197 do
1198 file = file.strip_extension(".nit")
1199 var tryfile = file + ".nit.h"
1200 if tryfile.file_exists then
1201 self.declare_once("#include \"{tryfile.basename("")}\"")
1202 self.compiler.files_to_copy.add(tryfile)
1203 end
1204 tryfile = file + "_nit.h"
1205 if tryfile.file_exists then
1206 self.declare_once("#include \"{tryfile.basename("")}\"")
1207 self.compiler.files_to_copy.add(tryfile)
1208 end
1209
1210 if self.compiler.seen_extern.has(file) then return
1211 self.compiler.seen_extern.add(file)
1212 tryfile = file + ".nit.c"
1213 if not tryfile.file_exists then
1214 tryfile = file + "_nit.c"
1215 if not tryfile.file_exists then return
1216 end
1217 var f = new ExternCFile(tryfile.basename(""), "")
1218 self.compiler.extern_bodies.add(f)
1219 self.compiler.files_to_copy.add(tryfile)
1220 end
1221
1222 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1223 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1224 do
1225 var res = new_var(mtype)
1226 self.add("{res} = {cexpr};")
1227 return res
1228 end
1229
1230 # Generate generic abort
1231 # used by aborts, asserts, casts, etc.
1232 fun add_abort(message: String)
1233 do
1234 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1235 add_raw_abort
1236 end
1237
1238 fun add_raw_abort
1239 do
1240 if self.current_node != null and self.current_node.location.file != null then
1241 self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1242 else
1243 self.add("PRINT_ERROR(\"\\n\");")
1244 end
1245 self.add("show_backtrace(1);")
1246 end
1247
1248 # Add a dynamic cast
1249 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1250 do
1251 var res = self.type_test(value, mtype, tag)
1252 self.add("if (unlikely(!{res})) \{")
1253 var cn = self.class_name_string(value)
1254 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1255 self.add_raw_abort
1256 self.add("\}")
1257 end
1258
1259 # Generate a return with the value `s`
1260 fun ret(s: RuntimeVariable)
1261 do
1262 self.assign(self.frame.returnvar.as(not null), s)
1263 self.add("goto {self.frame.returnlabel.as(not null)};")
1264 end
1265
1266 # Compile a statement (if any)
1267 fun stmt(nexpr: nullable AExpr)
1268 do
1269 if nexpr == null then return
1270 var old = self.current_node
1271 self.current_node = nexpr
1272 nexpr.stmt(self)
1273 self.current_node = old
1274 end
1275
1276 # Compile an expression an return its result
1277 # `mtype` is the expected return type, pass null if no specific type is expected.
1278 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1279 do
1280 var old = self.current_node
1281 self.current_node = nexpr
1282 var res = nexpr.expr(self).as(not null)
1283 if mtype != null then
1284 mtype = self.anchor(mtype)
1285 res = self.autobox(res, mtype)
1286 end
1287 res = autoadapt(res, nexpr.mtype.as(not null))
1288 var implicit_cast_to = nexpr.implicit_cast_to
1289 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1290 add_cast(res, implicit_cast_to, "auto")
1291 res = autoadapt(res, implicit_cast_to)
1292 end
1293 self.current_node = old
1294 return res
1295 end
1296
1297 # Alias for `self.expr(nexpr, self.bool_type)`
1298 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1299
1300 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1301 fun debug(message: String)
1302 do
1303 var node = self.current_node
1304 if node == null then
1305 print "?: {message}"
1306 else
1307 node.debug(message)
1308 end
1309 self.add("/* DEBUG: {message} */")
1310 end
1311 end
1312
1313 # A C function associated to a Nit method
1314 # Because of customization, a given Nit method can be compiler more that once
1315 abstract class AbstractRuntimeFunction
1316
1317 type COMPILER: AbstractCompiler
1318 type VISITOR: AbstractCompilerVisitor
1319
1320 # The associated Nit method
1321 var mmethoddef: MMethodDef
1322
1323 # The mangled c name of the runtime_function
1324 # Subclasses should redefine `build_c_name` instead
1325 fun c_name: String
1326 do
1327 var res = self.c_name_cache
1328 if res != null then return res
1329 res = self.build_c_name
1330 self.c_name_cache = res
1331 return res
1332 end
1333
1334 # Non cached version of `c_name`
1335 protected fun build_c_name: String is abstract
1336
1337 protected var c_name_cache: nullable String writable = null
1338
1339 # Implements a call of the runtime_function
1340 # May inline the body or generate a C function call
1341 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1342
1343 # Generate the code for the `AbstractRuntimeFunction`
1344 # Warning: compile more than once compilation makes CC unhappy
1345 fun compile_to_c(compiler: COMPILER) is abstract
1346 end
1347
1348 # A runtime variable hold a runtime value in C.
1349 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1350 #
1351 # 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.
1352 class RuntimeVariable
1353 # The name of the variable in the C code
1354 var name: String
1355
1356 # The static type of the variable (as declard in C)
1357 var mtype: MType
1358
1359 # The current casted type of the variable (as known in Nit)
1360 var mcasttype: MType writable
1361
1362 # If the variable exaclty a mcasttype?
1363 # false (usual value) means that the variable is a mcasttype or a subtype.
1364 var is_exact: Bool writable = false
1365
1366 init(name: String, mtype: MType, mcasttype: MType)
1367 do
1368 self.name = name
1369 self.mtype = mtype
1370 self.mcasttype = mcasttype
1371 assert not mtype.need_anchor
1372 assert not mcasttype.need_anchor
1373 end
1374
1375 redef fun to_s do return name
1376
1377 redef fun inspect
1378 do
1379 var exact_str
1380 if self.is_exact then
1381 exact_str = " exact"
1382 else
1383 exact_str = ""
1384 end
1385 var type_str
1386 if self.mtype == self.mcasttype then
1387 type_str = "{mtype}{exact_str}"
1388 else
1389 type_str = "{mtype}({mcasttype}{exact_str})"
1390 end
1391 return "<{name}:{type_str}>"
1392 end
1393 end
1394
1395 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1396 class Frame
1397
1398 type VISITOR: AbstractCompilerVisitor
1399
1400 # The associated visitor
1401 var visitor: VISITOR
1402
1403 # The executed property.
1404 # A Method in case of a call, an attribute in case of a default initialization.
1405 var mpropdef: MPropDef
1406
1407 # The static type of the receiver
1408 var receiver: MClassType
1409
1410 # Arguments of the method (the first is the receiver)
1411 var arguments: Array[RuntimeVariable]
1412
1413 # The runtime_variable associated to the return (in a function)
1414 var returnvar: nullable RuntimeVariable writable = null
1415
1416 # The label at the end of the property
1417 var returnlabel: nullable String writable = null
1418 end
1419
1420 redef class MType
1421 # Return the C type associated to a given Nit static type
1422 fun ctype: String do return "val*"
1423
1424 fun ctypename: String do return "val"
1425
1426 # Return the name of the C structure associated to a Nit live type
1427 fun c_name: String is abstract
1428 protected var c_name_cache: nullable String protected writable
1429 end
1430
1431 redef class MClassType
1432 redef fun c_name
1433 do
1434 var res = self.c_name_cache
1435 if res != null then return res
1436 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1437 self.c_name_cache = res
1438 return res
1439 end
1440
1441 redef fun ctype: String
1442 do
1443 if mclass.name == "Int" then
1444 return "long"
1445 else if mclass.name == "Bool" then
1446 return "short int"
1447 else if mclass.name == "Char" then
1448 return "char"
1449 else if mclass.name == "Float" then
1450 return "double"
1451 else if mclass.name == "NativeString" then
1452 return "char*"
1453 else if mclass.name == "NativeArray" then
1454 return "val*"
1455 else if mclass.kind == extern_kind then
1456 return "void*"
1457 else
1458 return "val*"
1459 end
1460 end
1461
1462 redef fun ctypename: String
1463 do
1464 if mclass.name == "Int" then
1465 return "l"
1466 else if mclass.name == "Bool" then
1467 return "s"
1468 else if mclass.name == "Char" then
1469 return "c"
1470 else if mclass.name == "Float" then
1471 return "d"
1472 else if mclass.name == "NativeString" then
1473 return "str"
1474 else if mclass.name == "NativeArray" then
1475 #return "{self.arguments.first.ctype}*"
1476 return "val"
1477 else if mclass.kind == extern_kind then
1478 return "ptr"
1479 else
1480 return "val"
1481 end
1482 end
1483 end
1484
1485 redef class MGenericType
1486 redef fun c_name
1487 do
1488 var res = self.c_name_cache
1489 if res != null then return res
1490 res = super
1491 for t in self.arguments do
1492 res = res + t.c_name
1493 end
1494 self.c_name_cache = res
1495 return res
1496 end
1497 end
1498
1499 redef class MParameterType
1500 redef fun c_name
1501 do
1502 var res = self.c_name_cache
1503 if res != null then return res
1504 res = "{self.mclass.c_name}_FT{self.rank}"
1505 self.c_name_cache = res
1506 return res
1507 end
1508 end
1509
1510 redef class MVirtualType
1511 redef fun c_name
1512 do
1513 var res = self.c_name_cache
1514 if res != null then return res
1515 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1516 self.c_name_cache = res
1517 return res
1518 end
1519 end
1520
1521 redef class MNullableType
1522 redef fun c_name
1523 do
1524 var res = self.c_name_cache
1525 if res != null then return res
1526 res = "nullable_{self.mtype.c_name}"
1527 self.c_name_cache = res
1528 return res
1529 end
1530 end
1531
1532 redef class MClass
1533 # Return the name of the C structure associated to a Nit class
1534 fun c_name: String do
1535 var res = self.c_name_cache
1536 if res != null then return res
1537 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1538 self.c_name_cache = res
1539 return res
1540 end
1541 private var c_name_cache: nullable String
1542 end
1543
1544 redef class MProperty
1545 fun c_name: String do
1546 var res = self.c_name_cache
1547 if res != null then return res
1548 res = "{self.intro.c_name}"
1549 self.c_name_cache = res
1550 return res
1551 end
1552 private var c_name_cache: nullable String
1553 end
1554
1555 redef class MPropDef
1556 type VISITOR: AbstractCompilerVisitor
1557
1558 private var c_name_cache: nullable String
1559
1560 # The mangled name associated to the property
1561 fun c_name: String
1562 do
1563 var res = self.c_name_cache
1564 if res != null then return res
1565 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1566 self.c_name_cache = res
1567 return res
1568 end
1569 end
1570
1571 redef class MMethodDef
1572 # Can the body be inlined?
1573 fun can_inline(v: VISITOR): Bool
1574 do
1575 if is_abstract then return true
1576 var modelbuilder = v.compiler.modelbuilder
1577 if modelbuilder.mpropdef2npropdef.has_key(self) then
1578 var npropdef = modelbuilder.mpropdef2npropdef[self]
1579 return npropdef.can_inline
1580 else if self.mproperty.name == "init" then
1581 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1582 return true
1583 else
1584 abort
1585 end
1586 end
1587
1588 # Inline the body in another visitor
1589 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1590 do
1591 var modelbuilder = v.compiler.modelbuilder
1592 if modelbuilder.mpropdef2npropdef.has_key(self) then
1593 var npropdef = modelbuilder.mpropdef2npropdef[self]
1594 var oldnode = v.current_node
1595 v.current_node = npropdef
1596 self.compile_parameter_check(v, arguments)
1597 npropdef.compile_to_c(v, self, arguments)
1598 v.current_node = oldnode
1599 else if self.mproperty.name == "init" then
1600 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1601 var oldnode = v.current_node
1602 v.current_node = nclassdef
1603 self.compile_parameter_check(v, arguments)
1604 nclassdef.compile_to_c(v, self, arguments)
1605 v.current_node = oldnode
1606 else
1607 abort
1608 end
1609 return null
1610 end
1611
1612 # Generate type checks in the C code to check covariant parameters
1613 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1614 do
1615 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1616
1617 for i in [0..msignature.arity[ do
1618 # skip test for vararg since the array is instantiated with the correct polymorphic type
1619 if msignature.vararg_rank == i then continue
1620
1621 # skip if the cast is not required
1622 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1623 if not origmtype.need_anchor then continue
1624
1625 # get the parameter type
1626 var mtype = self.msignature.mparameters[i].mtype
1627
1628 # generate the cast
1629 # note that v decides if and how to implements the cast
1630 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1631 v.add_cast(arguments[i+1], mtype, "covariance")
1632 end
1633 end
1634 end
1635
1636 # Node visit
1637
1638 redef class APropdef
1639 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1640 do
1641 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1642 debug("Not yet implemented")
1643 end
1644
1645 fun can_inline: Bool do return true
1646 end
1647
1648 redef class AMethPropdef
1649 redef fun compile_to_c(v, mpropdef, arguments)
1650 do
1651 if mpropdef.is_abstract then
1652 var cn = v.class_name_string(arguments.first)
1653 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1654 v.add_raw_abort
1655 return
1656 end
1657
1658 # Call the implicit super-init
1659 var auto_super_inits = self.auto_super_inits
1660 if auto_super_inits != null then
1661 var args = [arguments.first]
1662 for auto_super_init in auto_super_inits do
1663 args.clear
1664 for i in [0..auto_super_init.msignature.arity+1[ do
1665 args.add(arguments[i])
1666 end
1667 v.compile_callsite(auto_super_init, args)
1668 end
1669 end
1670
1671 var n_block = n_block
1672 if n_block != null then
1673 for i in [0..mpropdef.msignature.arity[ do
1674 var variable = self.n_signature.n_params[i].variable.as(not null)
1675 v.assign(v.variable(variable), arguments[i+1])
1676 end
1677 v.stmt(n_block)
1678 else if mpropdef.is_intern then
1679 compile_intern_to_c(v, mpropdef, arguments)
1680 else if mpropdef.is_extern then
1681 if mpropdef.mproperty.is_init then
1682 compile_externinit_to_c(v, mpropdef, arguments)
1683 else
1684 compile_externmeth_to_c(v, mpropdef, arguments)
1685 end
1686 end
1687 end
1688
1689 redef fun can_inline
1690 do
1691 if self.auto_super_inits != null then return false
1692 var nblock = self.n_block
1693 if nblock == null then return true
1694 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1695 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1696 return false
1697 end
1698
1699 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1700 do
1701 var pname = mpropdef.mproperty.name
1702 var cname = mpropdef.mclassdef.mclass.name
1703 var ret = mpropdef.msignature.return_mtype
1704 if ret != null then
1705 ret = v.resolve_for(ret, arguments.first)
1706 end
1707 if pname != "==" and pname != "!=" then
1708 v.adapt_signature(mpropdef, arguments)
1709 end
1710 if cname == "Int" then
1711 if pname == "output" then
1712 v.add("printf(\"%ld\\n\", {arguments.first});")
1713 return
1714 else if pname == "object_id" then
1715 v.ret(arguments.first)
1716 return
1717 else if pname == "+" then
1718 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1719 return
1720 else if pname == "-" then
1721 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1722 return
1723 else if pname == "unary -" then
1724 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1725 return
1726 else if pname == "*" then
1727 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1728 return
1729 else if pname == "/" then
1730 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1731 return
1732 else if pname == "%" then
1733 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1734 return
1735 else if pname == "lshift" then
1736 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1737 return
1738 else if pname == "rshift" then
1739 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1740 return
1741 else if pname == "==" then
1742 v.ret(v.equal_test(arguments[0], arguments[1]))
1743 return
1744 else if pname == "!=" then
1745 var res = v.equal_test(arguments[0], arguments[1])
1746 v.ret(v.new_expr("!{res}", ret.as(not null)))
1747 return
1748 else if pname == "<" then
1749 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1750 return
1751 else if pname == ">" then
1752 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1753 return
1754 else if pname == "<=" then
1755 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1756 return
1757 else if pname == ">=" then
1758 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1759 return
1760 else if pname == "to_f" then
1761 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1762 return
1763 else if pname == "ascii" then
1764 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1765 return
1766 end
1767 else if cname == "Char" then
1768 if pname == "output" then
1769 v.add("printf(\"%c\", {arguments.first});")
1770 return
1771 else if pname == "object_id" then
1772 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1773 return
1774 else if pname == "successor" then
1775 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1776 return
1777 else if pname == "predecessor" then
1778 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1779 return
1780 else if pname == "==" then
1781 v.ret(v.equal_test(arguments[0], arguments[1]))
1782 return
1783 else if pname == "!=" then
1784 var res = v.equal_test(arguments[0], arguments[1])
1785 v.ret(v.new_expr("!{res}", ret.as(not null)))
1786 return
1787 else if pname == "<" then
1788 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1789 return
1790 else if pname == ">" then
1791 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1792 return
1793 else if pname == "<=" then
1794 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1795 return
1796 else if pname == ">=" then
1797 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1798 return
1799 else if pname == "to_i" then
1800 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1801 return
1802 else if pname == "ascii" then
1803 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1804 return
1805 end
1806 else if cname == "Bool" then
1807 if pname == "output" then
1808 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1809 return
1810 else if pname == "object_id" then
1811 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1812 return
1813 else if pname == "==" then
1814 v.ret(v.equal_test(arguments[0], arguments[1]))
1815 return
1816 else if pname == "!=" then
1817 var res = v.equal_test(arguments[0], arguments[1])
1818 v.ret(v.new_expr("!{res}", ret.as(not null)))
1819 return
1820 end
1821 else if cname == "Float" then
1822 if pname == "output" then
1823 v.add("printf(\"%f\\n\", {arguments.first});")
1824 return
1825 else if pname == "object_id" then
1826 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1827 return
1828 else if pname == "+" then
1829 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1830 return
1831 else if pname == "-" then
1832 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1833 return
1834 else if pname == "unary -" then
1835 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1836 return
1837 else if pname == "succ" then
1838 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1839 return
1840 else if pname == "prec" then
1841 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1842 return
1843 else if pname == "*" then
1844 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1845 return
1846 else if pname == "/" then
1847 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1848 return
1849 else if pname == "==" then
1850 v.ret(v.equal_test(arguments[0], arguments[1]))
1851 return
1852 else if pname == "!=" then
1853 var res = v.equal_test(arguments[0], arguments[1])
1854 v.ret(v.new_expr("!{res}", ret.as(not null)))
1855 return
1856 else if pname == "<" then
1857 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1858 return
1859 else if pname == ">" then
1860 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1861 return
1862 else if pname == "<=" then
1863 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1864 return
1865 else if pname == ">=" then
1866 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1867 return
1868 else if pname == "to_i" then
1869 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1870 return
1871 end
1872 else if cname == "NativeString" then
1873 if pname == "[]" then
1874 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1875 return
1876 else if pname == "[]=" then
1877 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1878 return
1879 else if pname == "copy_to" then
1880 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1881 return
1882 else if pname == "atoi" then
1883 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1884 return
1885 end
1886 else if cname == "NativeArray" then
1887 v.native_array_def(pname, ret, arguments)
1888 return
1889 end
1890 if pname == "exit" then
1891 v.add("exit({arguments[1]});")
1892 return
1893 else if pname == "sys" then
1894 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1895 return
1896 else if pname == "calloc_string" then
1897 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1898 return
1899 else if pname == "calloc_array" then
1900 v.calloc_array(ret.as(not null), arguments)
1901 return
1902 else if pname == "object_id" then
1903 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1904 return
1905 else if pname == "is_same_type" then
1906 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1907 return
1908 else if pname == "is_same_instance" then
1909 v.ret(v.equal_test(arguments[0], arguments[1]))
1910 return
1911 else if pname == "output_class_name" then
1912 var nat = v.class_name_string(arguments.first)
1913 v.add("printf(\"%s\\n\", {nat});")
1914 return
1915 else if pname == "native_class_name" then
1916 var nat = v.class_name_string(arguments.first)
1917 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1918 return
1919 else if pname == "force_garbage_collection" then
1920 v.add("nit_gcollect();")
1921 return
1922 else if pname == "native_argc" then
1923 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1924 return
1925 else if pname == "native_argv" then
1926 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1927 return
1928 end
1929 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1930 debug("Not implemented {mpropdef}")
1931 end
1932
1933 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1934 do
1935 var externname
1936 var nextern = self.n_extern
1937 if nextern == null then
1938 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1939 v.add("show_backtrace(1);")
1940 return
1941 end
1942 externname = nextern.text.substring(1, nextern.text.length-2)
1943 if location.file != null then
1944 var file = location.file.filename
1945 v.add_extern(file)
1946 end
1947 var res: nullable RuntimeVariable = null
1948 var ret = mpropdef.msignature.return_mtype
1949 if ret != null then
1950 ret = v.resolve_for(ret, arguments.first)
1951 res = v.new_var(ret)
1952 end
1953 v.adapt_signature(mpropdef, arguments)
1954
1955 if res == null then
1956 v.add("{externname}({arguments.join(", ")});")
1957 else
1958 v.add("{res} = {externname}({arguments.join(", ")});")
1959 v.ret(res)
1960 end
1961 end
1962
1963 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1964 do
1965 var externname
1966 var nextern = self.n_extern
1967 if nextern == null then
1968 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1969 v.add("show_backtrace(1);")
1970 return
1971 end
1972 externname = nextern.text.substring(1, nextern.text.length-2)
1973 if location.file != null then
1974 var file = location.file.filename
1975 v.add_extern(file)
1976 end
1977 v.adapt_signature(mpropdef, arguments)
1978 var ret = arguments.first.mtype
1979 var res = v.new_var(ret)
1980
1981 arguments.shift
1982
1983 v.add("{res} = {externname}({arguments.join(", ")});")
1984 v.ret(res)
1985 end
1986 end
1987
1988 redef class AAttrPropdef
1989 redef fun compile_to_c(v, mpropdef, arguments)
1990 do
1991 if arguments.length == 1 then
1992 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1993 v.assign(v.frame.returnvar.as(not null), res)
1994 else
1995 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1996 end
1997 end
1998
1999 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2000 do
2001 var nexpr = self.n_expr
2002 if nexpr != null then
2003 var oldnode = v.current_node
2004 v.current_node = self
2005 var old_frame = v.frame
2006 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2007 v.frame = frame
2008 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2009 v.write_attribute(self.mpropdef.mproperty, recv, value)
2010 v.frame = old_frame
2011 v.current_node = oldnode
2012 end
2013 end
2014
2015 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2016 do
2017 var nexpr = self.n_expr
2018 if nexpr != null then return
2019
2020 var oldnode = v.current_node
2021 v.current_node = self
2022 var old_frame = v.frame
2023 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2024 v.frame = frame
2025 # Force read to check the initialization
2026 v.read_attribute(self.mpropdef.mproperty, recv)
2027 v.frame = old_frame
2028 v.current_node = oldnode
2029 end
2030 end
2031
2032 redef class AClassdef
2033 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2034 do
2035 if mpropdef == self.mfree_init then
2036 var super_inits = self.super_inits
2037 if super_inits != null then
2038 var args_of_super = arguments
2039 if arguments.length > 1 then args_of_super = [arguments.first]
2040 for su in super_inits do
2041 v.send(su, args_of_super)
2042 end
2043 end
2044 var recv = arguments.first
2045 var i = 1
2046 # Collect undefined attributes
2047 for npropdef in self.n_propdefs do
2048 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
2049 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
2050 i += 1
2051 end
2052 end
2053 else
2054 abort
2055 end
2056 end
2057 end
2058
2059 redef class AExpr
2060 # Try to compile self as an expression
2061 # Do not call this method directly, use `v.expr` instead
2062 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2063 do
2064 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2065 var mtype = self.mtype
2066 if mtype == null then
2067 return null
2068 else
2069 var res = v.new_var(mtype)
2070 v.add("/* {res} = NOT YET {class_name} */")
2071 return res
2072 end
2073 end
2074
2075 # Try to compile self as a statement
2076 # Do not call this method directly, use `v.stmt` instead
2077 private fun stmt(v: AbstractCompilerVisitor)
2078 do
2079 var res = expr(v)
2080 if res != null then v.add("{res};")
2081 end
2082 end
2083
2084 redef class ABlockExpr
2085 redef fun stmt(v)
2086 do
2087 for e in self.n_expr do v.stmt(e)
2088 end
2089 redef fun expr(v)
2090 do
2091 var last = self.n_expr.last
2092 for e in self.n_expr do
2093 if e == last then break
2094 v.stmt(e)
2095 end
2096 return v.expr(last, null)
2097 end
2098 end
2099
2100 redef class AVardeclExpr
2101 redef fun stmt(v)
2102 do
2103 var variable = self.variable.as(not null)
2104 var ne = self.n_expr
2105 if ne != null then
2106 var i = v.expr(ne, variable.declared_type)
2107 v.assign(v.variable(variable), i)
2108 end
2109 end
2110 end
2111
2112 redef class AVarExpr
2113 redef fun expr(v)
2114 do
2115 var res = v.variable(self.variable.as(not null))
2116 var mtype = self.mtype.as(not null)
2117 return v.autoadapt(res, mtype)
2118 end
2119 end
2120
2121 redef class AVarAssignExpr
2122 redef fun stmt(v)
2123 do
2124 var variable = self.variable.as(not null)
2125 var i = v.expr(self.n_value, variable.declared_type)
2126 v.assign(v.variable(variable), i)
2127 end
2128 redef fun expr(v)
2129 do
2130 var variable = self.variable.as(not null)
2131 var i = v.expr(self.n_value, variable.declared_type)
2132 v.assign(v.variable(variable), i)
2133 return i
2134 end
2135 end
2136
2137 redef class AVarReassignExpr
2138 redef fun stmt(v)
2139 do
2140 var variable = self.variable.as(not null)
2141 var vari = v.variable(variable)
2142 var value = v.expr(self.n_value, variable.declared_type)
2143 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2144 assert res != null
2145 v.assign(v.variable(variable), res)
2146 end
2147 end
2148
2149 redef class ASelfExpr
2150 redef fun expr(v) do return v.frame.arguments.first
2151 end
2152
2153 redef class AContinueExpr
2154 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2155 end
2156
2157 redef class ABreakExpr
2158 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2159 end
2160
2161 redef class AReturnExpr
2162 redef fun stmt(v)
2163 do
2164 var nexpr = self.n_expr
2165 if nexpr != null then
2166 var returnvar = v.frame.returnvar.as(not null)
2167 var i = v.expr(nexpr, returnvar.mtype)
2168 v.assign(returnvar, i)
2169 end
2170 v.add("goto {v.frame.returnlabel.as(not null)};")
2171 end
2172 end
2173
2174 redef class AAbortExpr
2175 redef fun stmt(v) do v.add_abort("Aborted")
2176 end
2177
2178 redef class AIfExpr
2179 redef fun stmt(v)
2180 do
2181 var cond = v.expr_bool(self.n_expr)
2182 v.add("if ({cond})\{")
2183 v.stmt(self.n_then)
2184 v.add("\} else \{")
2185 v.stmt(self.n_else)
2186 v.add("\}")
2187 end
2188
2189 redef fun expr(v)
2190 do
2191 var res = v.new_var(self.mtype.as(not null))
2192 var cond = v.expr_bool(self.n_expr)
2193 v.add("if ({cond})\{")
2194 v.assign(res, v.expr(self.n_then.as(not null), null))
2195 v.add("\} else \{")
2196 v.assign(res, v.expr(self.n_else.as(not null), null))
2197 v.add("\}")
2198 return res
2199 end
2200 end
2201
2202 redef class AIfexprExpr
2203 redef fun expr(v)
2204 do
2205 var res = v.new_var(self.mtype.as(not null))
2206 var cond = v.expr_bool(self.n_expr)
2207 v.add("if ({cond})\{")
2208 v.assign(res, v.expr(self.n_then, null))
2209 v.add("\} else \{")
2210 v.assign(res, v.expr(self.n_else, null))
2211 v.add("\}")
2212 return res
2213 end
2214 end
2215
2216 redef class ADoExpr
2217 redef fun stmt(v)
2218 do
2219 v.stmt(self.n_block)
2220 var escapemark = self.escapemark
2221 if escapemark != null then
2222 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2223 end
2224 end
2225 end
2226
2227 redef class AWhileExpr
2228 redef fun stmt(v)
2229 do
2230 v.add("for(;;) \{")
2231 var cond = v.expr_bool(self.n_expr)
2232 v.add("if (!{cond}) break;")
2233 v.stmt(self.n_block)
2234 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2235 v.add("\}")
2236 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2237 end
2238 end
2239
2240 redef class ALoopExpr
2241 redef fun stmt(v)
2242 do
2243 v.add("for(;;) \{")
2244 v.stmt(self.n_block)
2245 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2246 v.add("\}")
2247 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2248 end
2249 end
2250
2251 redef class AForExpr
2252 redef fun stmt(v)
2253 do
2254 # Shortcut on explicit range
2255 # Avoid the instantiation of the range and the iterator
2256 var nexpr = self.n_expr
2257 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2258 var from = v.expr(nexpr.n_expr, null)
2259 var to = v.expr(nexpr.n_expr2, null)
2260 var variable = v.variable(variables.first)
2261
2262 v.assign(variable, from)
2263 v.add("for(;;) \{ /* shortcut range */")
2264
2265 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2266 assert ok != null
2267 v.add("if(!{ok}) break;")
2268
2269 v.stmt(self.n_block)
2270
2271 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2272 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2273 assert succ != null
2274 v.assign(variable, succ)
2275 v.add("\}")
2276 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2277 return
2278 end
2279
2280 var cl = v.expr(self.n_expr, null)
2281 var it_meth = self.method_iterator
2282 assert it_meth != null
2283 var it = v.compile_callsite(it_meth, [cl])
2284 assert it != null
2285 v.add("for(;;) \{")
2286 var isok_meth = self.method_is_ok
2287 assert isok_meth != null
2288 var ok = v.compile_callsite(isok_meth, [it])
2289 assert ok != null
2290 v.add("if(!{ok}) break;")
2291 if self.variables.length == 1 then
2292 var item_meth = self.method_item
2293 assert item_meth != null
2294 var i = v.compile_callsite(item_meth, [it])
2295 assert i != null
2296 v.assign(v.variable(variables.first), i)
2297 else if self.variables.length == 2 then
2298 var key_meth = self.method_key
2299 assert key_meth != null
2300 var i = v.compile_callsite(key_meth, [it])
2301 assert i != null
2302 v.assign(v.variable(variables[0]), i)
2303 var item_meth = self.method_item
2304 assert item_meth != null
2305 i = v.compile_callsite(item_meth, [it])
2306 assert i != null
2307 v.assign(v.variable(variables[1]), i)
2308 else
2309 abort
2310 end
2311 v.stmt(self.n_block)
2312 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2313 var next_meth = self.method_next
2314 assert next_meth != null
2315 v.compile_callsite(next_meth, [it])
2316 v.add("\}")
2317 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2318 end
2319 end
2320
2321 redef class AAssertExpr
2322 redef fun stmt(v)
2323 do
2324 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2325
2326 var cond = v.expr_bool(self.n_expr)
2327 v.add("if (unlikely(!{cond})) \{")
2328 v.stmt(self.n_else)
2329 var nid = self.n_id
2330 if nid != null then
2331 v.add_abort("Assert '{nid.text}' failed")
2332 else
2333 v.add_abort("Assert failed")
2334 end
2335 v.add("\}")
2336 end
2337 end
2338
2339 redef class AOrExpr
2340 redef fun expr(v)
2341 do
2342 var res = v.new_var(self.mtype.as(not null))
2343 var i1 = v.expr_bool(self.n_expr)
2344 v.add("if ({i1}) \{")
2345 v.add("{res} = 1;")
2346 v.add("\} else \{")
2347 var i2 = v.expr_bool(self.n_expr2)
2348 v.add("{res} = {i2};")
2349 v.add("\}")
2350 return res
2351 end
2352 end
2353
2354 redef class AImpliesExpr
2355 redef fun expr(v)
2356 do
2357 var res = v.new_var(self.mtype.as(not null))
2358 var i1 = v.expr_bool(self.n_expr)
2359 v.add("if (!{i1}) \{")
2360 v.add("{res} = 1;")
2361 v.add("\} else \{")
2362 var i2 = v.expr_bool(self.n_expr2)
2363 v.add("{res} = {i2};")
2364 v.add("\}")
2365 return res
2366 end
2367 end
2368
2369 redef class AAndExpr
2370 redef fun expr(v)
2371 do
2372 var res = v.new_var(self.mtype.as(not null))
2373 var i1 = v.expr_bool(self.n_expr)
2374 v.add("if (!{i1}) \{")
2375 v.add("{res} = 0;")
2376 v.add("\} else \{")
2377 var i2 = v.expr_bool(self.n_expr2)
2378 v.add("{res} = {i2};")
2379 v.add("\}")
2380 return res
2381 end
2382 end
2383
2384 redef class ANotExpr
2385 redef fun expr(v)
2386 do
2387 var cond = v.expr_bool(self.n_expr)
2388 return v.new_expr("!{cond}", self.mtype.as(not null))
2389 end
2390 end
2391
2392 redef class AOrElseExpr
2393 redef fun expr(v)
2394 do
2395 var res = v.new_var(self.mtype.as(not null))
2396 var i1 = v.expr(self.n_expr, null)
2397 v.add("if ({i1}!=NULL) \{")
2398 v.assign(res, i1)
2399 v.add("\} else \{")
2400 var i2 = v.expr(self.n_expr2, null)
2401 v.assign(res, i2)
2402 v.add("\}")
2403 return res
2404 end
2405 end
2406
2407 redef class AIntExpr
2408 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2409 end
2410
2411 redef class AFloatExpr
2412 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2413 end
2414
2415 redef class ACharExpr
2416 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2417 end
2418
2419 redef class AArrayExpr
2420 redef fun expr(v)
2421 do
2422 var mtype = self.mtype.as(MClassType).arguments.first
2423 var array = new Array[RuntimeVariable]
2424 for nexpr in self.n_exprs.n_exprs do
2425 var i = v.expr(nexpr, mtype)
2426 array.add(i)
2427 end
2428 return v.array_instance(array, mtype)
2429 end
2430 end
2431
2432 redef class AStringFormExpr
2433 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2434 end
2435
2436 redef class ASuperstringExpr
2437 redef fun expr(v)
2438 do
2439 var array = new Array[RuntimeVariable]
2440 for ne in self.n_exprs do
2441 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2442 var i = v.expr(ne, null)
2443 array.add(i)
2444 end
2445 var a = v.array_instance(array, v.object_type)
2446 var res = v.send(v.get_property("to_s", a.mtype), [a])
2447 return res
2448 end
2449 end
2450
2451 redef class ACrangeExpr
2452 redef fun expr(v)
2453 do
2454 var i1 = v.expr(self.n_expr, null)
2455 var i2 = v.expr(self.n_expr2, null)
2456 var mtype = self.mtype.as(MClassType)
2457 var res = v.init_instance(mtype)
2458 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2459 return res
2460 end
2461 end
2462
2463 redef class AOrangeExpr
2464 redef fun expr(v)
2465 do
2466 var i1 = v.expr(self.n_expr, null)
2467 var i2 = v.expr(self.n_expr2, null)
2468 var mtype = self.mtype.as(MClassType)
2469 var res = v.init_instance(mtype)
2470 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2471 return res
2472 end
2473 end
2474
2475 redef class ATrueExpr
2476 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2477 end
2478
2479 redef class AFalseExpr
2480 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2481 end
2482
2483 redef class ANullExpr
2484 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2485 end
2486
2487 redef class AIsaExpr
2488 redef fun expr(v)
2489 do
2490 var i = v.expr(self.n_expr, null)
2491 return v.type_test(i, self.cast_type.as(not null), "isa")
2492 end
2493 end
2494
2495 redef class AAsCastExpr
2496 redef fun expr(v)
2497 do
2498 var i = v.expr(self.n_expr, null)
2499 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2500
2501 v.add_cast(i, self.mtype.as(not null), "as")
2502 return i
2503 end
2504 end
2505
2506 redef class AAsNotnullExpr
2507 redef fun expr(v)
2508 do
2509 var i = v.expr(self.n_expr, null)
2510 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2511
2512 v.add("if (unlikely({i} == NULL)) \{")
2513 v.add_abort("Cast failed")
2514 v.add("\}")
2515 return i
2516 end
2517 end
2518
2519 redef class AParExpr
2520 redef fun expr(v) do return v.expr(self.n_expr, null)
2521 end
2522
2523 redef class AOnceExpr
2524 redef fun expr(v)
2525 do
2526 var mtype = self.mtype.as(not null)
2527 var name = v.get_name("varonce")
2528 var guard = v.get_name(name + "_guard")
2529 v.add_decl("static {mtype.ctype} {name};")
2530 v.add_decl("static int {guard};")
2531 var res = v.new_var(mtype)
2532 v.add("if ({guard}) \{")
2533 v.add("{res} = {name};")
2534 v.add("\} else \{")
2535 var i = v.expr(self.n_expr, mtype)
2536 v.add("{res} = {i};")
2537 v.add("{name} = {res};")
2538 v.add("{guard} = 1;")
2539 v.add("\}")
2540 return res
2541 end
2542 end
2543
2544 redef class ASendExpr
2545 redef fun expr(v)
2546 do
2547 var recv = v.expr(self.n_expr, null)
2548 var args = [recv]
2549 for a in self.raw_arguments.as(not null) do
2550 args.add(v.expr(a, null))
2551 end
2552 return v.compile_callsite(self.callsite.as(not null), args)
2553 end
2554 end
2555
2556 redef class ASendReassignFormExpr
2557 redef fun stmt(v)
2558 do
2559 var recv = v.expr(self.n_expr, null)
2560 var args = [recv]
2561 for a in self.raw_arguments.as(not null) do
2562 args.add(v.expr(a, null))
2563 end
2564 var value = v.expr(self.n_value, null)
2565
2566 var left = v.compile_callsite(self.callsite.as(not null), args)
2567 assert left != null
2568
2569 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2570 assert res != null
2571
2572 args.add(res)
2573 v.compile_callsite(self.write_callsite.as(not null), args)
2574 end
2575 end
2576
2577 redef class ASuperExpr
2578 redef fun expr(v)
2579 do
2580 var recv = v.frame.arguments.first
2581 var args = [recv]
2582 for a in self.n_args.n_exprs do
2583 args.add(v.expr(a, null))
2584 end
2585
2586 var callsite = self.callsite
2587 if callsite != null then
2588 # Add additionnals arguments for the super init call
2589 if args.length == 1 then
2590 for i in [0..callsite.msignature.arity[ do
2591 args.add(v.frame.arguments[i+1])
2592 end
2593 end
2594 # Super init call
2595 var res = v.compile_callsite(callsite, args)
2596 return res
2597 end
2598
2599 if args.length == 1 then
2600 args = v.frame.arguments
2601 end
2602
2603 # stantard call-next-method
2604 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2605 end
2606 end
2607
2608 redef class ANewExpr
2609 redef fun expr(v)
2610 do
2611 var mtype = self.mtype.as(MClassType)
2612 var recv
2613 var ctype = mtype.ctype
2614 if ctype == "val*" then
2615 recv = v.init_instance(mtype)
2616 else if ctype == "void*" then
2617 recv = v.new_expr("NULL/*special!*/", mtype)
2618 else
2619 debug("cannot new {mtype}")
2620 abort
2621 end
2622 var args = [recv]
2623 for a in self.n_args.n_exprs do
2624 args.add(v.expr(a, null))
2625 end
2626 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2627 if res2 != null then
2628 #self.debug("got {res2} from {mproperty}. drop {recv}")
2629 return res2
2630 end
2631 return recv
2632 end
2633 end
2634
2635 redef class AAttrExpr
2636 redef fun expr(v)
2637 do
2638 var recv = v.expr(self.n_expr, null)
2639 var mproperty = self.mproperty.as(not null)
2640 return v.read_attribute(mproperty, recv)
2641 end
2642 end
2643
2644 redef class AAttrAssignExpr
2645 redef fun stmt(v)
2646 do
2647 var recv = v.expr(self.n_expr, null)
2648 var i = v.expr(self.n_value, null)
2649 var mproperty = self.mproperty.as(not null)
2650 v.write_attribute(mproperty, recv, i)
2651 end
2652 end
2653
2654 redef class AAttrReassignExpr
2655 redef fun stmt(v)
2656 do
2657 var recv = v.expr(self.n_expr, null)
2658 var value = v.expr(self.n_value, null)
2659 var mproperty = self.mproperty.as(not null)
2660 var attr = v.read_attribute(mproperty, recv)
2661 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2662 assert res != null
2663 v.write_attribute(mproperty, recv, res)
2664 end
2665 end
2666
2667 redef class AIssetAttrExpr
2668 redef fun expr(v)
2669 do
2670 var recv = v.expr(self.n_expr, null)
2671 var mproperty = self.mproperty.as(not null)
2672 return v.isset_attribute(mproperty, recv)
2673 end
2674 end
2675
2676 redef class ADebugTypeExpr
2677 redef fun stmt(v)
2678 do
2679 # do nothing
2680 end
2681 end
2682
2683 # Utils
2684
2685 redef class Array[E]
2686 # Return a new `Array` with the elements only contened in self and not in `o`
2687 fun -(o: Array[E]): Array[E] do
2688 var res = new Array[E]
2689 for e in self do if not o.has(e) then res.add(e)
2690 return res
2691 end
2692 end
2693
2694 redef class MModule
2695 # All `MProperty` associated to all `MClassDef` of `mclass`
2696 fun properties(mclass: MClass): Set[MProperty] do
2697 if not self.properties_cache.has_key(mclass) then
2698 var properties = new HashSet[MProperty]
2699 var parents = new Array[MClass]
2700 if self.flatten_mclass_hierarchy.has(mclass) then
2701 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2702 end
2703 for parent in parents do
2704 properties.add_all(self.properties(parent))
2705 end
2706 for mclassdef in mclass.mclassdefs do
2707 for mprop in mclassdef.intro_mproperties do
2708 properties.add(mprop)
2709 end
2710 end
2711 self.properties_cache[mclass] = properties
2712 end
2713 return properties_cache[mclass]
2714 end
2715 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2716
2717 # Write FFI and nitni results to file
2718 fun finalize_ffi(c: AbstractCompiler) do end
2719
2720 # Give requided addinional system libraries (as given to LD_LIBS)
2721 # Note: can return null instead of an empty set
2722 fun collect_linker_libs: nullable Set[String] do return null
2723 end
2724
2725 # Create a tool context to handle options and paths
2726 var toolcontext = new ToolContext
2727
2728 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
2729 toolcontext.option_context.add_option(opt_mixins)
2730
2731 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit\nCompiles Nit programs."
2732
2733 # We do not add other options, so process them now!
2734 toolcontext.process_options(args)
2735
2736 # We need a model to collect stufs
2737 var model = new Model
2738 # An a model builder to parse files
2739 var modelbuilder = new ModelBuilder(model, toolcontext)
2740
2741 var arguments = toolcontext.option_context.rest
2742 if arguments.length > 1 then
2743 print "Too much arguments: {arguments.join(" ")}"
2744 print toolcontext.tooldescription
2745 exit 1
2746 end
2747 var progname = arguments.first
2748
2749 # Here we load an process all modules passed on the command line
2750 var mmodules = modelbuilder.parse([progname])
2751 mmodules.add_all modelbuilder.parse(opt_mixins.value)
2752
2753 if mmodules.is_empty then return
2754 modelbuilder.run_phases
2755
2756 var mainmodule
2757 if mmodules.length == 1 then
2758 mainmodule = mmodules.first
2759 else
2760 mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location)
2761 mainmodule.set_imported_mmodules(mmodules)
2762 end
2763
2764 toolcontext.run_global_phases(mmodules)