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