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