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