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