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