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