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