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