engines: no more `super_inits` method used in old-style automatic init
[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 private var escapemark_names = new HashMap[EscapeMark, String]
1270
1271 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1272 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1273 fun class_name_string(value: RuntimeVariable): String is abstract
1274
1275 # Variables handling
1276
1277 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
1278
1279 # Return the local runtime_variable associated to a Nit local variable
1280 fun variable(variable: Variable): RuntimeVariable
1281 do
1282 if self.variables.has_key(variable) then
1283 return self.variables[variable]
1284 else
1285 var name = self.get_name("var_{variable.name}")
1286 var mtype = variable.declared_type.as(not null)
1287 mtype = self.anchor(mtype)
1288 var res = new RuntimeVariable(name, mtype, mtype)
1289 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1290 self.variables[variable] = res
1291 return res
1292 end
1293 end
1294
1295 # Return a new uninitialized local runtime_variable
1296 fun new_var(mtype: MType): RuntimeVariable
1297 do
1298 mtype = self.anchor(mtype)
1299 var name = self.get_name("var")
1300 var res = new RuntimeVariable(name, mtype, mtype)
1301 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1302 return res
1303 end
1304
1305 # The difference with `new_var` is the C static type of the local variable
1306 fun new_var_extern(mtype: MType): RuntimeVariable
1307 do
1308 mtype = self.anchor(mtype)
1309 var name = self.get_name("var")
1310 var res = new RuntimeVariable(name, mtype, mtype)
1311 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1312 return res
1313 end
1314
1315 # Return a new uninitialized named runtime_variable
1316 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1317 do
1318 mtype = self.anchor(mtype)
1319 var res = new RuntimeVariable(name, mtype, mtype)
1320 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1321 return res
1322 end
1323
1324 # Correctly assign a left and a right value
1325 # Boxing and unboxing is performed if required
1326 fun assign(left, right: RuntimeVariable)
1327 do
1328 right = self.autobox(right, left.mtype)
1329 self.add("{left} = {right};")
1330 end
1331
1332 # Generate instances
1333
1334 # Generate a alloc-instance + init-attributes
1335 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1336
1337 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1338 fun set_finalizer(recv: RuntimeVariable)
1339 do
1340 var mtype = recv.mtype
1341 var finalizable_type = compiler.mainmodule.finalizable_type
1342 if finalizable_type != null and not mtype.need_anchor and
1343 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1344 add "gc_register_finalizer({recv});"
1345 end
1346 end
1347
1348 # Generate an integer value
1349 fun int_instance(value: Int): RuntimeVariable
1350 do
1351 var res = self.new_var(self.get_class("Int").mclass_type)
1352 self.add("{res} = {value};")
1353 return res
1354 end
1355
1356 # Generate a string value
1357 fun string_instance(string: String): RuntimeVariable
1358 do
1359 var mtype = self.get_class("String").mclass_type
1360 var name = self.get_name("varonce")
1361 self.add_decl("static {mtype.ctype} {name};")
1362 var res = self.new_var(mtype)
1363 self.add("if ({name}) \{")
1364 self.add("{res} = {name};")
1365 self.add("\} else \{")
1366 var native_mtype = self.get_class("NativeString").mclass_type
1367 var nat = self.new_var(native_mtype)
1368 self.add("{nat} = \"{string.escape_to_c}\";")
1369 var length = self.int_instance(string.length)
1370 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1371 self.add("{name} = {res};")
1372 self.add("\}")
1373 return res
1374 end
1375
1376 # Generate an array value
1377 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1378
1379 # Get an instance of a array for a vararg
1380 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1381
1382 # Code generation
1383
1384 # Add a line in the main part of the generated C
1385 fun add(s: String) do self.writer.lines.add(s)
1386
1387 # Add a line in the
1388 # (used for local or global declaration)
1389 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1390
1391 # Request the presence of a global declaration
1392 fun require_declaration(key: String)
1393 do
1394 var reqs = self.writer.file.required_declarations
1395 if reqs.has(key) then return
1396 reqs.add(key)
1397 var node = current_node
1398 if node != null then compiler.requirers_of_declarations[key] = node
1399 end
1400
1401 # Add a declaration in the local-header
1402 # The declaration is ensured to be present once
1403 fun declare_once(s: String)
1404 do
1405 self.compiler.provide_declaration(s, s)
1406 self.require_declaration(s)
1407 end
1408
1409 # look for a needed .h and .c file for a given .nit source-file
1410 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1411 fun add_extern(file: String)
1412 do
1413 file = file.strip_extension(".nit")
1414 var tryfile = file + ".nit.h"
1415 if tryfile.file_exists then
1416 self.declare_once("#include \"{tryfile.basename("")}\"")
1417 self.compiler.files_to_copy.add(tryfile)
1418 end
1419 tryfile = file + "_nit.h"
1420 if tryfile.file_exists then
1421 self.declare_once("#include \"{tryfile.basename("")}\"")
1422 self.compiler.files_to_copy.add(tryfile)
1423 end
1424
1425 if self.compiler.seen_extern.has(file) then return
1426 self.compiler.seen_extern.add(file)
1427 tryfile = file + ".nit.c"
1428 if not tryfile.file_exists then
1429 tryfile = file + "_nit.c"
1430 if not tryfile.file_exists then return
1431 end
1432 var f = new ExternCFile(tryfile.basename(""), "")
1433 self.compiler.extern_bodies.add(f)
1434 self.compiler.files_to_copy.add(tryfile)
1435 end
1436
1437 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1438 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1439 do
1440 var res = new_var(mtype)
1441 self.add("{res} = {cexpr};")
1442 return res
1443 end
1444
1445 # Generate generic abort
1446 # used by aborts, asserts, casts, etc.
1447 fun add_abort(message: String)
1448 do
1449 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1450 add_raw_abort
1451 end
1452
1453 fun add_raw_abort
1454 do
1455 if self.current_node != null and self.current_node.location.file != null then
1456 self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1457 else
1458 self.add("PRINT_ERROR(\"\\n\");")
1459 end
1460 self.add("show_backtrace(1);")
1461 end
1462
1463 # Add a dynamic cast
1464 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1465 do
1466 var res = self.type_test(value, mtype, tag)
1467 self.add("if (unlikely(!{res})) \{")
1468 var cn = self.class_name_string(value)
1469 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1470 self.add_raw_abort
1471 self.add("\}")
1472 end
1473
1474 # Generate a return with the value `s`
1475 fun ret(s: RuntimeVariable)
1476 do
1477 self.assign(self.frame.returnvar.as(not null), s)
1478 self.add("goto {self.frame.returnlabel.as(not null)};")
1479 end
1480
1481 # Compile a statement (if any)
1482 fun stmt(nexpr: nullable AExpr)
1483 do
1484 if nexpr == null then return
1485 var old = self.current_node
1486 self.current_node = nexpr
1487 nexpr.stmt(self)
1488 self.current_node = old
1489 end
1490
1491 # Compile an expression an return its result
1492 # `mtype` is the expected return type, pass null if no specific type is expected.
1493 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1494 do
1495 var old = self.current_node
1496 self.current_node = nexpr
1497 var res = nexpr.expr(self).as(not null)
1498 if mtype != null then
1499 mtype = self.anchor(mtype)
1500 res = self.autobox(res, mtype)
1501 end
1502 res = autoadapt(res, nexpr.mtype.as(not null))
1503 var implicit_cast_to = nexpr.implicit_cast_to
1504 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1505 add_cast(res, implicit_cast_to, "auto")
1506 res = autoadapt(res, implicit_cast_to)
1507 end
1508 self.current_node = old
1509 return res
1510 end
1511
1512 # Alias for `self.expr(nexpr, self.bool_type)`
1513 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1514
1515 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1516 fun debug(message: String)
1517 do
1518 var node = self.current_node
1519 if node == null then
1520 print "?: {message}"
1521 else
1522 node.debug(message)
1523 end
1524 self.add("/* DEBUG: {message} */")
1525 end
1526 end
1527
1528 # A C function associated to a Nit method
1529 # Because of customization, a given Nit method can be compiler more that once
1530 abstract class AbstractRuntimeFunction
1531
1532 type COMPILER: AbstractCompiler
1533 type VISITOR: AbstractCompilerVisitor
1534
1535 # The associated Nit method
1536 var mmethoddef: MMethodDef
1537
1538 # The mangled c name of the runtime_function
1539 # Subclasses should redefine `build_c_name` instead
1540 fun c_name: String
1541 do
1542 var res = self.c_name_cache
1543 if res != null then return res
1544 res = self.build_c_name
1545 self.c_name_cache = res
1546 return res
1547 end
1548
1549 # Non cached version of `c_name`
1550 protected fun build_c_name: String is abstract
1551
1552 protected var c_name_cache: nullable String = null is writable
1553
1554 # Implements a call of the runtime_function
1555 # May inline the body or generate a C function call
1556 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1557
1558 # Generate the code for the `AbstractRuntimeFunction`
1559 # Warning: compile more than once compilation makes CC unhappy
1560 fun compile_to_c(compiler: COMPILER) is abstract
1561 end
1562
1563 # A runtime variable hold a runtime value in C.
1564 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1565 #
1566 # 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.
1567 class RuntimeVariable
1568 # The name of the variable in the C code
1569 var name: String
1570
1571 # The static type of the variable (as declard in C)
1572 var mtype: MType
1573
1574 # The current casted type of the variable (as known in Nit)
1575 var mcasttype: MType is writable
1576
1577 # If the variable exaclty a mcasttype?
1578 # false (usual value) means that the variable is a mcasttype or a subtype.
1579 var is_exact: Bool = false is writable
1580
1581 init(name: String, mtype: MType, mcasttype: MType)
1582 do
1583 self.name = name
1584 self.mtype = mtype
1585 self.mcasttype = mcasttype
1586 assert not mtype.need_anchor
1587 assert not mcasttype.need_anchor
1588 end
1589
1590 redef fun to_s do return name
1591
1592 redef fun inspect
1593 do
1594 var exact_str
1595 if self.is_exact then
1596 exact_str = " exact"
1597 else
1598 exact_str = ""
1599 end
1600 var type_str
1601 if self.mtype == self.mcasttype then
1602 type_str = "{mtype}{exact_str}"
1603 else
1604 type_str = "{mtype}({mcasttype}{exact_str})"
1605 end
1606 return "<{name}:{type_str}>"
1607 end
1608 end
1609
1610 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1611 class Frame
1612
1613 type VISITOR: AbstractCompilerVisitor
1614
1615 # The associated visitor
1616 var visitor: VISITOR
1617
1618 # The executed property.
1619 # A Method in case of a call, an attribute in case of a default initialization.
1620 var mpropdef: MPropDef
1621
1622 # The static type of the receiver
1623 var receiver: MClassType
1624
1625 # Arguments of the method (the first is the receiver)
1626 var arguments: Array[RuntimeVariable]
1627
1628 # The runtime_variable associated to the return (in a function)
1629 var returnvar: nullable RuntimeVariable = null is writable
1630
1631 # The label at the end of the property
1632 var returnlabel: nullable String = null is writable
1633 end
1634
1635 redef class MType
1636 # Return the C type associated to a given Nit static type
1637 fun ctype: String do return "val*"
1638
1639 # C type outside of the compiler code and in boxes
1640 fun ctype_extern: String do return "val*"
1641
1642 # Short name of the `ctype` to use in unions
1643 fun ctypename: String do return "val"
1644
1645 # Return the name of the C structure associated to a Nit live type
1646 fun c_name: String is abstract
1647 protected var c_name_cache: nullable String is protected writable
1648 end
1649
1650 redef class MClassType
1651 redef fun c_name
1652 do
1653 var res = self.c_name_cache
1654 if res != null then return res
1655 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1656 self.c_name_cache = res
1657 return res
1658 end
1659
1660 redef fun ctype: String
1661 do
1662 if mclass.name == "Int" then
1663 return "long"
1664 else if mclass.name == "Bool" then
1665 return "short int"
1666 else if mclass.name == "Char" then
1667 return "char"
1668 else if mclass.name == "Float" then
1669 return "double"
1670 else if mclass.name == "NativeString" then
1671 return "char*"
1672 else if mclass.name == "NativeArray" then
1673 return "val*"
1674 else
1675 return "val*"
1676 end
1677 end
1678
1679 redef fun ctype_extern: String
1680 do
1681 if mclass.kind == extern_kind then
1682 return "void*"
1683 else
1684 return ctype
1685 end
1686 end
1687
1688 redef fun ctypename: String
1689 do
1690 if mclass.name == "Int" then
1691 return "l"
1692 else if mclass.name == "Bool" then
1693 return "s"
1694 else if mclass.name == "Char" then
1695 return "c"
1696 else if mclass.name == "Float" then
1697 return "d"
1698 else if mclass.name == "NativeString" then
1699 return "str"
1700 else if mclass.name == "NativeArray" then
1701 #return "{self.arguments.first.ctype}*"
1702 return "val"
1703 else
1704 return "val"
1705 end
1706 end
1707 end
1708
1709 redef class MGenericType
1710 redef fun c_name
1711 do
1712 var res = self.c_name_cache
1713 if res != null then return res
1714 res = super
1715 for t in self.arguments do
1716 res = res + t.c_name
1717 end
1718 self.c_name_cache = res
1719 return res
1720 end
1721 end
1722
1723 redef class MParameterType
1724 redef fun c_name
1725 do
1726 var res = self.c_name_cache
1727 if res != null then return res
1728 res = "{self.mclass.c_name}_FT{self.rank}"
1729 self.c_name_cache = res
1730 return res
1731 end
1732 end
1733
1734 redef class MVirtualType
1735 redef fun c_name
1736 do
1737 var res = self.c_name_cache
1738 if res != null then return res
1739 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1740 self.c_name_cache = res
1741 return res
1742 end
1743 end
1744
1745 redef class MNullableType
1746 redef fun c_name
1747 do
1748 var res = self.c_name_cache
1749 if res != null then return res
1750 res = "nullable_{self.mtype.c_name}"
1751 self.c_name_cache = res
1752 return res
1753 end
1754 end
1755
1756 redef class MClass
1757 # Return the name of the C structure associated to a Nit class
1758 fun c_name: String do
1759 var res = self.c_name_cache
1760 if res != null then return res
1761 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1762 self.c_name_cache = res
1763 return res
1764 end
1765 private var c_name_cache: nullable String
1766 end
1767
1768 redef class MProperty
1769 fun c_name: String do
1770 var res = self.c_name_cache
1771 if res != null then return res
1772 res = "{self.intro.c_name}"
1773 self.c_name_cache = res
1774 return res
1775 end
1776 private var c_name_cache: nullable String
1777 end
1778
1779 redef class MPropDef
1780 type VISITOR: AbstractCompilerVisitor
1781
1782 private var c_name_cache: nullable String
1783
1784 # The mangled name associated to the property
1785 fun c_name: String
1786 do
1787 var res = self.c_name_cache
1788 if res != null then return res
1789 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1790 self.c_name_cache = res
1791 return res
1792 end
1793 end
1794
1795 redef class MMethodDef
1796 # Can the body be inlined?
1797 fun can_inline(v: VISITOR): Bool
1798 do
1799 if is_abstract then return true
1800 var modelbuilder = v.compiler.modelbuilder
1801 if modelbuilder.mpropdef2npropdef.has_key(self) then
1802 var npropdef = modelbuilder.mpropdef2npropdef[self]
1803 return npropdef.can_inline
1804 else if self.mproperty.name == "init" then
1805 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1806 return true
1807 else
1808 abort
1809 end
1810 end
1811
1812 # Inline the body in another visitor
1813 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1814 do
1815 var modelbuilder = v.compiler.modelbuilder
1816 if modelbuilder.mpropdef2npropdef.has_key(self) then
1817 var npropdef = modelbuilder.mpropdef2npropdef[self]
1818 var oldnode = v.current_node
1819 v.current_node = npropdef
1820 self.compile_parameter_check(v, arguments)
1821 npropdef.compile_to_c(v, self, arguments)
1822 v.current_node = oldnode
1823 else if self.mproperty.name == "init" then
1824 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1825 var oldnode = v.current_node
1826 v.current_node = nclassdef
1827 self.compile_parameter_check(v, arguments)
1828 nclassdef.compile_to_c(v, self, arguments)
1829 v.current_node = oldnode
1830 else
1831 abort
1832 end
1833 return null
1834 end
1835
1836 # Generate type checks in the C code to check covariant parameters
1837 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1838 do
1839 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1840
1841 for i in [0..msignature.arity[ do
1842 # skip test for vararg since the array is instantiated with the correct polymorphic type
1843 if msignature.vararg_rank == i then continue
1844
1845 # skip if the cast is not required
1846 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1847 if not origmtype.need_anchor then continue
1848
1849 # get the parameter type
1850 var mtype = self.msignature.mparameters[i].mtype
1851
1852 # generate the cast
1853 # note that v decides if and how to implements the cast
1854 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1855 v.add_cast(arguments[i+1], mtype, "covariance")
1856 end
1857 end
1858 end
1859
1860 # Node visit
1861
1862 redef class APropdef
1863 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1864 do
1865 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1866 debug("Not yet implemented")
1867 end
1868
1869 fun can_inline: Bool do return true
1870 end
1871
1872 redef class AMethPropdef
1873 redef fun compile_to_c(v, mpropdef, arguments)
1874 do
1875 if mpropdef.is_abstract then
1876 var cn = v.class_name_string(arguments.first)
1877 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1878 v.add_raw_abort
1879 return
1880 end
1881
1882 # Call the implicit super-init
1883 var auto_super_inits = self.auto_super_inits
1884 if auto_super_inits != null then
1885 var args = [arguments.first]
1886 for auto_super_init in auto_super_inits do
1887 assert auto_super_init.mproperty != mpropdef.mproperty
1888 args.clear
1889 for i in [0..auto_super_init.msignature.arity+1[ do
1890 args.add(arguments[i])
1891 end
1892 assert auto_super_init.mproperty != mpropdef.mproperty
1893 v.compile_callsite(auto_super_init, args)
1894 end
1895 end
1896 if auto_super_call then
1897 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1898 end
1899
1900 # Try special compilation
1901 if mpropdef.is_intern then
1902 if compile_intern_to_c(v, mpropdef, arguments) then return
1903 else if mpropdef.is_extern then
1904 if mpropdef.mproperty.is_init then
1905 if compile_externinit_to_c(v, mpropdef, arguments) then return
1906 else
1907 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1908 end
1909 end
1910
1911 # Compile block if any
1912 var n_block = n_block
1913 if n_block != null then
1914 for i in [0..mpropdef.msignature.arity[ do
1915 var variable = self.n_signature.n_params[i].variable.as(not null)
1916 v.assign(v.variable(variable), arguments[i+1])
1917 end
1918 v.stmt(n_block)
1919 return
1920 end
1921
1922 # We have a problem
1923 var cn = v.class_name_string(arguments.first)
1924 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1925 v.add_raw_abort
1926 end
1927
1928 redef fun can_inline
1929 do
1930 if self.auto_super_inits != null then return false
1931 var nblock = self.n_block
1932 if nblock == null then return true
1933 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1934 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1935 return false
1936 end
1937
1938 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1939 do
1940 var pname = mpropdef.mproperty.name
1941 var cname = mpropdef.mclassdef.mclass.name
1942 var ret = mpropdef.msignature.return_mtype
1943 if ret != null then
1944 ret = v.resolve_for(ret, arguments.first)
1945 else if mpropdef.mproperty.is_new then
1946 ret = arguments.first.mcasttype
1947 end
1948 if pname != "==" and pname != "!=" then
1949 v.adapt_signature(mpropdef, arguments)
1950 v.unbox_signature_extern(mpropdef, arguments)
1951 end
1952 if cname == "Int" then
1953 if pname == "output" then
1954 v.add("printf(\"%ld\\n\", {arguments.first});")
1955 return true
1956 else if pname == "object_id" then
1957 v.ret(arguments.first)
1958 return true
1959 else if pname == "+" then
1960 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1961 return true
1962 else if pname == "-" then
1963 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1964 return true
1965 else if pname == "unary -" then
1966 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1967 return true
1968 else if pname == "*" then
1969 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1970 return true
1971 else if pname == "/" then
1972 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1973 return true
1974 else if pname == "%" then
1975 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1976 return true
1977 else if pname == "lshift" then
1978 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1979 return true
1980 else if pname == "rshift" then
1981 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1982 return true
1983 else if pname == "==" then
1984 v.ret(v.equal_test(arguments[0], arguments[1]))
1985 return true
1986 else if pname == "!=" then
1987 var res = v.equal_test(arguments[0], arguments[1])
1988 v.ret(v.new_expr("!{res}", ret.as(not null)))
1989 return true
1990 else if pname == "<" then
1991 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1992 return true
1993 else if pname == ">" then
1994 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1995 return true
1996 else if pname == "<=" then
1997 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1998 return true
1999 else if pname == ">=" then
2000 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2001 return true
2002 else if pname == "to_f" then
2003 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2004 return true
2005 else if pname == "ascii" then
2006 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2007 return true
2008 end
2009 else if cname == "Char" then
2010 if pname == "output" then
2011 v.add("printf(\"%c\", {arguments.first});")
2012 return true
2013 else if pname == "object_id" then
2014 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2015 return true
2016 else if pname == "successor" then
2017 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2018 return true
2019 else if pname == "predecessor" then
2020 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2021 return true
2022 else if pname == "==" then
2023 v.ret(v.equal_test(arguments[0], arguments[1]))
2024 return true
2025 else if pname == "!=" then
2026 var res = v.equal_test(arguments[0], arguments[1])
2027 v.ret(v.new_expr("!{res}", ret.as(not null)))
2028 return true
2029 else if pname == "<" then
2030 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2031 return true
2032 else if pname == ">" then
2033 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2034 return true
2035 else if pname == "<=" then
2036 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2037 return true
2038 else if pname == ">=" then
2039 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2040 return true
2041 else if pname == "to_i" then
2042 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2043 return true
2044 else if pname == "ascii" then
2045 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2046 return true
2047 end
2048 else if cname == "Bool" then
2049 if pname == "output" then
2050 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2051 return true
2052 else if pname == "object_id" then
2053 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2054 return true
2055 else if pname == "==" then
2056 v.ret(v.equal_test(arguments[0], arguments[1]))
2057 return true
2058 else if pname == "!=" then
2059 var res = v.equal_test(arguments[0], arguments[1])
2060 v.ret(v.new_expr("!{res}", ret.as(not null)))
2061 return true
2062 end
2063 else if cname == "Float" then
2064 if pname == "output" then
2065 v.add("printf(\"%f\\n\", {arguments.first});")
2066 return true
2067 else if pname == "object_id" then
2068 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2069 return true
2070 else if pname == "+" then
2071 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2072 return true
2073 else if pname == "-" then
2074 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2075 return true
2076 else if pname == "unary -" then
2077 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2078 return true
2079 else if pname == "succ" then
2080 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2081 return true
2082 else if pname == "prec" then
2083 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2084 return true
2085 else if pname == "*" then
2086 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2087 return true
2088 else if pname == "/" then
2089 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2090 return true
2091 else if pname == "==" then
2092 v.ret(v.equal_test(arguments[0], arguments[1]))
2093 return true
2094 else if pname == "!=" then
2095 var res = v.equal_test(arguments[0], arguments[1])
2096 v.ret(v.new_expr("!{res}", ret.as(not null)))
2097 return true
2098 else if pname == "<" then
2099 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2100 return true
2101 else if pname == ">" then
2102 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2103 return true
2104 else if pname == "<=" then
2105 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2106 return true
2107 else if pname == ">=" then
2108 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2109 return true
2110 else if pname == "to_i" then
2111 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2112 return true
2113 end
2114 else if cname == "NativeString" then
2115 if pname == "[]" then
2116 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2117 return true
2118 else if pname == "[]=" then
2119 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2120 return true
2121 else if pname == "copy_to" then
2122 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2123 return true
2124 else if pname == "atoi" then
2125 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2126 return true
2127 else if pname == "init" then
2128 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2129 return true
2130 end
2131 else if cname == "NativeArray" then
2132 v.native_array_def(pname, ret, arguments)
2133 return true
2134 end
2135 if pname == "exit" then
2136 v.add("exit({arguments[1]});")
2137 return true
2138 else if pname == "sys" then
2139 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2140 return true
2141 else if pname == "calloc_string" then
2142 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2143 return true
2144 else if pname == "calloc_array" then
2145 v.calloc_array(ret.as(not null), arguments)
2146 return true
2147 else if pname == "object_id" then
2148 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2149 return true
2150 else if pname == "is_same_type" then
2151 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2152 return true
2153 else if pname == "is_same_instance" then
2154 v.ret(v.equal_test(arguments[0], arguments[1]))
2155 return true
2156 else if pname == "output_class_name" then
2157 var nat = v.class_name_string(arguments.first)
2158 v.add("printf(\"%s\\n\", {nat});")
2159 return true
2160 else if pname == "native_class_name" then
2161 var nat = v.class_name_string(arguments.first)
2162 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2163 return true
2164 else if pname == "force_garbage_collection" then
2165 v.add("nit_gcollect();")
2166 return true
2167 else if pname == "native_argc" then
2168 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2169 return true
2170 else if pname == "native_argv" then
2171 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2172 return true
2173 end
2174 return false
2175 end
2176
2177 # Compile an extern method
2178 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2179 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2180 do
2181 var externname
2182 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2183 if at != null then
2184 externname = at.arg_as_string(v.compiler.modelbuilder)
2185 if externname == null then return false
2186 else
2187 return false
2188 end
2189 if location.file != null then
2190 var file = location.file.filename
2191 v.add_extern(file)
2192 end
2193 var res: nullable RuntimeVariable = null
2194 var ret = mpropdef.msignature.return_mtype
2195 if ret != null then
2196 ret = v.resolve_for(ret, arguments.first)
2197 res = v.new_var_extern(ret)
2198 end
2199 v.adapt_signature(mpropdef, arguments)
2200 v.unbox_signature_extern(mpropdef, arguments)
2201
2202 if res == null then
2203 v.add("{externname}({arguments.join(", ")});")
2204 else
2205 v.add("{res} = {externname}({arguments.join(", ")});")
2206 res = v.box_extern(res, ret.as(not null))
2207 v.ret(res)
2208 end
2209 return true
2210 end
2211
2212 # Compile an extern factory
2213 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2214 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2215 do
2216 var externname
2217 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2218 if at != null then
2219 externname = at.arg_as_string(v.compiler.modelbuilder)
2220 if externname == null then return false
2221 else
2222 return false
2223 end
2224 if location.file != null then
2225 var file = location.file.filename
2226 v.add_extern(file)
2227 end
2228 v.adapt_signature(mpropdef, arguments)
2229 v.unbox_signature_extern(mpropdef, arguments)
2230 var ret = arguments.first.mtype
2231 var res = v.new_var_extern(ret)
2232
2233 arguments.shift
2234
2235 v.add("{res} = {externname}({arguments.join(", ")});")
2236 res = v.box_extern(res, ret)
2237 v.ret(res)
2238 return true
2239 end
2240 end
2241
2242 redef class AAttrPropdef
2243 redef fun compile_to_c(v, mpropdef, arguments)
2244 do
2245 if mpropdef == mreadpropdef then
2246 assert arguments.length == 1
2247 var res
2248 if is_lazy then
2249 var nexpr = n_expr
2250 assert nexpr != null
2251 var set
2252 var ret = self.mpropdef.static_mtype
2253 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2254 var guard = self.mlazypropdef.mproperty
2255 if useiset then
2256 set = v.isset_attribute(self.mpropdef.mproperty, arguments.first)
2257 else
2258 set = v.read_attribute(guard, arguments.first)
2259 end
2260 v.add("if(likely({set})) \{")
2261 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2262 v.add("\} else \{")
2263 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2264 v.write_attribute(self.mpropdef.mproperty, arguments.first, value)
2265 v.assign(res, value)
2266 if not useiset then
2267 var true_v = v.new_expr("1", v.bool_type)
2268 v.write_attribute(guard, arguments.first, true_v)
2269 end
2270 v.add("\}")
2271 else
2272 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2273 end
2274 v.assign(v.frame.returnvar.as(not null), res)
2275 else if mpropdef == mwritepropdef then
2276 assert arguments.length == 2
2277 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2278 if is_lazy then
2279 var ret = self.mpropdef.static_mtype
2280 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2281 if not useiset then
2282 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2283 end
2284 end
2285 else
2286 abort
2287 end
2288 end
2289
2290 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2291 do
2292 var nexpr = self.n_expr
2293 if nexpr != null and not is_lazy then
2294 var oldnode = v.current_node
2295 v.current_node = self
2296 var old_frame = v.frame
2297 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2298 v.frame = frame
2299 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2300 v.write_attribute(self.mpropdef.mproperty, recv, value)
2301 v.frame = old_frame
2302 v.current_node = oldnode
2303 end
2304 end
2305
2306 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2307 do
2308 var nexpr = self.n_expr
2309 if nexpr != null then return
2310
2311 var oldnode = v.current_node
2312 v.current_node = self
2313 var old_frame = v.frame
2314 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2315 v.frame = frame
2316 # Force read to check the initialization
2317 v.read_attribute(self.mpropdef.mproperty, recv)
2318 v.frame = old_frame
2319 v.current_node = oldnode
2320 end
2321 end
2322
2323 redef class AClassdef
2324 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2325 do
2326 if mpropdef == self.mfree_init then
2327 assert mpropdef.mproperty.is_root_init
2328 assert arguments.length == 1
2329 if not mpropdef.is_intro then
2330 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2331 end
2332 return
2333 else
2334 abort
2335 end
2336 end
2337 end
2338
2339 redef class AExpr
2340 # Try to compile self as an expression
2341 # Do not call this method directly, use `v.expr` instead
2342 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2343 do
2344 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2345 var mtype = self.mtype
2346 if mtype == null then
2347 return null
2348 else
2349 var res = v.new_var(mtype)
2350 v.add("/* {res} = NOT YET {class_name} */")
2351 return res
2352 end
2353 end
2354
2355 # Try to compile self as a statement
2356 # Do not call this method directly, use `v.stmt` instead
2357 private fun stmt(v: AbstractCompilerVisitor)
2358 do
2359 var res = expr(v)
2360 if res != null then v.add("{res};")
2361 end
2362 end
2363
2364 redef class ABlockExpr
2365 redef fun stmt(v)
2366 do
2367 for e in self.n_expr do v.stmt(e)
2368 end
2369 redef fun expr(v)
2370 do
2371 var last = self.n_expr.last
2372 for e in self.n_expr do
2373 if e == last then break
2374 v.stmt(e)
2375 end
2376 return v.expr(last, null)
2377 end
2378 end
2379
2380 redef class AVardeclExpr
2381 redef fun stmt(v)
2382 do
2383 var variable = self.variable.as(not null)
2384 var ne = self.n_expr
2385 if ne != null then
2386 var i = v.expr(ne, variable.declared_type)
2387 v.assign(v.variable(variable), i)
2388 end
2389 end
2390 end
2391
2392 redef class AVarExpr
2393 redef fun expr(v)
2394 do
2395 var res = v.variable(self.variable.as(not null))
2396 var mtype = self.mtype.as(not null)
2397 return v.autoadapt(res, mtype)
2398 end
2399 end
2400
2401 redef class AVarAssignExpr
2402 redef fun stmt(v)
2403 do
2404 var variable = self.variable.as(not null)
2405 var i = v.expr(self.n_value, variable.declared_type)
2406 v.assign(v.variable(variable), i)
2407 end
2408 redef fun expr(v)
2409 do
2410 var variable = self.variable.as(not null)
2411 var i = v.expr(self.n_value, variable.declared_type)
2412 v.assign(v.variable(variable), i)
2413 return i
2414 end
2415 end
2416
2417 redef class AVarReassignExpr
2418 redef fun stmt(v)
2419 do
2420 var variable = self.variable.as(not null)
2421 var vari = v.variable(variable)
2422 var value = v.expr(self.n_value, variable.declared_type)
2423 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2424 assert res != null
2425 v.assign(v.variable(variable), res)
2426 end
2427 end
2428
2429 redef class ASelfExpr
2430 redef fun expr(v) do return v.frame.arguments.first
2431 end
2432
2433 redef class AContinueExpr
2434 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2435 end
2436
2437 redef class ABreakExpr
2438 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2439 end
2440
2441 redef class AReturnExpr
2442 redef fun stmt(v)
2443 do
2444 var nexpr = self.n_expr
2445 if nexpr != null then
2446 var returnvar = v.frame.returnvar.as(not null)
2447 var i = v.expr(nexpr, returnvar.mtype)
2448 v.assign(returnvar, i)
2449 end
2450 v.add("goto {v.frame.returnlabel.as(not null)};")
2451 end
2452 end
2453
2454 redef class AAbortExpr
2455 redef fun stmt(v) do v.add_abort("Aborted")
2456 end
2457
2458 redef class AIfExpr
2459 redef fun stmt(v)
2460 do
2461 var cond = v.expr_bool(self.n_expr)
2462 v.add("if ({cond})\{")
2463 v.stmt(self.n_then)
2464 v.add("\} else \{")
2465 v.stmt(self.n_else)
2466 v.add("\}")
2467 end
2468
2469 redef fun expr(v)
2470 do
2471 var res = v.new_var(self.mtype.as(not null))
2472 var cond = v.expr_bool(self.n_expr)
2473 v.add("if ({cond})\{")
2474 v.assign(res, v.expr(self.n_then.as(not null), null))
2475 v.add("\} else \{")
2476 v.assign(res, v.expr(self.n_else.as(not null), null))
2477 v.add("\}")
2478 return res
2479 end
2480 end
2481
2482 redef class AIfexprExpr
2483 redef fun expr(v)
2484 do
2485 var res = v.new_var(self.mtype.as(not null))
2486 var cond = v.expr_bool(self.n_expr)
2487 v.add("if ({cond})\{")
2488 v.assign(res, v.expr(self.n_then, null))
2489 v.add("\} else \{")
2490 v.assign(res, v.expr(self.n_else, null))
2491 v.add("\}")
2492 return res
2493 end
2494 end
2495
2496 redef class ADoExpr
2497 redef fun stmt(v)
2498 do
2499 v.stmt(self.n_block)
2500 var escapemark = self.escapemark
2501 if escapemark != null then
2502 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2503 end
2504 end
2505 end
2506
2507 redef class AWhileExpr
2508 redef fun stmt(v)
2509 do
2510 v.add("for(;;) \{")
2511 var cond = v.expr_bool(self.n_expr)
2512 v.add("if (!{cond}) break;")
2513 v.stmt(self.n_block)
2514 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2515 v.add("\}")
2516 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2517 end
2518 end
2519
2520 redef class ALoopExpr
2521 redef fun stmt(v)
2522 do
2523 v.add("for(;;) \{")
2524 v.stmt(self.n_block)
2525 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2526 v.add("\}")
2527 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2528 end
2529 end
2530
2531 redef class AForExpr
2532 redef fun stmt(v)
2533 do
2534 # Shortcut on explicit range
2535 # Avoid the instantiation of the range and the iterator
2536 var nexpr = self.n_expr
2537 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2538 var from = v.expr(nexpr.n_expr, null)
2539 var to = v.expr(nexpr.n_expr2, null)
2540 var variable = v.variable(variables.first)
2541 var one = v.new_expr("1", v.get_class("Int").mclass_type)
2542
2543 v.assign(variable, from)
2544 v.add("for(;;) \{ /* shortcut range */")
2545
2546 var ok
2547 if nexpr isa AOrangeExpr then
2548 ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2549 else
2550 ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
2551 end
2552 assert ok != null
2553 v.add("if(!{ok}) break;")
2554
2555 v.stmt(self.n_block)
2556
2557 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2558 var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
2559 assert succ != null
2560 v.assign(variable, succ)
2561 v.add("\}")
2562 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2563 return
2564 end
2565
2566 var cl = v.expr(self.n_expr, null)
2567 var it_meth = self.method_iterator
2568 assert it_meth != null
2569 var it = v.compile_callsite(it_meth, [cl])
2570 assert it != null
2571 v.add("for(;;) \{")
2572 var isok_meth = self.method_is_ok
2573 assert isok_meth != null
2574 var ok = v.compile_callsite(isok_meth, [it])
2575 assert ok != null
2576 v.add("if(!{ok}) break;")
2577 if self.variables.length == 1 then
2578 var item_meth = self.method_item
2579 assert item_meth != null
2580 var i = v.compile_callsite(item_meth, [it])
2581 assert i != null
2582 v.assign(v.variable(variables.first), i)
2583 else if self.variables.length == 2 then
2584 var key_meth = self.method_key
2585 assert key_meth != null
2586 var i = v.compile_callsite(key_meth, [it])
2587 assert i != null
2588 v.assign(v.variable(variables[0]), i)
2589 var item_meth = self.method_item
2590 assert item_meth != null
2591 i = v.compile_callsite(item_meth, [it])
2592 assert i != null
2593 v.assign(v.variable(variables[1]), i)
2594 else
2595 abort
2596 end
2597 v.stmt(self.n_block)
2598 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2599 var next_meth = self.method_next
2600 assert next_meth != null
2601 v.compile_callsite(next_meth, [it])
2602 v.add("\}")
2603 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2604 end
2605 end
2606
2607 redef class AAssertExpr
2608 redef fun stmt(v)
2609 do
2610 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2611
2612 var cond = v.expr_bool(self.n_expr)
2613 v.add("if (unlikely(!{cond})) \{")
2614 v.stmt(self.n_else)
2615 var nid = self.n_id
2616 if nid != null then
2617 v.add_abort("Assert '{nid.text}' failed")
2618 else
2619 v.add_abort("Assert failed")
2620 end
2621 v.add("\}")
2622 end
2623 end
2624
2625 redef class AOrExpr
2626 redef fun expr(v)
2627 do
2628 var res = v.new_var(self.mtype.as(not null))
2629 var i1 = v.expr_bool(self.n_expr)
2630 v.add("if ({i1}) \{")
2631 v.add("{res} = 1;")
2632 v.add("\} else \{")
2633 var i2 = v.expr_bool(self.n_expr2)
2634 v.add("{res} = {i2};")
2635 v.add("\}")
2636 return res
2637 end
2638 end
2639
2640 redef class AImpliesExpr
2641 redef fun expr(v)
2642 do
2643 var res = v.new_var(self.mtype.as(not null))
2644 var i1 = v.expr_bool(self.n_expr)
2645 v.add("if (!{i1}) \{")
2646 v.add("{res} = 1;")
2647 v.add("\} else \{")
2648 var i2 = v.expr_bool(self.n_expr2)
2649 v.add("{res} = {i2};")
2650 v.add("\}")
2651 return res
2652 end
2653 end
2654
2655 redef class AAndExpr
2656 redef fun expr(v)
2657 do
2658 var res = v.new_var(self.mtype.as(not null))
2659 var i1 = v.expr_bool(self.n_expr)
2660 v.add("if (!{i1}) \{")
2661 v.add("{res} = 0;")
2662 v.add("\} else \{")
2663 var i2 = v.expr_bool(self.n_expr2)
2664 v.add("{res} = {i2};")
2665 v.add("\}")
2666 return res
2667 end
2668 end
2669
2670 redef class ANotExpr
2671 redef fun expr(v)
2672 do
2673 var cond = v.expr_bool(self.n_expr)
2674 return v.new_expr("!{cond}", self.mtype.as(not null))
2675 end
2676 end
2677
2678 redef class AOrElseExpr
2679 redef fun expr(v)
2680 do
2681 var res = v.new_var(self.mtype.as(not null))
2682 var i1 = v.expr(self.n_expr, null)
2683 v.add("if ({i1}!=NULL) \{")
2684 v.assign(res, i1)
2685 v.add("\} else \{")
2686 var i2 = v.expr(self.n_expr2, null)
2687 v.assign(res, i2)
2688 v.add("\}")
2689 return res
2690 end
2691 end
2692
2693 redef class AIntExpr
2694 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2695 end
2696
2697 redef class AFloatExpr
2698 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2699 end
2700
2701 redef class ACharExpr
2702 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2703 end
2704
2705 redef class AArrayExpr
2706 redef fun expr(v)
2707 do
2708 var mtype = self.mtype.as(MClassType).arguments.first
2709 var array = new Array[RuntimeVariable]
2710 for nexpr in self.n_exprs.n_exprs do
2711 var i = v.expr(nexpr, mtype)
2712 array.add(i)
2713 end
2714 return v.array_instance(array, mtype)
2715 end
2716 end
2717
2718 redef class AStringFormExpr
2719 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2720 end
2721
2722 redef class ASuperstringExpr
2723 redef fun expr(v)
2724 do
2725 var array = new Array[RuntimeVariable]
2726 for ne in self.n_exprs do
2727 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2728 var i = v.expr(ne, null)
2729 array.add(i)
2730 end
2731 var a = v.array_instance(array, v.object_type)
2732 var res = v.send(v.get_property("to_s", a.mtype), [a])
2733 return res
2734 end
2735 end
2736
2737 redef class ACrangeExpr
2738 redef fun expr(v)
2739 do
2740 var i1 = v.expr(self.n_expr, null)
2741 var i2 = v.expr(self.n_expr2, null)
2742 var mtype = self.mtype.as(MClassType)
2743 var res = v.init_instance(mtype)
2744 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2745 return res
2746 end
2747 end
2748
2749 redef class AOrangeExpr
2750 redef fun expr(v)
2751 do
2752 var i1 = v.expr(self.n_expr, null)
2753 var i2 = v.expr(self.n_expr2, null)
2754 var mtype = self.mtype.as(MClassType)
2755 var res = v.init_instance(mtype)
2756 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2757 return res
2758 end
2759 end
2760
2761 redef class ATrueExpr
2762 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2763 end
2764
2765 redef class AFalseExpr
2766 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2767 end
2768
2769 redef class ANullExpr
2770 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2771 end
2772
2773 redef class AIsaExpr
2774 redef fun expr(v)
2775 do
2776 var i = v.expr(self.n_expr, null)
2777 return v.type_test(i, self.cast_type.as(not null), "isa")
2778 end
2779 end
2780
2781 redef class AAsCastExpr
2782 redef fun expr(v)
2783 do
2784 var i = v.expr(self.n_expr, null)
2785 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2786
2787 v.add_cast(i, self.mtype.as(not null), "as")
2788 return i
2789 end
2790 end
2791
2792 redef class AAsNotnullExpr
2793 redef fun expr(v)
2794 do
2795 var i = v.expr(self.n_expr, null)
2796 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2797
2798 if i.mtype.ctype != "val*" then return i
2799
2800 v.add("if (unlikely({i} == NULL)) \{")
2801 v.add_abort("Cast failed")
2802 v.add("\}")
2803 return i
2804 end
2805 end
2806
2807 redef class AParExpr
2808 redef fun expr(v) do return v.expr(self.n_expr, null)
2809 end
2810
2811 redef class AOnceExpr
2812 redef fun expr(v)
2813 do
2814 var mtype = self.mtype.as(not null)
2815 var name = v.get_name("varonce")
2816 var guard = v.get_name(name + "_guard")
2817 v.add_decl("static {mtype.ctype} {name};")
2818 v.add_decl("static int {guard};")
2819 var res = v.new_var(mtype)
2820 v.add("if ({guard}) \{")
2821 v.add("{res} = {name};")
2822 v.add("\} else \{")
2823 var i = v.expr(self.n_expr, mtype)
2824 v.add("{res} = {i};")
2825 v.add("{name} = {res};")
2826 v.add("{guard} = 1;")
2827 v.add("\}")
2828 return res
2829 end
2830 end
2831
2832 redef class ASendExpr
2833 redef fun expr(v)
2834 do
2835 var recv = v.expr(self.n_expr, null)
2836 var args = [recv]
2837 for a in self.raw_arguments do
2838 args.add(v.expr(a, null))
2839 end
2840 return v.compile_callsite(self.callsite.as(not null), args)
2841 end
2842 end
2843
2844 redef class ASendReassignFormExpr
2845 redef fun stmt(v)
2846 do
2847 var recv = v.expr(self.n_expr, null)
2848 var args = [recv]
2849 for a in self.raw_arguments do
2850 args.add(v.expr(a, null))
2851 end
2852 var value = v.expr(self.n_value, null)
2853
2854 var left = v.compile_callsite(self.callsite.as(not null), args)
2855 assert left != null
2856
2857 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2858 assert res != null
2859
2860 args.add(res)
2861 v.compile_callsite(self.write_callsite.as(not null), args)
2862 end
2863 end
2864
2865 redef class ASuperExpr
2866 redef fun expr(v)
2867 do
2868 var recv = v.frame.arguments.first
2869 var args = [recv]
2870 for a in self.n_args.n_exprs do
2871 args.add(v.expr(a, null))
2872 end
2873
2874 var callsite = self.callsite
2875 if callsite != null then
2876 # Add additionnals arguments for the super init call
2877 if args.length == 1 then
2878 for i in [0..callsite.msignature.arity[ do
2879 args.add(v.frame.arguments[i+1])
2880 end
2881 end
2882 # Super init call
2883 var res = v.compile_callsite(callsite, args)
2884 return res
2885 end
2886
2887 if args.length == 1 then
2888 args = v.frame.arguments
2889 end
2890
2891 # stantard call-next-method
2892 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2893 end
2894 end
2895
2896 redef class ANewExpr
2897 redef fun expr(v)
2898 do
2899 var mtype = self.mtype.as(MClassType)
2900 var recv
2901 var ctype = mtype.ctype
2902 if mtype.mclass.name == "NativeArray" then
2903 assert self.n_args.n_exprs.length == 1
2904 var l = v.expr(self.n_args.n_exprs.first, null)
2905 assert mtype isa MGenericType
2906 var elttype = mtype.arguments.first
2907 return v.native_array_instance(elttype, l)
2908 else if ctype == "val*" then
2909 recv = v.init_instance(mtype)
2910 else if ctype == "char*" then
2911 recv = v.new_expr("NULL/*special!*/", mtype)
2912 else
2913 recv = v.new_expr("({ctype})0/*special!*/", mtype)
2914 end
2915 var args = [recv]
2916 for a in self.n_args.n_exprs do
2917 args.add(v.expr(a, null))
2918 end
2919 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2920 if res2 != null then
2921 #self.debug("got {res2} from {mproperty}. drop {recv}")
2922 return res2
2923 end
2924 return recv
2925 end
2926 end
2927
2928 redef class AAttrExpr
2929 redef fun expr(v)
2930 do
2931 var recv = v.expr(self.n_expr, null)
2932 var mproperty = self.mproperty.as(not null)
2933 return v.read_attribute(mproperty, recv)
2934 end
2935 end
2936
2937 redef class AAttrAssignExpr
2938 redef fun stmt(v)
2939 do
2940 var recv = v.expr(self.n_expr, null)
2941 var i = v.expr(self.n_value, null)
2942 var mproperty = self.mproperty.as(not null)
2943 v.write_attribute(mproperty, recv, i)
2944 end
2945 end
2946
2947 redef class AAttrReassignExpr
2948 redef fun stmt(v)
2949 do
2950 var recv = v.expr(self.n_expr, null)
2951 var value = v.expr(self.n_value, null)
2952 var mproperty = self.mproperty.as(not null)
2953 var attr = v.read_attribute(mproperty, recv)
2954 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2955 assert res != null
2956 v.write_attribute(mproperty, recv, res)
2957 end
2958 end
2959
2960 redef class AIssetAttrExpr
2961 redef fun expr(v)
2962 do
2963 var recv = v.expr(self.n_expr, null)
2964 var mproperty = self.mproperty.as(not null)
2965 return v.isset_attribute(mproperty, recv)
2966 end
2967 end
2968
2969 redef class ADebugTypeExpr
2970 redef fun stmt(v)
2971 do
2972 # do nothing
2973 end
2974 end
2975
2976 # Utils
2977
2978 redef class Array[E]
2979 # Return a new `Array` with the elements only contened in self and not in `o`
2980 fun -(o: Array[E]): Array[E] do
2981 var res = new Array[E]
2982 for e in self do if not o.has(e) then res.add(e)
2983 return res
2984 end
2985 end
2986
2987 redef class MModule
2988 # All `MProperty` associated to all `MClassDef` of `mclass`
2989 fun properties(mclass: MClass): Set[MProperty] do
2990 if not self.properties_cache.has_key(mclass) then
2991 var properties = new HashSet[MProperty]
2992 var parents = new Array[MClass]
2993 if self.flatten_mclass_hierarchy.has(mclass) then
2994 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2995 end
2996 for parent in parents do
2997 properties.add_all(self.properties(parent))
2998 end
2999 for mclassdef in mclass.mclassdefs do
3000 if not self.in_importation <= mclassdef.mmodule then continue
3001 for mprop in mclassdef.intro_mproperties do
3002 properties.add(mprop)
3003 end
3004 end
3005 self.properties_cache[mclass] = properties
3006 end
3007 return properties_cache[mclass]
3008 end
3009 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3010
3011 # Write FFI and nitni results to file
3012 fun finalize_ffi(c: AbstractCompiler) do end
3013
3014 # Give requided addinional system libraries (as given to LD_LIBS)
3015 # Note: can return null instead of an empty set
3016 fun collect_linker_libs: nullable Set[String] do return null
3017 end
3018
3019 # Create a tool context to handle options and paths
3020 var toolcontext = new ToolContext
3021
3022 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
3023 toolcontext.option_context.add_option(opt_mixins)
3024
3025 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
3026
3027 # We do not add other options, so process them now!
3028 toolcontext.process_options(args)
3029
3030 # We need a model to collect stufs
3031 var model = new Model
3032 # An a model builder to parse files
3033 var modelbuilder = new ModelBuilder(model, toolcontext)
3034
3035 var arguments = toolcontext.option_context.rest
3036 if arguments.length > 1 and toolcontext.opt_output.value != null then
3037 print "Error: --output needs a single source file. Do you prefer --dir?"
3038 exit 1
3039 end
3040
3041 # Here we load an process all modules passed on the command line
3042 var mmodules = modelbuilder.parse(arguments)
3043 var mixins = modelbuilder.parse(opt_mixins.value)
3044
3045 if mmodules.is_empty then return
3046 modelbuilder.run_phases
3047
3048 for mmodule in mmodules do
3049 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3050 var ms = [mmodule]
3051 if not mixins.is_empty then
3052 ms.add_all mixins
3053 end
3054 toolcontext.run_global_phases(ms)
3055 end