abstract_compiler: default stmt implmentation do not need to execute variables as...
[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 hfile: nullable OFStream = null
257 var count = 0
258 var cfilename = "{f.name}.0.h"
259 var cfilepath = "{compile_dir}/{cfilename}"
260 hfile = new OFStream.open(cfilepath)
261 hfile.write "#include \"{hfilename}\"\n"
262 for key in f.required_declarations do
263 if not compiler.provided_declarations.has_key(key) then
264 var node = compiler.requirers_of_declarations.get_or_null(key)
265 if node != null then
266 node.debug "No provided declaration for {key}"
267 else
268 print "No provided declaration for {key}"
269 end
270 abort
271 end
272 hfile.write compiler.provided_declarations[key]
273 hfile.write "\n"
274 end
275 hfile.close
276 var file: nullable OFStream = null
277 for vis in f.writers do
278 if vis == compiler.header then continue
279 var total_lines = vis.lines.length + vis.decl_lines.length
280 if total_lines == 0 then continue
281 count += total_lines
282 if file == null or count > 10000 then
283 i += 1
284 if file != null then file.close
285 cfilename = "{f.name}.{i}.c"
286 cfilepath = "{compile_dir}/{cfilename}"
287 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
288 cfiles.add(cfilename)
289 file = new OFStream.open(cfilepath)
290 file.write "#include \"{f.name}.0.h\"\n"
291 count = total_lines
292 end
293 for l in vis.decl_lines do
294 file.write l
295 file.write "\n"
296 end
297 for l in vis.lines do
298 file.write l
299 file.write "\n"
300 end
301 end
302 if file != null then file.close
303 end
304
305 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
306 end
307
308 fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
309
310 fun default_outname(mainmodule: MModule): String
311 do
312 # Search a non fictive module
313 var res = mainmodule.name
314 while mainmodule.is_fictive do
315 mainmodule = mainmodule.in_importation.direct_greaters.first
316 res = mainmodule.name
317 end
318 return res
319 end
320
321 # Combine options and platform informations to get the final path of the outfile
322 fun outfile(mainmodule: MModule): String
323 do
324 var res = self.toolcontext.opt_output.value
325 if res != null then return res
326 res = default_outname(mainmodule)
327 var dir = self.toolcontext.opt_dir.value
328 if dir != null then return dir.join_path(res)
329 return res
330 end
331
332 fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
333 do
334 var mainmodule = compiler.mainmodule
335 var platform = compiler.mainmodule.target_platform
336
337 var outname = outfile(mainmodule)
338
339 var orig_dir = compile_dir.relpath(".")
340 var outpath = orig_dir.join_path(outname).simplify_path
341 var makename = makefile_name(mainmodule)
342 var makepath = "{compile_dir}/{makename}"
343 var makefile = new OFStream.open(makepath)
344
345 var cc_includes = ""
346 for p in cc_paths do
347 cc_includes += " -I \"" + p + "\""
348 end
349
350 var linker_options = new HashSet[String]
351 for m in mainmodule.in_importation.greaters do
352 var libs = m.collect_linker_libs
353 if libs != null then linker_options.add_all(libs)
354 end
355
356 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")
357
358 var ost = toolcontext.opt_stacktrace.value
359 if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
360
361 # Dynamic adaptations
362 # While `platform` enable complex toolchains, they are statically applied
363 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
364
365 # Check and adapt the targeted system
366 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
367 makefile.write("ifeq ($(uname_S),Darwin)\n")
368 # remove -lunwind since it is already included on macosx
369 makefile.write("\tNEED_LIBUNWIND :=\n")
370 makefile.write("endif\n\n")
371
372 # Check and adapt for the compiler used
373 # clang need an additionnal `-Qunused-arguments`
374 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")
375
376 makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
377
378 makefile.write("all: {outpath}\n\n")
379
380 var ofiles = new Array[String]
381 var dep_rules = new Array[String]
382 # Compile each generated file
383 for f in cfiles do
384 var o = f.strip_extension(".c") + ".o"
385 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
386 ofiles.add(o)
387 dep_rules.add(o)
388 end
389
390 var java_files = new Array[ExternFile]
391
392 # Compile each required extern body into a specific .o
393 for f in compiler.extern_bodies do
394 var o = f.makefile_rule_name
395 var ff = f.filename.basename("")
396 makefile.write("{o}: {ff}\n")
397 makefile.write("\t{f.makefile_rule_content}\n\n")
398 dep_rules.add(f.makefile_rule_name)
399
400 if f.compiles_to_o_file then ofiles.add(o)
401 if f.add_to_jar then java_files.add(f)
402 end
403
404 if not java_files.is_empty then
405 var jar_file = "{outpath}.jar"
406
407 var class_files_array = new Array[String]
408 for f in java_files do class_files_array.add(f.makefile_rule_name)
409 var class_files = class_files_array.join(" ")
410
411 makefile.write("{jar_file}: {class_files}\n")
412 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
413 dep_rules.add jar_file
414 end
415
416 # Link edition
417 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n")
418 # Clean
419 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
420 makefile.close
421 self.toolcontext.info("Generated makefile: {makepath}", 2)
422 end
423
424 fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
425 do
426 var makename = makefile_name(compiler.mainmodule)
427
428 var makeflags = self.toolcontext.opt_make_flags.value
429 if makeflags == null then makeflags = ""
430 self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
431
432 var res
433 if self.toolcontext.verbose_level >= 3 then
434 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
435 else
436 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
437 end
438 if res != 0 then
439 toolcontext.error(null, "make failed! Error code: {res}.")
440 end
441 end
442 end
443
444 # Singleton that store the knowledge about the compilation process
445 abstract class AbstractCompiler
446 type VISITOR: AbstractCompilerVisitor
447
448 # Table corresponding c_names to nit names (methods)
449 var names = new HashMap[String, String]
450
451 # The main module of the program currently compiled
452 # Is assigned during the separate compilation
453 var mainmodule: MModule is writable
454
455 # The real main module of the program
456 var realmainmodule: MModule
457
458 # The modelbuilder used to know the model and the AST
459 var modelbuilder: ModelBuilder is protected writable
460
461 # Is hardening asked? (see --hardening)
462 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
463
464 init(mainmodule: MModule, modelbuilder: ModelBuilder)
465 do
466 self.mainmodule = mainmodule
467 self.realmainmodule = mainmodule
468 self.modelbuilder = modelbuilder
469 end
470
471 # Force the creation of a new file
472 # The point is to avoid contamination between must-be-compiled-separately files
473 fun new_file(name: String): CodeFile
474 do
475 var f = new CodeFile(name)
476 self.files.add(f)
477 return f
478 end
479
480 # The list of all associated files
481 # Used to generate .c files
482 var files = new List[CodeFile]
483
484 # Initialize a visitor specific for a compiler engine
485 fun new_visitor: VISITOR is abstract
486
487 # Where global declaration are stored (the main .h)
488 var header: CodeWriter is writable
489
490 # Provide a declaration that can be requested (before or latter) by a visitor
491 fun provide_declaration(key: String, s: String)
492 do
493 if self.provided_declarations.has_key(key) then
494 assert self.provided_declarations[key] == s
495 end
496 self.provided_declarations[key] = s
497 end
498
499 private var provided_declarations = new HashMap[String, String]
500
501 private var requirers_of_declarations = new HashMap[String, ANode]
502
503 # Builds the .c and .h files to be used when generating a Stack Trace
504 # Binds the generated C function names to Nit function names
505 fun build_c_to_nit_bindings
506 do
507 var compile_dir = modelbuilder.compile_dir
508
509 var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
510 stream.write("#include <string.h>\n")
511 stream.write("#include <stdlib.h>\n")
512 stream.write("#include \"c_functions_hash.h\"\n")
513 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
514 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
515 stream.write("char* procname = malloc(len+1);")
516 stream.write("memcpy(procname, procproc, len);")
517 stream.write("procname[len] = '\\0';")
518 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
519 for i in names.keys do
520 stream.write("\{\"")
521 stream.write(i)
522 stream.write("\",\"")
523 stream.write(names[i])
524 stream.write("\"\},\n")
525 end
526 stream.write("\};\n")
527 stream.write("int i;")
528 stream.write("for(i = 0; i < {names.length}; i++)\{")
529 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
530 stream.write("free(procname);")
531 stream.write("return map[i].nit_name;")
532 stream.write("\}")
533 stream.write("\}")
534 stream.write("free(procname);")
535 stream.write("return NULL;")
536 stream.write("\}\n")
537 stream.close
538
539 stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
540 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
541 stream.close
542
543 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
544 end
545
546 # Compile C headers
547 # This method call compile_header_strucs method that has to be refined
548 fun compile_header do
549 self.header.add_decl("#include <stdlib.h>")
550 self.header.add_decl("#include <stdio.h>")
551 self.header.add_decl("#include <string.h>")
552 self.header.add_decl("#include \"gc_chooser.h\"")
553 self.header.add_decl("#ifdef ANDROID")
554 self.header.add_decl(" #include <android/log.h>")
555 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
556 self.header.add_decl("#else")
557 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
558 self.header.add_decl("#endif")
559
560 compile_header_structs
561 compile_nitni_structs
562
563 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
564 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
565 # Signal handler function prototype
566 self.header.add_decl("void show_backtrace(int);")
567 else
568 self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
569 end
570
571 if gccd_disable.has("likely") or gccd_disable.has("all") then
572 self.header.add_decl("#define likely(x) (x)")
573 self.header.add_decl("#define unlikely(x) (x)")
574 else if gccd_disable.has("correct-likely") then
575 # invert the `likely` definition
576 # Used by masochists to bench the worst case
577 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
578 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
579 else
580 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
581 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
582 end
583
584 # Global variable used by intern methods
585 self.header.add_decl("extern int glob_argc;")
586 self.header.add_decl("extern char **glob_argv;")
587 self.header.add_decl("extern val *glob_sys;")
588 end
589
590 # Declaration of structures for live Nit types
591 protected fun compile_header_structs is abstract
592
593 # Declaration of structures for nitni undelying the FFI
594 protected fun compile_nitni_structs
595 do
596 self.header.add_decl """
597 /* Native reference to Nit objects */
598 /* This structure is used to represent every Nit type in extern methods and custom C code. */
599 struct nitni_ref {
600 struct nitni_ref *next,
601 *prev; /* adjacent global references in global list */
602 int count; /* number of time this global reference has been marked */
603 };
604
605 /* List of global references from C code to Nit objects */
606 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
607 struct nitni_global_ref_list_t {
608 struct nitni_ref *head, *tail;
609 };
610 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
611
612 /* Initializer of global reference list */
613 extern void nitni_global_ref_list_init();
614
615 /* Intern function to add a global reference to the list */
616 extern void nitni_global_ref_add( struct nitni_ref *ref );
617
618 /* Intern function to remove a global reference from the list */
619 extern void nitni_global_ref_remove( struct nitni_ref *ref );
620
621 /* Increase count on an existing global reference */
622 extern void nitni_global_ref_incr( struct nitni_ref *ref );
623
624 /* Decrease count on an existing global reference */
625 extern void nitni_global_ref_decr( struct nitni_ref *ref );
626 """
627 end
628
629 fun compile_finalizer_function
630 do
631 var finalizable_type = mainmodule.finalizable_type
632 if finalizable_type == null then return
633
634 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
635
636 if finalize_meth == null then
637 modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
638 return
639 end
640
641 var v = self.new_visitor
642 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
643 var recv = v.new_expr("obj", finalizable_type)
644 v.send(finalize_meth, [recv])
645 v.add "\}"
646 end
647
648 # Generate the main C function.
649 #
650 # This function:
651 #
652 # * allocate the Sys object if it exists
653 # * call init if is exists
654 # * call main if it exists
655 fun compile_main_function
656 do
657 var v = self.new_visitor
658 v.add_decl("#include <signal.h>")
659 var ost = modelbuilder.toolcontext.opt_stacktrace.value
660 var platform = mainmodule.target_platform
661
662 if platform != null and not platform.supports_libunwind then ost = "none"
663
664 var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
665
666 if ost == "nitstack" or ost == "libunwind" then
667 v.add_decl("#define UNW_LOCAL_ONLY")
668 v.add_decl("#include <libunwind.h>")
669 if ost == "nitstack" then
670 v.add_decl("#include \"c_functions_hash.h\"")
671 end
672 end
673 v.add_decl("int glob_argc;")
674 v.add_decl("char **glob_argv;")
675 v.add_decl("val *glob_sys;")
676
677 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
678 for tag in count_type_test_tags do
679 v.add_decl("long count_type_test_resolved_{tag};")
680 v.add_decl("long count_type_test_unresolved_{tag};")
681 v.add_decl("long count_type_test_skipped_{tag};")
682 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
683 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
684 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
685 end
686 end
687
688 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
689 v.add_decl("long count_invoke_by_tables;")
690 v.add_decl("long count_invoke_by_direct;")
691 v.add_decl("long count_invoke_by_inline;")
692 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
693 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
694 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
695 end
696
697 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
698 v.add_decl("long count_attr_reads = 0;")
699 v.add_decl("long count_isset_checks = 0;")
700 v.compiler.header.add_decl("extern long count_attr_reads;")
701 v.compiler.header.add_decl("extern long count_isset_checks;")
702 end
703
704 v.add_decl("void sig_handler(int signo)\{")
705 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
706 v.add_decl("show_backtrace(signo);")
707 v.add_decl("\}")
708
709 v.add_decl("void show_backtrace (int signo) \{")
710 if ost == "nitstack" or ost == "libunwind" then
711 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
712 v.add_decl("unw_cursor_t cursor;")
713 v.add_decl("if(opt==NULL)\{")
714 v.add_decl("unw_context_t uc;")
715 v.add_decl("unw_word_t ip;")
716 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
717 v.add_decl("unw_getcontext(&uc);")
718 v.add_decl("unw_init_local(&cursor, &uc);")
719 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
720 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
721 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
722 v.add_decl("while (unw_step(&cursor) > 0) \{")
723 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
724 if ost == "nitstack" then
725 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
726 v.add_decl(" if (recv != NULL)\{")
727 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
728 v.add_decl(" \}else\{")
729 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
730 v.add_decl(" \}")
731 else
732 v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
733 end
734 v.add_decl("\}")
735 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
736 v.add_decl("free(procname);")
737 v.add_decl("\}")
738 end
739 v.add_decl("exit(signo);")
740 v.add_decl("\}")
741
742 if no_main then
743 v.add_decl("int nit_main(int argc, char** argv) \{")
744 else
745 v.add_decl("int main(int argc, char** argv) \{")
746 end
747
748 v.add("signal(SIGABRT, sig_handler);")
749 v.add("signal(SIGFPE, sig_handler);")
750 v.add("signal(SIGILL, sig_handler);")
751 v.add("signal(SIGINT, sig_handler);")
752 v.add("signal(SIGTERM, sig_handler);")
753 v.add("signal(SIGSEGV, sig_handler);")
754 v.add("signal(SIGPIPE, sig_handler);")
755
756 v.add("glob_argc = argc; glob_argv = argv;")
757 v.add("initialize_gc_option();")
758
759 v.add "initialize_nitni_global_refs();"
760
761 var main_type = mainmodule.sys_type
762 if main_type != null then
763 var mainmodule = v.compiler.mainmodule
764 var glob_sys = v.init_instance(main_type)
765 v.add("glob_sys = {glob_sys};")
766 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
767 if main_init != null then
768 v.send(main_init, [glob_sys])
769 end
770 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
771 mainmodule.try_get_primitive_method("main", main_type.mclass)
772 if main_method != null then
773 v.send(main_method, [glob_sys])
774 end
775 end
776
777 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
778 v.add_decl("long count_type_test_resolved_total = 0;")
779 v.add_decl("long count_type_test_unresolved_total = 0;")
780 v.add_decl("long count_type_test_skipped_total = 0;")
781 v.add_decl("long count_type_test_total_total = 0;")
782 for tag in count_type_test_tags do
783 v.add_decl("long count_type_test_total_{tag};")
784 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
785 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
786 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
787 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
788 v.add("count_type_test_total_total += count_type_test_total_{tag};")
789 end
790 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
791 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
792 var tags = count_type_test_tags.to_a
793 tags.add("total")
794 for tag in tags do
795 v.add("printf(\"{tag}\");")
796 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
797 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
798 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
799 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
800 end
801 end
802
803 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
804 v.add_decl("long count_invoke_total;")
805 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
806 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
807 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
808 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
809 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
810 end
811
812 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
813 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
814 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
815 end
816
817 v.add("return 0;")
818 v.add("\}")
819 end
820
821 # Copile all C functions related to the [incr|decr]_ref features of the FFI
822 fun compile_nitni_global_ref_functions
823 do
824 var v = self.new_visitor
825 v.add """
826 struct nitni_global_ref_list_t *nitni_global_ref_list;
827 void initialize_nitni_global_refs() {
828 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
829 nitni_global_ref_list->head = NULL;
830 nitni_global_ref_list->tail = NULL;
831 }
832
833 void nitni_global_ref_add( struct nitni_ref *ref ) {
834 if ( nitni_global_ref_list->head == NULL ) {
835 nitni_global_ref_list->head = ref;
836 ref->prev = NULL;
837 } else {
838 nitni_global_ref_list->tail->next = ref;
839 ref->prev = nitni_global_ref_list->tail;
840 }
841 nitni_global_ref_list->tail = ref;
842
843 ref->next = NULL;
844 }
845
846 void nitni_global_ref_remove( struct nitni_ref *ref ) {
847 if ( ref->prev == NULL ) {
848 nitni_global_ref_list->head = ref->next;
849 } else {
850 ref->prev->next = ref->next;
851 }
852
853 if ( ref->next == NULL ) {
854 nitni_global_ref_list->tail = ref->prev;
855 } else {
856 ref->next->prev = ref->prev;
857 }
858 }
859
860 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
861 if ( ref->count == 0 ) /* not registered */
862 {
863 /* add to list */
864 nitni_global_ref_add( ref );
865 }
866
867 ref->count ++;
868 }
869
870 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
871 if ( ref->count == 1 ) /* was last reference */
872 {
873 /* remove from list */
874 nitni_global_ref_remove( ref );
875 }
876
877 ref->count --;
878 }
879 """
880 end
881
882 # List of additional files required to compile (FFI)
883 var extern_bodies = new Array[ExternFile]
884
885 # List of source files to copy over to the compile dir
886 var files_to_copy = new Array[String]
887
888 # This is used to avoid adding an extern file more than once
889 private var seen_extern = new ArraySet[String]
890
891 # Generate code that initialize the attributes on a new instance
892 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
893 do
894 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
895 self.mainmodule.linearize_mclassdefs(cds)
896 for cd in cds do
897 if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
898 var n = self.modelbuilder.mclassdef2nclassdef[cd]
899 for npropdef in n.n_propdefs do
900 if npropdef isa AAttrPropdef then
901 npropdef.init_expr(v, recv)
902 end
903 end
904 end
905 end
906
907 # Generate code that check if an attribute is correctly initialized
908 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
909 do
910 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
911 self.mainmodule.linearize_mclassdefs(cds)
912 for cd in cds do
913 if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
914 var n = self.modelbuilder.mclassdef2nclassdef[cd]
915 for npropdef in n.n_propdefs do
916 if npropdef isa AAttrPropdef then
917 npropdef.check_expr(v, recv)
918 end
919 end
920 end
921 end
922
923 # stats
924
925 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
926 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
927 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
928 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
929
930 protected fun init_count_type_test_tags: HashMap[String, Int]
931 do
932 var res = new HashMap[String, Int]
933 for tag in count_type_test_tags do
934 res[tag] = 0
935 end
936 return res
937 end
938
939 # Display stats about compilation process
940 #
941 # Metrics used:
942 #
943 # * type tests against resolved types (`x isa Collection[Animal]`)
944 # * type tests against unresolved types (`x isa Collection[E]`)
945 # * type tests skipped
946 # * type tests total
947 fun display_stats
948 do
949 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
950 print "# static count_type_test"
951 print "\tresolved:\tunresolved\tskipped\ttotal"
952 var count_type_test_total = init_count_type_test_tags
953 count_type_test_resolved["total"] = 0
954 count_type_test_unresolved["total"] = 0
955 count_type_test_skipped["total"] = 0
956 count_type_test_total["total"] = 0
957 for tag in count_type_test_tags do
958 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
959 count_type_test_resolved["total"] += count_type_test_resolved[tag]
960 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
961 count_type_test_skipped["total"] += count_type_test_skipped[tag]
962 count_type_test_total["total"] += count_type_test_total[tag]
963 end
964 var count_type_test = count_type_test_total["total"]
965 var tags = count_type_test_tags.to_a
966 tags.add("total")
967 for tag in tags do
968 printn tag
969 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
970 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
971 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
972 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
973 print ""
974 end
975 end
976 end
977
978 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
979
980 # Division facility
981 # Avoid division by zero by returning the string "n/a"
982 fun div(a,b:Int):String
983 do
984 if b == 0 then return "n/a"
985 return ((a*10000/b).to_f / 100.0).to_precision(2)
986 end
987 end
988
989 # A file unit (may be more than one file if
990 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
991 class CodeFile
992 var name: String
993 var writers = new Array[CodeWriter]
994 var required_declarations = new HashSet[String]
995 end
996
997 # Where to store generated lines
998 class CodeWriter
999 var file: CodeFile
1000 var lines: List[String] = new List[String]
1001 var decl_lines: List[String] = new List[String]
1002
1003 # Add a line in the main part of the generated C
1004 fun add(s: String) do self.lines.add(s)
1005
1006 # Add a line in the
1007 # (used for local or global declaration)
1008 fun add_decl(s: String) do self.decl_lines.add(s)
1009
1010 init(file: CodeFile)
1011 do
1012 self.file = file
1013 file.writers.add(self)
1014 end
1015 end
1016
1017 # A visitor on the AST of property definition that generate the C code.
1018 abstract class AbstractCompilerVisitor
1019
1020 type COMPILER: AbstractCompiler
1021
1022 # The associated compiler
1023 var compiler: COMPILER
1024
1025 # The current visited AST node
1026 var current_node: nullable ANode = null is writable
1027
1028 # The current `Frame`
1029 var frame: nullable Frame is writable
1030
1031 # Alias for self.compiler.mainmodule.object_type
1032 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1033
1034 # Alias for self.compiler.mainmodule.bool_type
1035 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1036
1037 var writer: CodeWriter
1038
1039 init(compiler: COMPILER)
1040 do
1041 self.compiler = compiler
1042 self.writer = new CodeWriter(compiler.files.last)
1043 end
1044
1045 # Force to get the primitive class named `name` or abort
1046 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
1047
1048 # Force to get the primitive property named `name` in the instance `recv` or abort
1049 fun get_property(name: String, recv: MType): MMethod
1050 do
1051 assert recv isa MClassType
1052 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1053 end
1054
1055 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1056 do
1057 var initializers = callsite.mpropdef.initializers
1058 if not initializers.is_empty then
1059 var recv = arguments.first
1060
1061 var i = 1
1062 for p in initializers do
1063 if p isa MMethod then
1064 var args = [recv]
1065 for x in p.intro.msignature.mparameters do
1066 args.add arguments[i]
1067 i += 1
1068 end
1069 self.send(p, args)
1070 else if p isa MAttribute then
1071 self.write_attribute(p, recv, arguments[i])
1072 i += 1
1073 else abort
1074 end
1075 assert i == arguments.length
1076
1077 return self.send(callsite.mproperty, [recv])
1078 end
1079
1080 return self.send(callsite.mproperty, arguments)
1081 end
1082
1083 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1084
1085 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1086
1087 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1088
1089 # Transform varargs, in raw arguments, into a single argument of type `Array`
1090 # Note: this method modify the given `args`
1091 # If there is no vararg, then `args` is not modified.
1092 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
1093 do
1094 var recv = args.first
1095 var vararg_rank = msignature.vararg_rank
1096 if vararg_rank >= 0 then
1097 assert args.length >= msignature.arity + 1 # because of self
1098 var rawargs = args
1099 args = new Array[RuntimeVariable]
1100
1101 args.add(rawargs.first) # recv
1102
1103 for i in [0..vararg_rank[ do
1104 args.add(rawargs[i+1])
1105 end
1106
1107 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
1108 var vararg = new Array[RuntimeVariable]
1109 for i in [vararg_rank..vararg_lastrank] do
1110 vararg.add(rawargs[i+1])
1111 end
1112
1113 var elttype = msignature.mparameters[vararg_rank].mtype
1114 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
1115
1116 for i in [vararg_lastrank+1..rawargs.length-1[ do
1117 args.add(rawargs[i+1])
1118 end
1119 rawargs.clear
1120 rawargs.add_all(args)
1121 end
1122 end
1123
1124 # Type handling
1125
1126 # Anchor a type to the main module and the current receiver
1127 fun anchor(mtype: MType): MType
1128 do
1129 if not mtype.need_anchor then return mtype
1130 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1131 end
1132
1133 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1134 do
1135 if not mtype.need_anchor then return mtype
1136 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1137 end
1138
1139 # Unsafely cast a value to a new type
1140 # ie the result share the same C variable but my have a different mcasttype
1141 # NOTE: if the adaptation is useless then `value` is returned as it.
1142 # ENSURE: `result.name == value.name`
1143 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1144 do
1145 mtype = self.anchor(mtype)
1146 var valmtype = value.mcasttype
1147 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1148 return value
1149 end
1150
1151 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1152 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1153 return res
1154 else
1155 var res = new RuntimeVariable(value.name, valmtype, mtype)
1156 return res
1157 end
1158 end
1159
1160 # Generate a super call from a method definition
1161 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1162
1163 # Adapt the arguments of a method according to targetted `MMethodDef`
1164 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1165
1166 # Unbox all the arguments of a method when implemented `extern` or `intern`
1167 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1168
1169 # Box or unbox a value to another type iff a C type conversion is needed
1170 # ENSURE: `result.mtype.ctype == mtype.ctype`
1171 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1172
1173 # Box extern classes to be used in the generated code
1174 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1175
1176 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1177 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1178
1179 # Generate a polymorphic subtype test
1180 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1181
1182 # Generate the code required to dynamically check if 2 objects share the same runtime type
1183 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1184
1185 # Generate a Nit "is" for two runtime_variables
1186 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1187
1188 # Sends
1189
1190 # Generate a static call on a method definition
1191 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1192
1193 # Generate a polymorphic send for the method `m` and the arguments `args`
1194 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1195
1196 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1197 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1198 do
1199 assert t isa MClassType
1200 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1201 return self.call(propdef, t, args)
1202 end
1203
1204 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1205 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1206 do
1207 assert t isa MClassType
1208 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1209 return self.call(m, t, args)
1210 end
1211
1212 # Attributes handling
1213
1214 # Generate a polymorphic attribute is_set test
1215 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1216
1217 # Generate a polymorphic attribute read
1218 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1219
1220 # Generate a polymorphic attribute write
1221 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1222
1223 # Checks
1224
1225 # Add a check and an abort for a null receiver if needed
1226 fun check_recv_notnull(recv: RuntimeVariable)
1227 do
1228 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1229
1230 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1231 if maybenull then
1232 self.add("if (unlikely({recv} == NULL)) \{")
1233 self.add_abort("Receiver is null")
1234 self.add("\}")
1235 end
1236 end
1237
1238 # Names handling
1239
1240 private var names = new HashSet[String]
1241 private var last: Int = 0
1242
1243 # Return a new name based on `s` and unique in the visitor
1244 fun get_name(s: String): String
1245 do
1246 if not self.names.has(s) then
1247 self.names.add(s)
1248 return s
1249 end
1250 var i = self.last + 1
1251 loop
1252 var s2 = s + i.to_s
1253 if not self.names.has(s2) then
1254 self.last = i
1255 self.names.add(s2)
1256 return s2
1257 end
1258 i = i + 1
1259 end
1260 end
1261
1262 # Return an unique and stable identifier associated with an escapemark
1263 fun escapemark_name(e: nullable EscapeMark): String
1264 do
1265 assert e != null
1266 if escapemark_names.has_key(e) then return escapemark_names[e]
1267 var name = e.name
1268 if name == null then name = "label"
1269 name = get_name(name)
1270 escapemark_names[e] = name
1271 return name
1272 end
1273
1274 # Insert a C label for associated with an escapemark
1275 fun add_escape_label(e: nullable EscapeMark)
1276 do
1277 if e == null then return
1278 if e.escapes.is_empty then return
1279 add("BREAK_{escapemark_name(e)}: (void)0;")
1280 end
1281
1282 private var escapemark_names = new HashMap[EscapeMark, String]
1283
1284 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1285 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1286 fun class_name_string(value: RuntimeVariable): String is abstract
1287
1288 # Variables handling
1289
1290 protected var variables = new HashMap[Variable, RuntimeVariable]
1291
1292 # Return the local runtime_variable associated to a Nit local variable
1293 fun variable(variable: Variable): RuntimeVariable
1294 do
1295 if self.variables.has_key(variable) then
1296 return self.variables[variable]
1297 else
1298 var name = self.get_name("var_{variable.name}")
1299 var mtype = variable.declared_type.as(not null)
1300 mtype = self.anchor(mtype)
1301 var res = new RuntimeVariable(name, mtype, mtype)
1302 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1303 self.variables[variable] = res
1304 return res
1305 end
1306 end
1307
1308 # Return a new uninitialized local runtime_variable
1309 fun new_var(mtype: MType): RuntimeVariable
1310 do
1311 mtype = self.anchor(mtype)
1312 var name = self.get_name("var")
1313 var res = new RuntimeVariable(name, mtype, mtype)
1314 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1315 return res
1316 end
1317
1318 # The difference with `new_var` is the C static type of the local variable
1319 fun new_var_extern(mtype: MType): RuntimeVariable
1320 do
1321 mtype = self.anchor(mtype)
1322 var name = self.get_name("var")
1323 var res = new RuntimeVariable(name, mtype, mtype)
1324 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1325 return res
1326 end
1327
1328 # Return a new uninitialized named runtime_variable
1329 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1330 do
1331 mtype = self.anchor(mtype)
1332 var res = new RuntimeVariable(name, mtype, mtype)
1333 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1334 return res
1335 end
1336
1337 # Correctly assign a left and a right value
1338 # Boxing and unboxing is performed if required
1339 fun assign(left, right: RuntimeVariable)
1340 do
1341 right = self.autobox(right, left.mtype)
1342 self.add("{left} = {right};")
1343 end
1344
1345 # Generate instances
1346
1347 # Generate a alloc-instance + init-attributes
1348 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1349
1350 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1351 fun set_finalizer(recv: RuntimeVariable)
1352 do
1353 var mtype = recv.mtype
1354 var finalizable_type = compiler.mainmodule.finalizable_type
1355 if finalizable_type != null and not mtype.need_anchor and
1356 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1357 add "gc_register_finalizer({recv});"
1358 end
1359 end
1360
1361 # Generate an integer value
1362 fun int_instance(value: Int): RuntimeVariable
1363 do
1364 var res = self.new_var(self.get_class("Int").mclass_type)
1365 self.add("{res} = {value};")
1366 return res
1367 end
1368
1369 # Generate an integer value
1370 fun bool_instance(value: Bool): RuntimeVariable
1371 do
1372 var res = self.new_var(self.get_class("Bool").mclass_type)
1373 if value then
1374 self.add("{res} = 1;")
1375 else
1376 self.add("{res} = 0;")
1377 end
1378 return res
1379 end
1380
1381 # Generate a string value
1382 fun string_instance(string: String): RuntimeVariable
1383 do
1384 var mtype = self.get_class("String").mclass_type
1385 var name = self.get_name("varonce")
1386 self.add_decl("static {mtype.ctype} {name};")
1387 var res = self.new_var(mtype)
1388 self.add("if ({name}) \{")
1389 self.add("{res} = {name};")
1390 self.add("\} else \{")
1391 var native_mtype = self.get_class("NativeString").mclass_type
1392 var nat = self.new_var(native_mtype)
1393 self.add("{nat} = \"{string.escape_to_c}\";")
1394 var length = self.int_instance(string.length)
1395 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1396 self.add("{name} = {res};")
1397 self.add("\}")
1398 return res
1399 end
1400
1401 fun value_instance(object: Object): RuntimeVariable
1402 do
1403 if object isa Int then
1404 return int_instance(object)
1405 else if object isa Bool then
1406 return bool_instance(object)
1407 else if object isa String then
1408 return string_instance(object)
1409 else
1410 abort
1411 end
1412 end
1413
1414 # Generate an array value
1415 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1416
1417 # Get an instance of a array for a vararg
1418 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1419
1420 # Code generation
1421
1422 # Add a line in the main part of the generated C
1423 fun add(s: String) do self.writer.lines.add(s)
1424
1425 # Add a line in the
1426 # (used for local or global declaration)
1427 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1428
1429 # Request the presence of a global declaration
1430 fun require_declaration(key: String)
1431 do
1432 var reqs = self.writer.file.required_declarations
1433 if reqs.has(key) then return
1434 reqs.add(key)
1435 var node = current_node
1436 if node != null then compiler.requirers_of_declarations[key] = node
1437 end
1438
1439 # Add a declaration in the local-header
1440 # The declaration is ensured to be present once
1441 fun declare_once(s: String)
1442 do
1443 self.compiler.provide_declaration(s, s)
1444 self.require_declaration(s)
1445 end
1446
1447 # look for a needed .h and .c file for a given .nit source-file
1448 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1449 fun add_extern(file: String)
1450 do
1451 file = file.strip_extension(".nit")
1452 var tryfile = file + ".nit.h"
1453 if tryfile.file_exists then
1454 self.declare_once("#include \"{tryfile.basename("")}\"")
1455 self.compiler.files_to_copy.add(tryfile)
1456 end
1457 tryfile = file + "_nit.h"
1458 if tryfile.file_exists then
1459 self.declare_once("#include \"{tryfile.basename("")}\"")
1460 self.compiler.files_to_copy.add(tryfile)
1461 end
1462
1463 if self.compiler.seen_extern.has(file) then return
1464 self.compiler.seen_extern.add(file)
1465 tryfile = file + ".nit.c"
1466 if not tryfile.file_exists then
1467 tryfile = file + "_nit.c"
1468 if not tryfile.file_exists then return
1469 end
1470 var f = new ExternCFile(tryfile.basename(""), "")
1471 self.compiler.extern_bodies.add(f)
1472 self.compiler.files_to_copy.add(tryfile)
1473 end
1474
1475 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1476 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1477 do
1478 var res = new_var(mtype)
1479 self.add("{res} = {cexpr};")
1480 return res
1481 end
1482
1483 # Generate generic abort
1484 # used by aborts, asserts, casts, etc.
1485 fun add_abort(message: String)
1486 do
1487 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1488 add_raw_abort
1489 end
1490
1491 fun add_raw_abort
1492 do
1493 if self.current_node != null and self.current_node.location.file != null then
1494 self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1495 else
1496 self.add("PRINT_ERROR(\"\\n\");")
1497 end
1498 self.add("show_backtrace(1);")
1499 end
1500
1501 # Add a dynamic cast
1502 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1503 do
1504 var res = self.type_test(value, mtype, tag)
1505 self.add("if (unlikely(!{res})) \{")
1506 var cn = self.class_name_string(value)
1507 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1508 self.add_raw_abort
1509 self.add("\}")
1510 end
1511
1512 # Generate a return with the value `s`
1513 fun ret(s: RuntimeVariable)
1514 do
1515 self.assign(self.frame.returnvar.as(not null), s)
1516 self.add("goto {self.frame.returnlabel.as(not null)};")
1517 end
1518
1519 # Compile a statement (if any)
1520 fun stmt(nexpr: nullable AExpr)
1521 do
1522 if nexpr == null then return
1523 var old = self.current_node
1524 self.current_node = nexpr
1525 nexpr.stmt(self)
1526 self.current_node = old
1527 end
1528
1529 # Compile an expression an return its result
1530 # `mtype` is the expected return type, pass null if no specific type is expected.
1531 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1532 do
1533 var old = self.current_node
1534 self.current_node = nexpr
1535 var res = nexpr.expr(self).as(not null)
1536 if mtype != null then
1537 mtype = self.anchor(mtype)
1538 res = self.autobox(res, mtype)
1539 end
1540 res = autoadapt(res, nexpr.mtype.as(not null))
1541 var implicit_cast_to = nexpr.implicit_cast_to
1542 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1543 add_cast(res, implicit_cast_to, "auto")
1544 res = autoadapt(res, implicit_cast_to)
1545 end
1546 self.current_node = old
1547 return res
1548 end
1549
1550 # Alias for `self.expr(nexpr, self.bool_type)`
1551 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1552
1553 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1554 fun debug(message: String)
1555 do
1556 var node = self.current_node
1557 if node == null then
1558 print "?: {message}"
1559 else
1560 node.debug(message)
1561 end
1562 self.add("/* DEBUG: {message} */")
1563 end
1564 end
1565
1566 # A C function associated to a Nit method
1567 # Because of customization, a given Nit method can be compiler more that once
1568 abstract class AbstractRuntimeFunction
1569
1570 type COMPILER: AbstractCompiler
1571 type VISITOR: AbstractCompilerVisitor
1572
1573 # The associated Nit method
1574 var mmethoddef: MMethodDef
1575
1576 # The mangled c name of the runtime_function
1577 # Subclasses should redefine `build_c_name` instead
1578 fun c_name: String
1579 do
1580 var res = self.c_name_cache
1581 if res != null then return res
1582 res = self.build_c_name
1583 self.c_name_cache = res
1584 return res
1585 end
1586
1587 # Non cached version of `c_name`
1588 protected fun build_c_name: String is abstract
1589
1590 protected var c_name_cache: nullable String = null is writable
1591
1592 # Implements a call of the runtime_function
1593 # May inline the body or generate a C function call
1594 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1595
1596 # Generate the code for the `AbstractRuntimeFunction`
1597 # Warning: compile more than once compilation makes CC unhappy
1598 fun compile_to_c(compiler: COMPILER) is abstract
1599 end
1600
1601 # A runtime variable hold a runtime value in C.
1602 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1603 #
1604 # 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.
1605 class RuntimeVariable
1606 # The name of the variable in the C code
1607 var name: String
1608
1609 # The static type of the variable (as declard in C)
1610 var mtype: MType
1611
1612 # The current casted type of the variable (as known in Nit)
1613 var mcasttype: MType is writable
1614
1615 # If the variable exaclty a mcasttype?
1616 # false (usual value) means that the variable is a mcasttype or a subtype.
1617 var is_exact: Bool = false is writable
1618
1619 init(name: String, mtype: MType, mcasttype: MType)
1620 do
1621 self.name = name
1622 self.mtype = mtype
1623 self.mcasttype = mcasttype
1624 assert not mtype.need_anchor
1625 assert not mcasttype.need_anchor
1626 end
1627
1628 redef fun to_s do return name
1629
1630 redef fun inspect
1631 do
1632 var exact_str
1633 if self.is_exact then
1634 exact_str = " exact"
1635 else
1636 exact_str = ""
1637 end
1638 var type_str
1639 if self.mtype == self.mcasttype then
1640 type_str = "{mtype}{exact_str}"
1641 else
1642 type_str = "{mtype}({mcasttype}{exact_str})"
1643 end
1644 return "<{name}:{type_str}>"
1645 end
1646 end
1647
1648 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1649 class Frame
1650
1651 type VISITOR: AbstractCompilerVisitor
1652
1653 # The associated visitor
1654 var visitor: VISITOR
1655
1656 # The executed property.
1657 # A Method in case of a call, an attribute in case of a default initialization.
1658 var mpropdef: MPropDef
1659
1660 # The static type of the receiver
1661 var receiver: MClassType
1662
1663 # Arguments of the method (the first is the receiver)
1664 var arguments: Array[RuntimeVariable]
1665
1666 # The runtime_variable associated to the return (in a function)
1667 var returnvar: nullable RuntimeVariable = null is writable
1668
1669 # The label at the end of the property
1670 var returnlabel: nullable String = null is writable
1671 end
1672
1673 redef class MType
1674 # Return the C type associated to a given Nit static type
1675 fun ctype: String do return "val*"
1676
1677 # C type outside of the compiler code and in boxes
1678 fun ctype_extern: String do return "val*"
1679
1680 # Short name of the `ctype` to use in unions
1681 fun ctypename: String do return "val"
1682
1683 # Return the name of the C structure associated to a Nit live type
1684 fun c_name: String is abstract
1685 protected var c_name_cache: nullable String is protected writable
1686 end
1687
1688 redef class MClassType
1689 redef fun c_name
1690 do
1691 var res = self.c_name_cache
1692 if res != null then return res
1693 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1694 self.c_name_cache = res
1695 return res
1696 end
1697
1698 redef fun ctype: String
1699 do
1700 if mclass.name == "Int" then
1701 return "long"
1702 else if mclass.name == "Bool" then
1703 return "short int"
1704 else if mclass.name == "Char" then
1705 return "char"
1706 else if mclass.name == "Float" then
1707 return "double"
1708 else if mclass.name == "NativeString" then
1709 return "char*"
1710 else if mclass.name == "NativeArray" then
1711 return "val*"
1712 else
1713 return "val*"
1714 end
1715 end
1716
1717 redef fun ctype_extern: String
1718 do
1719 if mclass.kind == extern_kind then
1720 return "void*"
1721 else
1722 return ctype
1723 end
1724 end
1725
1726 redef fun ctypename: String
1727 do
1728 if mclass.name == "Int" then
1729 return "l"
1730 else if mclass.name == "Bool" then
1731 return "s"
1732 else if mclass.name == "Char" then
1733 return "c"
1734 else if mclass.name == "Float" then
1735 return "d"
1736 else if mclass.name == "NativeString" then
1737 return "str"
1738 else if mclass.name == "NativeArray" then
1739 #return "{self.arguments.first.ctype}*"
1740 return "val"
1741 else
1742 return "val"
1743 end
1744 end
1745 end
1746
1747 redef class MGenericType
1748 redef fun c_name
1749 do
1750 var res = self.c_name_cache
1751 if res != null then return res
1752 res = super
1753 for t in self.arguments do
1754 res = res + t.c_name
1755 end
1756 self.c_name_cache = res
1757 return res
1758 end
1759 end
1760
1761 redef class MParameterType
1762 redef fun c_name
1763 do
1764 var res = self.c_name_cache
1765 if res != null then return res
1766 res = "{self.mclass.c_name}_FT{self.rank}"
1767 self.c_name_cache = res
1768 return res
1769 end
1770 end
1771
1772 redef class MVirtualType
1773 redef fun c_name
1774 do
1775 var res = self.c_name_cache
1776 if res != null then return res
1777 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1778 self.c_name_cache = res
1779 return res
1780 end
1781 end
1782
1783 redef class MNullableType
1784 redef fun c_name
1785 do
1786 var res = self.c_name_cache
1787 if res != null then return res
1788 res = "nullable_{self.mtype.c_name}"
1789 self.c_name_cache = res
1790 return res
1791 end
1792 end
1793
1794 redef class MClass
1795 # Return the name of the C structure associated to a Nit class
1796 fun c_name: String do
1797 var res = self.c_name_cache
1798 if res != null then return res
1799 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1800 self.c_name_cache = res
1801 return res
1802 end
1803 private var c_name_cache: nullable String
1804 end
1805
1806 redef class MProperty
1807 fun c_name: String do
1808 var res = self.c_name_cache
1809 if res != null then return res
1810 res = "{self.intro.c_name}"
1811 self.c_name_cache = res
1812 return res
1813 end
1814 private var c_name_cache: nullable String
1815 end
1816
1817 redef class MPropDef
1818 type VISITOR: AbstractCompilerVisitor
1819
1820 private var c_name_cache: nullable String
1821
1822 # The mangled name associated to the property
1823 fun c_name: String
1824 do
1825 var res = self.c_name_cache
1826 if res != null then return res
1827 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1828 self.c_name_cache = res
1829 return res
1830 end
1831 end
1832
1833 redef class MMethodDef
1834 # Can the body be inlined?
1835 fun can_inline(v: VISITOR): Bool
1836 do
1837 if is_abstract then return true
1838 var modelbuilder = v.compiler.modelbuilder
1839 if modelbuilder.mpropdef2npropdef.has_key(self) then
1840 var npropdef = modelbuilder.mpropdef2npropdef[self]
1841 return npropdef.can_inline
1842 else if self.mproperty.name == "init" then
1843 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1844 return true
1845 else
1846 abort
1847 end
1848 end
1849
1850 # Inline the body in another visitor
1851 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1852 do
1853 var modelbuilder = v.compiler.modelbuilder
1854 var val = constant_value
1855 if modelbuilder.mpropdef2npropdef.has_key(self) then
1856 var npropdef = modelbuilder.mpropdef2npropdef[self]
1857 var oldnode = v.current_node
1858 v.current_node = npropdef
1859 self.compile_parameter_check(v, arguments)
1860 npropdef.compile_to_c(v, self, arguments)
1861 v.current_node = oldnode
1862 else if self.mproperty.name == "init" then
1863 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1864 var oldnode = v.current_node
1865 v.current_node = nclassdef
1866 self.compile_parameter_check(v, arguments)
1867 nclassdef.compile_to_c(v, self, arguments)
1868 v.current_node = oldnode
1869 else if val != null then
1870 v.ret(v.value_instance(val))
1871 else
1872 abort
1873 end
1874 return null
1875 end
1876
1877 # Generate type checks in the C code to check covariant parameters
1878 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1879 do
1880 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1881
1882 for i in [0..msignature.arity[ do
1883 # skip test for vararg since the array is instantiated with the correct polymorphic type
1884 if msignature.vararg_rank == i then continue
1885
1886 # skip if the cast is not required
1887 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1888 if not origmtype.need_anchor then continue
1889
1890 # get the parameter type
1891 var mtype = self.msignature.mparameters[i].mtype
1892
1893 # generate the cast
1894 # note that v decides if and how to implements the cast
1895 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1896 v.add_cast(arguments[i+1], mtype, "covariance")
1897 end
1898 end
1899 end
1900
1901 # Node visit
1902
1903 redef class APropdef
1904 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1905 do
1906 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1907 debug("Not yet implemented")
1908 end
1909
1910 fun can_inline: Bool do return true
1911 end
1912
1913 redef class AMethPropdef
1914 redef fun compile_to_c(v, mpropdef, arguments)
1915 do
1916 if mpropdef.is_abstract then
1917 var cn = v.class_name_string(arguments.first)
1918 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1919 v.add_raw_abort
1920 return
1921 end
1922
1923 # Call the implicit super-init
1924 var auto_super_inits = self.auto_super_inits
1925 if auto_super_inits != null then
1926 var args = [arguments.first]
1927 for auto_super_init in auto_super_inits do
1928 assert auto_super_init.mproperty != mpropdef.mproperty
1929 args.clear
1930 for i in [0..auto_super_init.msignature.arity+1[ do
1931 args.add(arguments[i])
1932 end
1933 assert auto_super_init.mproperty != mpropdef.mproperty
1934 v.compile_callsite(auto_super_init, args)
1935 end
1936 end
1937 if auto_super_call then
1938 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1939 end
1940
1941 # Try special compilation
1942 if mpropdef.is_intern then
1943 if compile_intern_to_c(v, mpropdef, arguments) then return
1944 else if mpropdef.is_extern then
1945 if mpropdef.mproperty.is_init then
1946 if compile_externinit_to_c(v, mpropdef, arguments) then return
1947 else
1948 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1949 end
1950 end
1951
1952 # Compile block if any
1953 var n_block = n_block
1954 if n_block != null then
1955 for i in [0..mpropdef.msignature.arity[ do
1956 var variable = self.n_signature.n_params[i].variable.as(not null)
1957 v.assign(v.variable(variable), arguments[i+1])
1958 end
1959 v.stmt(n_block)
1960 return
1961 end
1962
1963 # We have a problem
1964 var cn = v.class_name_string(arguments.first)
1965 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1966 v.add_raw_abort
1967 end
1968
1969 redef fun can_inline
1970 do
1971 if self.auto_super_inits != null then return false
1972 var nblock = self.n_block
1973 if nblock == null then return true
1974 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1975 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1976 return false
1977 end
1978
1979 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1980 do
1981 var pname = mpropdef.mproperty.name
1982 var cname = mpropdef.mclassdef.mclass.name
1983 var ret = mpropdef.msignature.return_mtype
1984 if ret != null then
1985 ret = v.resolve_for(ret, arguments.first)
1986 else if mpropdef.mproperty.is_new then
1987 ret = arguments.first.mcasttype
1988 end
1989 if pname != "==" and pname != "!=" then
1990 v.adapt_signature(mpropdef, arguments)
1991 v.unbox_signature_extern(mpropdef, arguments)
1992 end
1993 if cname == "Int" then
1994 if pname == "output" then
1995 v.add("printf(\"%ld\\n\", {arguments.first});")
1996 return true
1997 else if pname == "object_id" then
1998 v.ret(arguments.first)
1999 return true
2000 else if pname == "+" then
2001 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2002 return true
2003 else if pname == "-" then
2004 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2005 return true
2006 else if pname == "unary -" then
2007 v.ret(v.new_expr("-{arguments[0]}", 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 == "/" then
2013 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", 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 == "lshift" then
2019 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2020 return true
2021 else if pname == "rshift" then
2022 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2023 return true
2024 else if pname == "==" then
2025 v.ret(v.equal_test(arguments[0], arguments[1]))
2026 return true
2027 else if pname == "!=" then
2028 var res = v.equal_test(arguments[0], arguments[1])
2029 v.ret(v.new_expr("!{res}", ret.as(not null)))
2030 return true
2031 else if pname == "<" then
2032 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2033 return true
2034 else if pname == ">" then
2035 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", 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 == "to_f" then
2044 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2045 return true
2046 else if pname == "ascii" then
2047 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2048 return true
2049 end
2050 else if cname == "Char" then
2051 if pname == "output" then
2052 v.add("printf(\"%c\", {arguments.first});")
2053 return true
2054 else if pname == "object_id" then
2055 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2056 return true
2057 else if pname == "successor" then
2058 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2059 return true
2060 else if pname == "predecessor" then
2061 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2062 return true
2063 else if pname == "==" then
2064 v.ret(v.equal_test(arguments[0], arguments[1]))
2065 return true
2066 else if pname == "!=" then
2067 var res = v.equal_test(arguments[0], arguments[1])
2068 v.ret(v.new_expr("!{res}", ret.as(not null)))
2069 return true
2070 else if pname == "<" then
2071 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2072 return true
2073 else if pname == ">" then
2074 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", 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 == "to_i" then
2083 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2084 return true
2085 else if pname == "ascii" then
2086 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2087 return true
2088 end
2089 else if cname == "Bool" then
2090 if pname == "output" then
2091 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2092 return true
2093 else if pname == "object_id" then
2094 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2095 return true
2096 else if pname == "==" then
2097 v.ret(v.equal_test(arguments[0], arguments[1]))
2098 return true
2099 else if pname == "!=" then
2100 var res = v.equal_test(arguments[0], arguments[1])
2101 v.ret(v.new_expr("!{res}", ret.as(not null)))
2102 return true
2103 end
2104 else if cname == "Float" then
2105 if pname == "output" then
2106 v.add("printf(\"%f\\n\", {arguments.first});")
2107 return true
2108 else if pname == "object_id" then
2109 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2110 return true
2111 else if pname == "+" then
2112 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2113 return true
2114 else if pname == "-" then
2115 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2116 return true
2117 else if pname == "unary -" then
2118 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2119 return true
2120 else if pname == "succ" then
2121 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2122 return true
2123 else if pname == "prec" then
2124 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2125 return true
2126 else if pname == "*" then
2127 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2128 return true
2129 else if pname == "/" then
2130 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2131 return true
2132 else if pname == "==" then
2133 v.ret(v.equal_test(arguments[0], arguments[1]))
2134 return true
2135 else if pname == "!=" then
2136 var res = v.equal_test(arguments[0], arguments[1])
2137 v.ret(v.new_expr("!{res}", ret.as(not null)))
2138 return true
2139 else if pname == "<" then
2140 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2141 return true
2142 else if pname == ">" then
2143 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", 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 == "to_i" then
2152 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2153 return true
2154 end
2155 else if cname == "NativeString" then
2156 if pname == "[]" then
2157 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2158 return true
2159 else if pname == "[]=" then
2160 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2161 return true
2162 else if pname == "copy_to" then
2163 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2164 return true
2165 else if pname == "atoi" then
2166 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2167 return true
2168 else if pname == "init" then
2169 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2170 return true
2171 end
2172 else if cname == "NativeArray" then
2173 v.native_array_def(pname, ret, arguments)
2174 return true
2175 end
2176 if pname == "exit" then
2177 v.add("exit({arguments[1]});")
2178 return true
2179 else if pname == "sys" then
2180 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2181 return true
2182 else if pname == "calloc_string" then
2183 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2184 return true
2185 else if pname == "calloc_array" then
2186 v.calloc_array(ret.as(not null), arguments)
2187 return true
2188 else if pname == "object_id" then
2189 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2190 return true
2191 else if pname == "is_same_type" then
2192 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2193 return true
2194 else if pname == "is_same_instance" then
2195 v.ret(v.equal_test(arguments[0], arguments[1]))
2196 return true
2197 else if pname == "output_class_name" then
2198 var nat = v.class_name_string(arguments.first)
2199 v.add("printf(\"%s\\n\", {nat});")
2200 return true
2201 else if pname == "native_class_name" then
2202 var nat = v.class_name_string(arguments.first)
2203 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2204 return true
2205 else if pname == "force_garbage_collection" then
2206 v.add("nit_gcollect();")
2207 return true
2208 else if pname == "native_argc" then
2209 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2210 return true
2211 else if pname == "native_argv" then
2212 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2213 return true
2214 end
2215 return false
2216 end
2217
2218 # Compile an extern method
2219 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2220 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2221 do
2222 var externname
2223 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2224 if at != null then
2225 externname = at.arg_as_string(v.compiler.modelbuilder)
2226 if externname == null then return false
2227 else
2228 return false
2229 end
2230 if location.file != null then
2231 var file = location.file.filename
2232 v.add_extern(file)
2233 end
2234 var res: nullable RuntimeVariable = null
2235 var ret = mpropdef.msignature.return_mtype
2236 if ret != null then
2237 ret = v.resolve_for(ret, arguments.first)
2238 res = v.new_var_extern(ret)
2239 end
2240 v.adapt_signature(mpropdef, arguments)
2241 v.unbox_signature_extern(mpropdef, arguments)
2242
2243 if res == null then
2244 v.add("{externname}({arguments.join(", ")});")
2245 else
2246 v.add("{res} = {externname}({arguments.join(", ")});")
2247 res = v.box_extern(res, ret.as(not null))
2248 v.ret(res)
2249 end
2250 return true
2251 end
2252
2253 # Compile an extern factory
2254 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2255 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2256 do
2257 var externname
2258 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2259 if at != null then
2260 externname = at.arg_as_string(v.compiler.modelbuilder)
2261 if externname == null then return false
2262 else
2263 return false
2264 end
2265 if location.file != null then
2266 var file = location.file.filename
2267 v.add_extern(file)
2268 end
2269 v.adapt_signature(mpropdef, arguments)
2270 v.unbox_signature_extern(mpropdef, arguments)
2271 var ret = arguments.first.mtype
2272 var res = v.new_var_extern(ret)
2273
2274 arguments.shift
2275
2276 v.add("{res} = {externname}({arguments.join(", ")});")
2277 res = v.box_extern(res, ret)
2278 v.ret(res)
2279 return true
2280 end
2281 end
2282
2283 redef class AAttrPropdef
2284 redef fun compile_to_c(v, mpropdef, arguments)
2285 do
2286 if mpropdef == mreadpropdef then
2287 assert arguments.length == 1
2288 var res
2289 if is_lazy then
2290 var nexpr = n_expr
2291 assert nexpr != null
2292 var set
2293 var ret = self.mpropdef.static_mtype
2294 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2295 var guard = self.mlazypropdef.mproperty
2296 if useiset then
2297 set = v.isset_attribute(self.mpropdef.mproperty, arguments.first)
2298 else
2299 set = v.read_attribute(guard, arguments.first)
2300 end
2301 v.add("if(likely({set})) \{")
2302 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2303 v.add("\} else \{")
2304 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2305 v.write_attribute(self.mpropdef.mproperty, arguments.first, value)
2306 v.assign(res, value)
2307 if not useiset then
2308 var true_v = v.new_expr("1", v.bool_type)
2309 v.write_attribute(guard, arguments.first, true_v)
2310 end
2311 v.add("\}")
2312 else
2313 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2314 end
2315 v.assign(v.frame.returnvar.as(not null), res)
2316 else if mpropdef == mwritepropdef then
2317 assert arguments.length == 2
2318 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2319 if is_lazy then
2320 var ret = self.mpropdef.static_mtype
2321 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2322 if not useiset then
2323 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2324 end
2325 end
2326 else
2327 abort
2328 end
2329 end
2330
2331 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2332 do
2333 var nexpr = self.n_expr
2334 if nexpr != null and not is_lazy then
2335 var oldnode = v.current_node
2336 v.current_node = self
2337 var old_frame = v.frame
2338 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2339 v.frame = frame
2340 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2341 v.write_attribute(self.mpropdef.mproperty, recv, value)
2342 v.frame = old_frame
2343 v.current_node = oldnode
2344 end
2345 end
2346
2347 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2348 do
2349 var nexpr = self.n_expr
2350 if nexpr != null then return
2351
2352 var oldnode = v.current_node
2353 v.current_node = self
2354 var old_frame = v.frame
2355 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2356 v.frame = frame
2357 # Force read to check the initialization
2358 v.read_attribute(self.mpropdef.mproperty, recv)
2359 v.frame = old_frame
2360 v.current_node = oldnode
2361 end
2362 end
2363
2364 redef class AClassdef
2365 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2366 do
2367 if mpropdef == self.mfree_init then
2368 assert mpropdef.mproperty.is_root_init
2369 assert arguments.length == 1
2370 if not mpropdef.is_intro then
2371 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2372 end
2373 return
2374 else
2375 abort
2376 end
2377 end
2378 end
2379
2380 redef class AExpr
2381 # Try to compile self as an expression
2382 # Do not call this method directly, use `v.expr` instead
2383 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2384 do
2385 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2386 var mtype = self.mtype
2387 if mtype == null then
2388 return null
2389 else
2390 var res = v.new_var(mtype)
2391 v.add("/* {res} = NOT YET {class_name} */")
2392 return res
2393 end
2394 end
2395
2396 # Try to compile self as a statement
2397 # Do not call this method directly, use `v.stmt` instead
2398 private fun stmt(v: AbstractCompilerVisitor)
2399 do
2400 expr(v)
2401 end
2402 end
2403
2404 redef class ABlockExpr
2405 redef fun stmt(v)
2406 do
2407 for e in self.n_expr do v.stmt(e)
2408 end
2409 redef fun expr(v)
2410 do
2411 var last = self.n_expr.last
2412 for e in self.n_expr do
2413 if e == last then break
2414 v.stmt(e)
2415 end
2416 return v.expr(last, null)
2417 end
2418 end
2419
2420 redef class AVardeclExpr
2421 redef fun stmt(v)
2422 do
2423 var variable = self.variable.as(not null)
2424 var ne = self.n_expr
2425 if ne != null then
2426 var i = v.expr(ne, variable.declared_type)
2427 v.assign(v.variable(variable), i)
2428 end
2429 end
2430 end
2431
2432 redef class AVarExpr
2433 redef fun expr(v)
2434 do
2435 var res = v.variable(self.variable.as(not null))
2436 var mtype = self.mtype.as(not null)
2437 return v.autoadapt(res, mtype)
2438 end
2439 end
2440
2441 redef class AVarAssignExpr
2442 redef fun stmt(v)
2443 do
2444 var variable = self.variable.as(not null)
2445 var i = v.expr(self.n_value, variable.declared_type)
2446 v.assign(v.variable(variable), i)
2447 end
2448 redef fun expr(v)
2449 do
2450 var variable = self.variable.as(not null)
2451 var i = v.expr(self.n_value, variable.declared_type)
2452 v.assign(v.variable(variable), i)
2453 return i
2454 end
2455 end
2456
2457 redef class AVarReassignExpr
2458 redef fun stmt(v)
2459 do
2460 var variable = self.variable.as(not null)
2461 var vari = v.variable(variable)
2462 var value = v.expr(self.n_value, variable.declared_type)
2463 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2464 assert res != null
2465 v.assign(v.variable(variable), res)
2466 end
2467 end
2468
2469 redef class ASelfExpr
2470 redef fun expr(v) do return v.frame.arguments.first
2471 end
2472
2473 redef class 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 args = [recv]
2844 for a in self.raw_arguments do
2845 args.add(v.expr(a, null))
2846 end
2847 return v.compile_callsite(self.callsite.as(not null), args)
2848 end
2849 end
2850
2851 redef class ASendReassignFormExpr
2852 redef fun stmt(v)
2853 do
2854 var recv = v.expr(self.n_expr, null)
2855 var args = [recv]
2856 for a in self.raw_arguments do
2857 args.add(v.expr(a, null))
2858 end
2859 var value = v.expr(self.n_value, null)
2860
2861 var left = v.compile_callsite(self.callsite.as(not null), args)
2862 assert left != null
2863
2864 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2865 assert res != null
2866
2867 args.add(res)
2868 v.compile_callsite(self.write_callsite.as(not null), args)
2869 end
2870 end
2871
2872 redef class ASuperExpr
2873 redef fun expr(v)
2874 do
2875 var recv = v.frame.arguments.first
2876 var args = [recv]
2877 for a in self.n_args.n_exprs do
2878 args.add(v.expr(a, null))
2879 end
2880
2881 var callsite = self.callsite
2882 if callsite != null then
2883 # Add additionnals arguments for the super init call
2884 if args.length == 1 then
2885 for i in [0..callsite.msignature.arity[ do
2886 args.add(v.frame.arguments[i+1])
2887 end
2888 end
2889 # Super init call
2890 var res = v.compile_callsite(callsite, args)
2891 return res
2892 end
2893
2894 if args.length == 1 then
2895 args = v.frame.arguments
2896 end
2897
2898 # stantard call-next-method
2899 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2900 end
2901 end
2902
2903 redef class ANewExpr
2904 redef fun expr(v)
2905 do
2906 var mtype = self.mtype.as(MClassType)
2907 var recv
2908 var ctype = mtype.ctype
2909 if mtype.mclass.name == "NativeArray" then
2910 assert self.n_args.n_exprs.length == 1
2911 var l = v.expr(self.n_args.n_exprs.first, null)
2912 assert mtype isa MGenericType
2913 var elttype = mtype.arguments.first
2914 return v.native_array_instance(elttype, l)
2915 else if ctype == "val*" then
2916 recv = v.init_instance(mtype)
2917 else if ctype == "char*" then
2918 recv = v.new_expr("NULL/*special!*/", mtype)
2919 else
2920 recv = v.new_expr("({ctype})0/*special!*/", mtype)
2921 end
2922 var args = [recv]
2923 for a in self.n_args.n_exprs do
2924 args.add(v.expr(a, null))
2925 end
2926 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2927 if res2 != null then
2928 #self.debug("got {res2} from {mproperty}. drop {recv}")
2929 return res2
2930 end
2931 return recv
2932 end
2933 end
2934
2935 redef class AAttrExpr
2936 redef fun expr(v)
2937 do
2938 var recv = v.expr(self.n_expr, null)
2939 var mproperty = self.mproperty.as(not null)
2940 return v.read_attribute(mproperty, recv)
2941 end
2942 end
2943
2944 redef class AAttrAssignExpr
2945 redef fun stmt(v)
2946 do
2947 var recv = v.expr(self.n_expr, null)
2948 var i = v.expr(self.n_value, null)
2949 var mproperty = self.mproperty.as(not null)
2950 v.write_attribute(mproperty, recv, i)
2951 end
2952 end
2953
2954 redef class AAttrReassignExpr
2955 redef fun stmt(v)
2956 do
2957 var recv = v.expr(self.n_expr, null)
2958 var value = v.expr(self.n_value, null)
2959 var mproperty = self.mproperty.as(not null)
2960 var attr = v.read_attribute(mproperty, recv)
2961 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2962 assert res != null
2963 v.write_attribute(mproperty, recv, res)
2964 end
2965 end
2966
2967 redef class AIssetAttrExpr
2968 redef fun expr(v)
2969 do
2970 var recv = v.expr(self.n_expr, null)
2971 var mproperty = self.mproperty.as(not null)
2972 return v.isset_attribute(mproperty, recv)
2973 end
2974 end
2975
2976 redef class ADebugTypeExpr
2977 redef fun stmt(v)
2978 do
2979 # do nothing
2980 end
2981 end
2982
2983 # Utils
2984
2985 redef class Array[E]
2986 # Return a new `Array` with the elements only contened in self and not in `o`
2987 fun -(o: Array[E]): Array[E] do
2988 var res = new Array[E]
2989 for e in self do if not o.has(e) then res.add(e)
2990 return res
2991 end
2992 end
2993
2994 redef class MModule
2995 # All `MProperty` associated to all `MClassDef` of `mclass`
2996 fun properties(mclass: MClass): Set[MProperty] do
2997 if not self.properties_cache.has_key(mclass) then
2998 var properties = new HashSet[MProperty]
2999 var parents = new Array[MClass]
3000 if self.flatten_mclass_hierarchy.has(mclass) then
3001 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3002 end
3003 for parent in parents do
3004 properties.add_all(self.properties(parent))
3005 end
3006 for mclassdef in mclass.mclassdefs do
3007 if not self.in_importation <= mclassdef.mmodule then continue
3008 for mprop in mclassdef.intro_mproperties do
3009 properties.add(mprop)
3010 end
3011 end
3012 self.properties_cache[mclass] = properties
3013 end
3014 return properties_cache[mclass]
3015 end
3016 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3017
3018 # Write FFI and nitni results to file
3019 fun finalize_ffi(c: AbstractCompiler) do end
3020
3021 # Give requided addinional system libraries (as given to LD_LIBS)
3022 # Note: can return null instead of an empty set
3023 fun collect_linker_libs: nullable Set[String] do return null
3024 end
3025
3026 # Create a tool context to handle options and paths
3027 var toolcontext = new ToolContext
3028
3029 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
3030
3031 # We do not add other options, so process them now!
3032 toolcontext.process_options(args)
3033
3034 # We need a model to collect stufs
3035 var model = new Model
3036 # An a model builder to parse files
3037 var modelbuilder = new ModelBuilder(model, toolcontext)
3038
3039 var arguments = toolcontext.option_context.rest
3040 if arguments.length > 1 and toolcontext.opt_output.value != null then
3041 print "Error: --output needs a single source file. Do you prefer --dir?"
3042 exit 1
3043 end
3044
3045 # Here we load an process all modules passed on the command line
3046 var mmodules = modelbuilder.parse(arguments)
3047
3048 if mmodules.is_empty then return
3049 modelbuilder.run_phases
3050
3051 for mmodule in mmodules do
3052 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3053 var ms = [mmodule]
3054 toolcontext.run_global_phases(ms)
3055 end