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