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