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