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