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