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