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