compiler: runtime error (instead of noop) on dead/invalid code
[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 self.toolcontext.opt_stacktrace.value == "nitstack" and 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 ost = modelbuilder.toolcontext.opt_stacktrace.value
717 var platform = target_platform
718
719 if not platform.supports_libunwind then ost = "none"
720
721 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
722
723 if ost == "nitstack" or ost == "libunwind" then
724 v.add_decl("#define UNW_LOCAL_ONLY")
725 v.add_decl("#include <libunwind.h>")
726 if ost == "nitstack" then
727 v.add_decl("#include \"c_functions_hash.h\"")
728 end
729 end
730 v.add_decl("int glob_argc;")
731 v.add_decl("char **glob_argv;")
732 v.add_decl("val *glob_sys;")
733
734 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
735 for tag in count_type_test_tags do
736 v.add_decl("long count_type_test_resolved_{tag};")
737 v.add_decl("long count_type_test_unresolved_{tag};")
738 v.add_decl("long count_type_test_skipped_{tag};")
739 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
740 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
741 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
742 end
743 end
744
745 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
746 v.add_decl("long count_invoke_by_tables;")
747 v.add_decl("long count_invoke_by_direct;")
748 v.add_decl("long count_invoke_by_inline;")
749 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
750 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
751 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
752 end
753
754 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
755 v.add_decl("long count_attr_reads = 0;")
756 v.add_decl("long count_isset_checks = 0;")
757 v.compiler.header.add_decl("extern long count_attr_reads;")
758 v.compiler.header.add_decl("extern long count_isset_checks;")
759 end
760
761 v.add_decl("static void show_backtrace(void) \{")
762 if ost == "nitstack" or ost == "libunwind" then
763 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
764 v.add_decl("unw_cursor_t cursor;")
765 v.add_decl("if(opt==NULL)\{")
766 v.add_decl("unw_context_t uc;")
767 v.add_decl("unw_word_t ip;")
768 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
769 v.add_decl("unw_getcontext(&uc);")
770 v.add_decl("unw_init_local(&cursor, &uc);")
771 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
772 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
773 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
774 v.add_decl("while (unw_step(&cursor) > 0) \{")
775 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
776 if ost == "nitstack" then
777 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
778 v.add_decl(" if (recv != NULL)\{")
779 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
780 v.add_decl(" \}else\{")
781 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
782 v.add_decl(" \}")
783 else
784 v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
785 end
786 v.add_decl("\}")
787 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
788 v.add_decl("free(procname);")
789 v.add_decl("\}")
790 end
791 v.add_decl("\}")
792
793 v.add_decl("void sig_handler(int signo)\{")
794 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
795 v.add_decl("show_backtrace();")
796 # rethrows
797 v.add_decl("signal(signo, SIG_DFL);")
798 v.add_decl("kill(getpid(), signo);")
799 v.add_decl("\}")
800
801 v.add_decl("void fatal_exit(int status) \{")
802 v.add_decl("show_backtrace();")
803 v.add_decl("exit(status);")
804 v.add_decl("\}")
805
806 if no_main then
807 v.add_decl("int nit_main(int argc, char** argv) \{")
808 else
809 v.add_decl("int main(int argc, char** argv) \{")
810 end
811
812 v.add("signal(SIGABRT, sig_handler);")
813 v.add("signal(SIGFPE, sig_handler);")
814 v.add("signal(SIGILL, sig_handler);")
815 v.add("signal(SIGINT, sig_handler);")
816 v.add("signal(SIGTERM, sig_handler);")
817 v.add("signal(SIGSEGV, sig_handler);")
818 v.add("signal(SIGPIPE, sig_handler);")
819
820 v.add("glob_argc = argc; glob_argv = argv;")
821 v.add("initialize_gc_option();")
822
823 v.add "initialize_nitni_global_refs();"
824
825 var main_type = mainmodule.sys_type
826 if main_type != null then
827 var mainmodule = v.compiler.mainmodule
828 var glob_sys = v.init_instance(main_type)
829 v.add("glob_sys = {glob_sys};")
830 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
831 if main_init != null then
832 v.send(main_init, [glob_sys])
833 end
834 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
835 mainmodule.try_get_primitive_method("main", main_type.mclass)
836 if main_method != null then
837 v.send(main_method, [glob_sys])
838 end
839 end
840
841 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
842 v.add_decl("long count_type_test_resolved_total = 0;")
843 v.add_decl("long count_type_test_unresolved_total = 0;")
844 v.add_decl("long count_type_test_skipped_total = 0;")
845 v.add_decl("long count_type_test_total_total = 0;")
846 for tag in count_type_test_tags do
847 v.add_decl("long count_type_test_total_{tag};")
848 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
849 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
850 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
851 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
852 v.add("count_type_test_total_total += count_type_test_total_{tag};")
853 end
854 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
855 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
856 var tags = count_type_test_tags.to_a
857 tags.add("total")
858 for tag in tags do
859 v.add("printf(\"{tag}\");")
860 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
861 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
862 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
863 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
864 end
865 end
866
867 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
868 v.add_decl("long count_invoke_total;")
869 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
870 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
871 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
872 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
873 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
874 end
875
876 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
877 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
878 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
879 end
880
881 v.add("return 0;")
882 v.add("\}")
883
884 for m in mainmodule.in_importation.greaters do
885 var f = "FILE_"+m.c_name
886 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
887 provide_declaration(f, "extern const char {f}[];")
888 end
889 end
890
891 # Copile all C functions related to the [incr|decr]_ref features of the FFI
892 fun compile_nitni_global_ref_functions
893 do
894 var v = self.new_visitor
895 v.add """
896 struct nitni_global_ref_list_t *nitni_global_ref_list;
897 void initialize_nitni_global_refs() {
898 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
899 nitni_global_ref_list->head = NULL;
900 nitni_global_ref_list->tail = NULL;
901 }
902
903 void nitni_global_ref_add( struct nitni_ref *ref ) {
904 if ( nitni_global_ref_list->head == NULL ) {
905 nitni_global_ref_list->head = ref;
906 ref->prev = NULL;
907 } else {
908 nitni_global_ref_list->tail->next = ref;
909 ref->prev = nitni_global_ref_list->tail;
910 }
911 nitni_global_ref_list->tail = ref;
912
913 ref->next = NULL;
914 }
915
916 void nitni_global_ref_remove( struct nitni_ref *ref ) {
917 if ( ref->prev == NULL ) {
918 nitni_global_ref_list->head = ref->next;
919 } else {
920 ref->prev->next = ref->next;
921 }
922
923 if ( ref->next == NULL ) {
924 nitni_global_ref_list->tail = ref->prev;
925 } else {
926 ref->next->prev = ref->prev;
927 }
928 }
929
930 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
931 if ( ref->count == 0 ) /* not registered */
932 {
933 /* add to list */
934 nitni_global_ref_add( ref );
935 }
936
937 ref->count ++;
938 }
939
940 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
941 if ( ref->count == 1 ) /* was last reference */
942 {
943 /* remove from list */
944 nitni_global_ref_remove( ref );
945 }
946
947 ref->count --;
948 }
949 """
950 end
951
952 # List of additional files required to compile (FFI)
953 var extern_bodies = new Array[ExternFile]
954
955 # List of source files to copy over to the compile dir
956 var files_to_copy = new Array[String]
957
958 # This is used to avoid adding an extern file more than once
959 private var seen_extern = new ArraySet[String]
960
961 # Generate code that initialize the attributes on a new instance
962 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
963 do
964 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
965 self.mainmodule.linearize_mclassdefs(cds)
966 for cd in cds do
967 for npropdef in modelbuilder.collect_attr_propdef(cd) do
968 npropdef.init_expr(v, recv)
969 end
970 end
971 end
972
973 # Generate code that check if an attribute is correctly initialized
974 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
975 do
976 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
977 self.mainmodule.linearize_mclassdefs(cds)
978 for cd in cds do
979 for npropdef in modelbuilder.collect_attr_propdef(cd) do
980 npropdef.check_expr(v, recv)
981 end
982 end
983 end
984
985 # stats
986
987 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
988 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
989 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
990 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
991
992 protected fun init_count_type_test_tags: HashMap[String, Int]
993 do
994 var res = new HashMap[String, Int]
995 for tag in count_type_test_tags do
996 res[tag] = 0
997 end
998 return res
999 end
1000
1001 # Display stats about compilation process
1002 #
1003 # Metrics used:
1004 #
1005 # * type tests against resolved types (`x isa Collection[Animal]`)
1006 # * type tests against unresolved types (`x isa Collection[E]`)
1007 # * type tests skipped
1008 # * type tests total
1009 fun display_stats
1010 do
1011 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1012 print "# static count_type_test"
1013 print "\tresolved:\tunresolved\tskipped\ttotal"
1014 var count_type_test_total = init_count_type_test_tags
1015 count_type_test_resolved["total"] = 0
1016 count_type_test_unresolved["total"] = 0
1017 count_type_test_skipped["total"] = 0
1018 count_type_test_total["total"] = 0
1019 for tag in count_type_test_tags do
1020 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1021 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1022 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1023 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1024 count_type_test_total["total"] += count_type_test_total[tag]
1025 end
1026 var count_type_test = count_type_test_total["total"]
1027 var tags = count_type_test_tags.to_a
1028 tags.add("total")
1029 for tag in tags do
1030 printn tag
1031 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1032 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1033 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1034 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1035 print ""
1036 end
1037 end
1038 end
1039
1040 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1041 end
1042
1043 # A file unit (may be more than one file if
1044 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1045 class CodeFile
1046 var name: String
1047 var writers = new Array[CodeWriter]
1048 var required_declarations = new HashSet[String]
1049 end
1050
1051 # Where to store generated lines
1052 class CodeWriter
1053 var file: CodeFile
1054 var lines: List[String] = new List[String]
1055 var decl_lines: List[String] = new List[String]
1056
1057 # Add a line in the main part of the generated C
1058 fun add(s: String) do self.lines.add(s)
1059
1060 # Add a line in the
1061 # (used for local or global declaration)
1062 fun add_decl(s: String) do self.decl_lines.add(s)
1063
1064 init
1065 do
1066 file.writers.add(self)
1067 end
1068 end
1069
1070 # A visitor on the AST of property definition that generate the C code.
1071 abstract class AbstractCompilerVisitor
1072
1073 type COMPILER: AbstractCompiler
1074
1075 # The associated compiler
1076 var compiler: COMPILER
1077
1078 # The current visited AST node
1079 var current_node: nullable ANode = null is writable
1080
1081 # The current `StaticFrame`
1082 var frame: nullable StaticFrame = null is writable
1083
1084 # Alias for self.compiler.mainmodule.object_type
1085 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1086
1087 # Alias for self.compiler.mainmodule.bool_type
1088 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1089
1090 var writer: CodeWriter is noinit
1091
1092 init
1093 do
1094 self.writer = new CodeWriter(compiler.files.last)
1095 end
1096
1097 # Force to get the primitive property named `name` in the instance `recv` or abort
1098 fun get_property(name: String, recv: MType): MMethod
1099 do
1100 assert recv isa MClassType
1101 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1102 end
1103
1104 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1105 do
1106 var initializers = callsite.mpropdef.initializers
1107 if not initializers.is_empty then
1108 var recv = arguments.first
1109
1110 var i = 1
1111 for p in initializers do
1112 if p isa MMethod then
1113 var args = [recv]
1114 for x in p.intro.msignature.mparameters do
1115 args.add arguments[i]
1116 i += 1
1117 end
1118 self.send(p, args)
1119 else if p isa MAttribute then
1120 self.write_attribute(p, recv, arguments[i])
1121 i += 1
1122 else abort
1123 end
1124 assert i == arguments.length
1125
1126 return self.send(callsite.mproperty, [recv])
1127 end
1128
1129 return self.send(callsite.mproperty, arguments)
1130 end
1131
1132 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1133
1134 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1135
1136 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1137
1138 # Return an element of a native array.
1139 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1140 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1141
1142 # Store an element in a native array.
1143 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1144 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1145
1146 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1147 # This method is used to manage varargs in signatures and returns the real array
1148 # of runtime variables to use in the call.
1149 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1150 do
1151 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1152 var res = new Array[RuntimeVariable]
1153 res.add(recv)
1154
1155 if msignature.arity == 0 then return res
1156
1157 if map == null then
1158 assert args.length == msignature.arity
1159 for ne in args do
1160 res.add self.expr(ne, null)
1161 end
1162 return res
1163 end
1164
1165 # Eval in order of arguments, not parameters
1166 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1167 for ne in args do
1168 exprs.add self.expr(ne, null)
1169 end
1170
1171 # Fill `res` with the result of the evaluation according to the mapping
1172 for i in [0..msignature.arity[ do
1173 var param = msignature.mparameters[i]
1174 var j = map.map.get_or_null(i)
1175 if j == null then
1176 # default value
1177 res.add(null_instance)
1178 continue
1179 end
1180 if param.is_vararg and map.vararg_decl > 0 then
1181 var vararg = exprs.sub(j, map.vararg_decl)
1182 var elttype = param.mtype
1183 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1184 res.add(arg)
1185 continue
1186 end
1187 res.add exprs[j]
1188 end
1189 return res
1190 end
1191
1192 # Type handling
1193
1194 # Anchor a type to the main module and the current receiver
1195 fun anchor(mtype: MType): MType
1196 do
1197 if not mtype.need_anchor then return mtype
1198 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1199 end
1200
1201 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1202 do
1203 if not mtype.need_anchor then return mtype
1204 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1205 end
1206
1207 # Unsafely cast a value to a new type
1208 # ie the result share the same C variable but my have a different mcasttype
1209 # NOTE: if the adaptation is useless then `value` is returned as it.
1210 # ENSURE: `result.name == value.name`
1211 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1212 do
1213 mtype = self.anchor(mtype)
1214 var valmtype = value.mcasttype
1215 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1216 return value
1217 end
1218
1219 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1220 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1221 return res
1222 else
1223 var res = new RuntimeVariable(value.name, valmtype, mtype)
1224 return res
1225 end
1226 end
1227
1228 # Generate a super call from a method definition
1229 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1230
1231 # Adapt the arguments of a method according to targetted `MMethodDef`
1232 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1233
1234 # Unbox all the arguments of a method when implemented `extern` or `intern`
1235 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1236
1237 # Box or unbox a value to another type iff a C type conversion is needed
1238 # ENSURE: `result.mtype.ctype == mtype.ctype`
1239 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1240
1241 # Box extern classes to be used in the generated code
1242 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1243
1244 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1245 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1246
1247 # Generate a polymorphic subtype test
1248 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1249
1250 # Generate the code required to dynamically check if 2 objects share the same runtime type
1251 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1252
1253 # Generate a Nit "is" for two runtime_variables
1254 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1255
1256 # Sends
1257
1258 # Generate a static call on a method definition
1259 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1260
1261 # Generate a polymorphic send for the method `m` and the arguments `args`
1262 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1263
1264 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1265 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1266 do
1267 assert t isa MClassType
1268 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1269 return self.call(propdef, t, args)
1270 end
1271
1272 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1273 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1274 do
1275 assert t isa MClassType
1276 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1277 return self.call(m, t, args)
1278 end
1279
1280 # Attributes handling
1281
1282 # Generate a polymorphic attribute is_set test
1283 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1284
1285 # Generate a polymorphic attribute read
1286 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1287
1288 # Generate a polymorphic attribute write
1289 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1290
1291 # Checks
1292
1293 # Add a check and an abort for a null receiver if needed
1294 fun check_recv_notnull(recv: RuntimeVariable)
1295 do
1296 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1297
1298 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1299 if maybenull then
1300 self.add("if (unlikely({recv} == NULL)) \{")
1301 self.add_abort("Receiver is null")
1302 self.add("\}")
1303 end
1304 end
1305
1306 # Names handling
1307
1308 private var names = new HashSet[String]
1309 private var last: Int = 0
1310
1311 # Return a new name based on `s` and unique in the visitor
1312 fun get_name(s: String): String
1313 do
1314 if not self.names.has(s) then
1315 self.names.add(s)
1316 return s
1317 end
1318 var i = self.last + 1
1319 loop
1320 var s2 = s + i.to_s
1321 if not self.names.has(s2) then
1322 self.last = i
1323 self.names.add(s2)
1324 return s2
1325 end
1326 i = i + 1
1327 end
1328 end
1329
1330 # Return an unique and stable identifier associated with an escapemark
1331 fun escapemark_name(e: nullable EscapeMark): String
1332 do
1333 assert e != null
1334 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1335 var name = e.name
1336 if name == null then name = "label"
1337 name = get_name(name)
1338 frame.escapemark_names[e] = name
1339 return name
1340 end
1341
1342 # Insert a C label for associated with an escapemark
1343 fun add_escape_label(e: nullable EscapeMark)
1344 do
1345 if e == null then return
1346 if e.escapes.is_empty then return
1347 add("BREAK_{escapemark_name(e)}: (void)0;")
1348 end
1349
1350 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1351 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1352 fun class_name_string(value: RuntimeVariable): String is abstract
1353
1354 # Variables handling
1355
1356 protected var variables = new HashMap[Variable, RuntimeVariable]
1357
1358 # Return the local runtime_variable associated to a Nit local variable
1359 fun variable(variable: Variable): RuntimeVariable
1360 do
1361 if self.variables.has_key(variable) then
1362 return self.variables[variable]
1363 else
1364 var name = self.get_name("var_{variable.name}")
1365 var mtype = variable.declared_type.as(not null)
1366 mtype = self.anchor(mtype)
1367 var res = new RuntimeVariable(name, mtype, mtype)
1368 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1369 self.variables[variable] = res
1370 return res
1371 end
1372 end
1373
1374 # Return a new uninitialized local runtime_variable
1375 fun new_var(mtype: MType): RuntimeVariable
1376 do
1377 mtype = self.anchor(mtype)
1378 var name = self.get_name("var")
1379 var res = new RuntimeVariable(name, mtype, mtype)
1380 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1381 return res
1382 end
1383
1384 # The difference with `new_var` is the C static type of the local variable
1385 fun new_var_extern(mtype: MType): RuntimeVariable
1386 do
1387 mtype = self.anchor(mtype)
1388 var name = self.get_name("var")
1389 var res = new RuntimeVariable(name, mtype, mtype)
1390 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1391 return res
1392 end
1393
1394 # Return a new uninitialized named runtime_variable
1395 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1396 do
1397 mtype = self.anchor(mtype)
1398 var res = new RuntimeVariable(name, mtype, mtype)
1399 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1400 return res
1401 end
1402
1403 # Correctly assign a left and a right value
1404 # Boxing and unboxing is performed if required
1405 fun assign(left, right: RuntimeVariable)
1406 do
1407 right = self.autobox(right, left.mtype)
1408 self.add("{left} = {right};")
1409 end
1410
1411 # Generate instances
1412
1413 # Generate a alloc-instance + init-attributes
1414 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1415
1416 # Allocate and init attributes of an instance of a standard or extern class
1417 #
1418 # Does not support universals and the pseudo-internal `NativeArray` class.
1419 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1420 do
1421 var recv
1422 var ctype = mtype.ctype
1423 assert mtype.mclass.name != "NativeArray"
1424 if not mtype.is_c_primitive then
1425 recv = init_instance(mtype)
1426 else if ctype == "char*" then
1427 recv = new_expr("NULL/*special!*/", mtype)
1428 else
1429 recv = new_expr("({ctype})0/*special!*/", mtype)
1430 end
1431 return recv
1432 end
1433
1434 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1435 fun set_finalizer(recv: RuntimeVariable)
1436 do
1437 var mtype = recv.mtype
1438 var finalizable_type = compiler.mainmodule.finalizable_type
1439 if finalizable_type != null and not mtype.need_anchor and
1440 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1441 add "gc_register_finalizer({recv});"
1442 end
1443 end
1444
1445 # The currently processed module
1446 #
1447 # alias for `compiler.mainmodule`
1448 fun mmodule: MModule do return compiler.mainmodule
1449
1450 # Generate an integer value
1451 fun int_instance(value: Int): RuntimeVariable
1452 do
1453 var t = mmodule.int_type
1454 var res = new RuntimeVariable("{value.to_s}l", t, t)
1455 return res
1456 end
1457
1458 # Generate a char value
1459 fun char_instance(value: Char): RuntimeVariable
1460 do
1461 var t = mmodule.char_type
1462 var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1463 return res
1464 end
1465
1466 # Generate a float value
1467 #
1468 # FIXME pass a Float, not a string
1469 fun float_instance(value: String): RuntimeVariable
1470 do
1471 var t = mmodule.float_type
1472 var res = new RuntimeVariable("{value}", t, t)
1473 return res
1474 end
1475
1476 # Generate an integer value
1477 fun bool_instance(value: Bool): RuntimeVariable
1478 do
1479 var s = if value then "1" else "0"
1480 var res = new RuntimeVariable(s, bool_type, bool_type)
1481 return res
1482 end
1483
1484 # Generate the `null` value
1485 fun null_instance: RuntimeVariable
1486 do
1487 var t = compiler.mainmodule.model.null_type
1488 var res = new RuntimeVariable("((val*)NULL)", t, t)
1489 return res
1490 end
1491
1492 # Generate a string value
1493 fun string_instance(string: String): RuntimeVariable
1494 do
1495 var mtype = mmodule.string_type
1496 var name = self.get_name("varonce")
1497 self.add_decl("static {mtype.ctype} {name};")
1498 var res = self.new_var(mtype)
1499 self.add("if (likely({name}!=NULL)) \{")
1500 self.add("{res} = {name};")
1501 self.add("\} else \{")
1502 var native_mtype = mmodule.native_string_type
1503 var nat = self.new_var(native_mtype)
1504 self.add("{nat} = \"{string.escape_to_c}\";")
1505 var length = self.int_instance(string.length)
1506 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1507 self.add("{name} = {res};")
1508 self.add("\}")
1509 return res
1510 end
1511
1512 fun value_instance(object: Object): RuntimeVariable
1513 do
1514 if object isa Int then
1515 return int_instance(object)
1516 else if object isa Bool then
1517 return bool_instance(object)
1518 else if object isa String then
1519 return string_instance(object)
1520 else
1521 abort
1522 end
1523 end
1524
1525 # Generate an array value
1526 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1527
1528 # Get an instance of a array for a vararg
1529 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1530
1531 # Code generation
1532
1533 # Add a line in the main part of the generated C
1534 fun add(s: String) do self.writer.lines.add(s)
1535
1536 # Add a line in the
1537 # (used for local or global declaration)
1538 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1539
1540 # Request the presence of a global declaration
1541 fun require_declaration(key: String)
1542 do
1543 var reqs = self.writer.file.required_declarations
1544 if reqs.has(key) then return
1545 reqs.add(key)
1546 var node = current_node
1547 if node != null then compiler.requirers_of_declarations[key] = node
1548 end
1549
1550 # Add a declaration in the local-header
1551 # The declaration is ensured to be present once
1552 fun declare_once(s: String)
1553 do
1554 self.compiler.provide_declaration(s, s)
1555 self.require_declaration(s)
1556 end
1557
1558 # Look for a needed .h and .c file for a given module
1559 # This is used for the legacy FFI
1560 fun add_extern(mmodule: MModule)
1561 do
1562 var file = mmodule.location.file.filename
1563 file = file.strip_extension(".nit")
1564 var tryfile = file + ".nit.h"
1565 if tryfile.file_exists then
1566 self.declare_once("#include \"{tryfile.basename("")}\"")
1567 self.compiler.files_to_copy.add(tryfile)
1568 end
1569 tryfile = file + "_nit.h"
1570 if tryfile.file_exists then
1571 self.declare_once("#include \"{tryfile.basename("")}\"")
1572 self.compiler.files_to_copy.add(tryfile)
1573 end
1574
1575 if self.compiler.seen_extern.has(file) then return
1576 self.compiler.seen_extern.add(file)
1577 tryfile = file + ".nit.c"
1578 if not tryfile.file_exists then
1579 tryfile = file + "_nit.c"
1580 if not tryfile.file_exists then return
1581 end
1582 var f = new ExternCFile(tryfile.basename(""), "")
1583 self.compiler.extern_bodies.add(f)
1584 self.compiler.files_to_copy.add(tryfile)
1585 end
1586
1587 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1588 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1589 do
1590 var res = new_var(mtype)
1591 self.add("{res} = {cexpr};")
1592 return res
1593 end
1594
1595 # Generate generic abort
1596 # used by aborts, asserts, casts, etc.
1597 fun add_abort(message: String)
1598 do
1599 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1600 add_raw_abort
1601 end
1602
1603 fun add_raw_abort
1604 do
1605 if self.current_node != null and self.current_node.location.file != null and
1606 self.current_node.location.file.mmodule != null then
1607 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1608 self.require_declaration(f)
1609 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1610 else
1611 self.add("PRINT_ERROR(\"\\n\");")
1612 end
1613 self.add("fatal_exit(1);")
1614 end
1615
1616 # Add a dynamic cast
1617 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1618 do
1619 var res = self.type_test(value, mtype, tag)
1620 self.add("if (unlikely(!{res})) \{")
1621 var cn = self.class_name_string(value)
1622 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1623 self.add_raw_abort
1624 self.add("\}")
1625 end
1626
1627 # Generate a return with the value `s`
1628 fun ret(s: RuntimeVariable)
1629 do
1630 self.assign(self.frame.returnvar.as(not null), s)
1631 self.add("goto {self.frame.returnlabel.as(not null)};")
1632 end
1633
1634 # Compile a statement (if any)
1635 fun stmt(nexpr: nullable AExpr)
1636 do
1637 if nexpr == null then return
1638 if nexpr.mtype == null and not nexpr.is_typed then
1639 # Untyped expression.
1640 # Might mean dead code or invalid code
1641 # so aborts
1642 add_abort("FATAL: bad statement executed.")
1643 return
1644 end
1645
1646 var narray = nexpr.comprehension
1647 if narray != null then
1648 var recv = frame.comprehension.as(not null)
1649 var val = expr(nexpr, narray.element_mtype)
1650 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1651 return
1652 end
1653
1654 var old = self.current_node
1655 self.current_node = nexpr
1656 nexpr.stmt(self)
1657 self.current_node = old
1658 end
1659
1660 # Compile an expression an return its result
1661 # `mtype` is the expected return type, pass null if no specific type is expected.
1662 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1663 do
1664 if nexpr.mtype == null then
1665 # Untyped expression.
1666 # Might mean dead code or invalid code.
1667 # so aborts
1668 add_abort("FATAL: bad expression executed.")
1669 # and return a placebo result to please the C compiler
1670 if mtype == null then mtype = compiler.mainmodule.object_type
1671 return new_var(mtype)
1672 end
1673 var old = self.current_node
1674 self.current_node = nexpr
1675 var res = nexpr.expr(self).as(not null)
1676 if mtype != null then
1677 mtype = self.anchor(mtype)
1678 res = self.autobox(res, mtype)
1679 end
1680 res = autoadapt(res, nexpr.mtype.as(not null))
1681 var implicit_cast_to = nexpr.implicit_cast_to
1682 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1683 add_cast(res, implicit_cast_to, "auto")
1684 res = autoadapt(res, implicit_cast_to)
1685 end
1686 self.current_node = old
1687 return res
1688 end
1689
1690 # Alias for `self.expr(nexpr, self.bool_type)`
1691 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1692
1693 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1694 fun debug(message: String)
1695 do
1696 var node = self.current_node
1697 if node == null then
1698 print "?: {message}"
1699 else
1700 node.debug(message)
1701 end
1702 self.add("/* DEBUG: {message} */")
1703 end
1704 end
1705
1706 # A C function associated to a Nit method
1707 # Because of customization, a given Nit method can be compiler more that once
1708 abstract class AbstractRuntimeFunction
1709
1710 type COMPILER: AbstractCompiler
1711 type VISITOR: AbstractCompilerVisitor
1712
1713 # The associated Nit method
1714 var mmethoddef: MMethodDef
1715
1716 # The mangled c name of the runtime_function
1717 # Subclasses should redefine `build_c_name` instead
1718 fun c_name: String
1719 do
1720 var res = self.c_name_cache
1721 if res != null then return res
1722 res = self.build_c_name
1723 self.c_name_cache = res
1724 return res
1725 end
1726
1727 # Non cached version of `c_name`
1728 protected fun build_c_name: String is abstract
1729
1730 protected var c_name_cache: nullable String = null is writable
1731
1732 # Implements a call of the runtime_function
1733 # May inline the body or generate a C function call
1734 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1735
1736 # Generate the code for the `AbstractRuntimeFunction`
1737 # Warning: compile more than once compilation makes CC unhappy
1738 fun compile_to_c(compiler: COMPILER) is abstract
1739 end
1740
1741 # A runtime variable hold a runtime value in C.
1742 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1743 #
1744 # 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.
1745 class RuntimeVariable
1746 # The name of the variable in the C code
1747 var name: String
1748
1749 # The static type of the variable (as declard in C)
1750 var mtype: MType
1751
1752 # The current casted type of the variable (as known in Nit)
1753 var mcasttype: MType is writable
1754
1755 # If the variable exaclty a mcasttype?
1756 # false (usual value) means that the variable is a mcasttype or a subtype.
1757 var is_exact: Bool = false is writable
1758
1759 init
1760 do
1761 assert not mtype.need_anchor
1762 assert not mcasttype.need_anchor
1763 end
1764
1765 redef fun to_s do return name
1766
1767 redef fun inspect
1768 do
1769 var exact_str
1770 if self.is_exact then
1771 exact_str = " exact"
1772 else
1773 exact_str = ""
1774 end
1775 var type_str
1776 if self.mtype == self.mcasttype then
1777 type_str = "{mtype}{exact_str}"
1778 else
1779 type_str = "{mtype}({mcasttype}{exact_str})"
1780 end
1781 return "<{name}:{type_str}>"
1782 end
1783 end
1784
1785 # The static context of a visited property in a `AbstractCompilerVisitor`
1786 class StaticFrame
1787
1788 type VISITOR: AbstractCompilerVisitor
1789
1790 # The associated visitor
1791 var visitor: VISITOR
1792
1793 # The executed property.
1794 # A Method in case of a call, an attribute in case of a default initialization.
1795 var mpropdef: MPropDef
1796
1797 # The static type of the receiver
1798 var receiver: MClassType
1799
1800 # Arguments of the method (the first is the receiver)
1801 var arguments: Array[RuntimeVariable]
1802
1803 # The runtime_variable associated to the return (in a function)
1804 var returnvar: nullable RuntimeVariable = null is writable
1805
1806 # The label at the end of the property
1807 var returnlabel: nullable String = null is writable
1808
1809 # Labels associated to a each escapemarks.
1810 # Because of inlinings, escape-marks must be associated to their context (the frame)
1811 private var escapemark_names = new HashMap[EscapeMark, String]
1812
1813 # The array comprehension currently filled, if any
1814 private var comprehension: nullable RuntimeVariable = null
1815 end
1816
1817 redef class MType
1818 # Return the C type associated to a given Nit static type
1819 fun ctype: String do return "val*"
1820
1821 # C type outside of the compiler code and in boxes
1822 fun ctype_extern: String do return "val*"
1823
1824 # Short name of the `ctype` to use in unions
1825 fun ctypename: String do return "val"
1826
1827 # Is the associated C type a primitive one?
1828 #
1829 # ENSURE `result == (ctype != "val*")`
1830 fun is_c_primitive: Bool do return false
1831 end
1832
1833 redef class MClassType
1834
1835 redef var ctype is lazy do
1836 if mclass.name == "Int" then
1837 return "long"
1838 else if mclass.name == "Bool" then
1839 return "short int"
1840 else if mclass.name == "Char" then
1841 return "char"
1842 else if mclass.name == "Float" then
1843 return "double"
1844 else if mclass.name == "NativeString" then
1845 return "char*"
1846 else if mclass.name == "NativeArray" then
1847 return "val*"
1848 else
1849 return "val*"
1850 end
1851 end
1852
1853 redef var is_c_primitive is lazy do return ctype != "val*"
1854
1855 redef fun ctype_extern: String
1856 do
1857 if mclass.kind == extern_kind then
1858 return "void*"
1859 else
1860 return ctype
1861 end
1862 end
1863
1864 redef fun ctypename: String
1865 do
1866 if mclass.name == "Int" then
1867 return "l"
1868 else if mclass.name == "Bool" then
1869 return "s"
1870 else if mclass.name == "Char" then
1871 return "c"
1872 else if mclass.name == "Float" then
1873 return "d"
1874 else if mclass.name == "NativeString" then
1875 return "str"
1876 else if mclass.name == "NativeArray" then
1877 #return "{self.arguments.first.ctype}*"
1878 return "val"
1879 else
1880 return "val"
1881 end
1882 end
1883 end
1884
1885 redef class MPropDef
1886 type VISITOR: AbstractCompilerVisitor
1887 end
1888
1889 redef class MMethodDef
1890 # Can the body be inlined?
1891 fun can_inline(v: VISITOR): Bool
1892 do
1893 if is_abstract then return true
1894 var modelbuilder = v.compiler.modelbuilder
1895 var node = modelbuilder.mpropdef2node(self)
1896 if node isa APropdef then
1897 return node.can_inline
1898 else if node isa AClassdef then
1899 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1900 return true
1901 else
1902 abort
1903 end
1904 end
1905
1906 # Inline the body in another visitor
1907 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1908 do
1909 var modelbuilder = v.compiler.modelbuilder
1910 var val = constant_value
1911 var node = modelbuilder.mpropdef2node(self)
1912
1913 if is_abstract then
1914 var cn = v.class_name_string(arguments.first)
1915 v.current_node = node
1916 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1917 v.add_raw_abort
1918 return null
1919 end
1920
1921 if node isa APropdef then
1922 var oldnode = v.current_node
1923 v.current_node = node
1924 self.compile_parameter_check(v, arguments)
1925 node.compile_to_c(v, self, arguments)
1926 v.current_node = oldnode
1927 else if node isa AClassdef then
1928 var oldnode = v.current_node
1929 v.current_node = node
1930 self.compile_parameter_check(v, arguments)
1931 node.compile_to_c(v, self, arguments)
1932 v.current_node = oldnode
1933 else if val != null then
1934 v.ret(v.value_instance(val))
1935 else
1936 abort
1937 end
1938 return null
1939 end
1940
1941 # Generate type checks in the C code to check covariant parameters
1942 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1943 do
1944 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1945
1946 for i in [0..msignature.arity[ do
1947 # skip test for vararg since the array is instantiated with the correct polymorphic type
1948 if msignature.vararg_rank == i then continue
1949
1950 # skip if the cast is not required
1951 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1952 if not origmtype.need_anchor then continue
1953
1954 # get the parameter type
1955 var mtype = self.msignature.mparameters[i].mtype
1956
1957 # generate the cast
1958 # note that v decides if and how to implements the cast
1959 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1960 v.add_cast(arguments[i+1], mtype, "covariance")
1961 end
1962 end
1963 end
1964
1965 # Node visit
1966
1967 redef class APropdef
1968 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1969 do
1970 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1971 debug("Not yet implemented")
1972 end
1973
1974 fun can_inline: Bool do return true
1975 end
1976
1977 redef class AMethPropdef
1978 redef fun compile_to_c(v, mpropdef, arguments)
1979 do
1980 # Call the implicit super-init
1981 var auto_super_inits = self.auto_super_inits
1982 if auto_super_inits != null then
1983 var args = [arguments.first]
1984 for auto_super_init in auto_super_inits do
1985 assert auto_super_init.mproperty != mpropdef.mproperty
1986 args.clear
1987 for i in [0..auto_super_init.msignature.arity+1[ do
1988 args.add(arguments[i])
1989 end
1990 assert auto_super_init.mproperty != mpropdef.mproperty
1991 v.compile_callsite(auto_super_init, args)
1992 end
1993 end
1994 if auto_super_call then
1995 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1996 end
1997
1998 # Try special compilation
1999 if mpropdef.is_intern then
2000 if compile_intern_to_c(v, mpropdef, arguments) then return
2001 else if mpropdef.is_extern then
2002 if mpropdef.mproperty.is_init then
2003 if compile_externinit_to_c(v, mpropdef, arguments) then return
2004 else
2005 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2006 end
2007 end
2008
2009 # Compile block if any
2010 var n_block = n_block
2011 if n_block != null then
2012 for i in [0..mpropdef.msignature.arity[ do
2013 var variable = self.n_signature.n_params[i].variable.as(not null)
2014 v.assign(v.variable(variable), arguments[i+1])
2015 end
2016 v.stmt(n_block)
2017 return
2018 end
2019
2020 # We have a problem
2021 var cn = v.class_name_string(arguments.first)
2022 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2023 v.add_raw_abort
2024 end
2025
2026 redef fun can_inline
2027 do
2028 if self.auto_super_inits != null then return false
2029 var nblock = self.n_block
2030 if nblock == null then return true
2031 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2032 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2033 return false
2034 end
2035
2036 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2037 do
2038 var pname = mpropdef.mproperty.name
2039 var cname = mpropdef.mclassdef.mclass.name
2040 var ret = mpropdef.msignature.return_mtype
2041 if ret != null then
2042 ret = v.resolve_for(ret, arguments.first)
2043 end
2044 if pname != "==" and pname != "!=" then
2045 v.adapt_signature(mpropdef, arguments)
2046 v.unbox_signature_extern(mpropdef, arguments)
2047 end
2048 if cname == "Int" then
2049 if pname == "output" then
2050 v.add("printf(\"%ld\\n\", {arguments.first});")
2051 return true
2052 else if pname == "object_id" then
2053 v.ret(arguments.first)
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 == "unary -" then
2062 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2063 return true
2064 else if pname == "unary +" then
2065 v.ret(arguments[0])
2066 return true
2067 else if pname == "*" then
2068 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2069 return true
2070 else if pname == "/" then
2071 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2072 return true
2073 else if pname == "%" then
2074 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2075 return true
2076 else if pname == "lshift" then
2077 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2078 return true
2079 else if pname == "rshift" then
2080 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2081 return true
2082 else if pname == "==" then
2083 v.ret(v.equal_test(arguments[0], arguments[1]))
2084 return true
2085 else if pname == "!=" then
2086 var res = v.equal_test(arguments[0], arguments[1])
2087 v.ret(v.new_expr("!{res}", ret.as(not null)))
2088 return true
2089 else if pname == "<" then
2090 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2091 return true
2092 else if pname == ">" then
2093 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2094 return true
2095 else if pname == "<=" then
2096 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2097 return true
2098 else if pname == ">=" then
2099 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2100 return true
2101 else if pname == "to_f" then
2102 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2103 return true
2104 else if pname == "ascii" then
2105 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2106 return true
2107 end
2108 else if cname == "Char" then
2109 if pname == "output" then
2110 v.add("printf(\"%c\", {arguments.first});")
2111 return true
2112 else if pname == "object_id" then
2113 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2114 return true
2115 else if pname == "successor" then
2116 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2117 return true
2118 else if pname == "predecessor" then
2119 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2120 return true
2121 else if pname == "==" then
2122 v.ret(v.equal_test(arguments[0], arguments[1]))
2123 return true
2124 else if pname == "!=" then
2125 var res = v.equal_test(arguments[0], arguments[1])
2126 v.ret(v.new_expr("!{res}", ret.as(not null)))
2127 return true
2128 else if pname == "<" then
2129 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2130 return true
2131 else if pname == ">" then
2132 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2133 return true
2134 else if pname == "<=" then
2135 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2136 return true
2137 else if pname == ">=" then
2138 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2139 return true
2140 else if pname == "to_i" then
2141 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2142 return true
2143 else if pname == "ascii" then
2144 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2145 return true
2146 end
2147 else if cname == "Bool" then
2148 if pname == "output" then
2149 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2150 return true
2151 else if pname == "object_id" then
2152 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2153 return true
2154 else if pname == "==" then
2155 v.ret(v.equal_test(arguments[0], arguments[1]))
2156 return true
2157 else if pname == "!=" then
2158 var res = v.equal_test(arguments[0], arguments[1])
2159 v.ret(v.new_expr("!{res}", ret.as(not null)))
2160 return true
2161 end
2162 else if cname == "Float" then
2163 if pname == "output" then
2164 v.add("printf(\"%f\\n\", {arguments.first});")
2165 return true
2166 else if pname == "object_id" then
2167 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2168 return true
2169 else if pname == "+" then
2170 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2171 return true
2172 else if pname == "-" then
2173 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2174 return true
2175 else if pname == "unary -" then
2176 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2177 return true
2178 else if pname == "unary +" then
2179 v.ret(arguments[0])
2180 return true
2181 else if pname == "succ" then
2182 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2183 return true
2184 else if pname == "prec" then
2185 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2186 return true
2187 else if pname == "*" then
2188 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2189 return true
2190 else if pname == "/" then
2191 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2192 return true
2193 else if pname == "==" then
2194 v.ret(v.equal_test(arguments[0], arguments[1]))
2195 return true
2196 else if pname == "!=" then
2197 var res = v.equal_test(arguments[0], arguments[1])
2198 v.ret(v.new_expr("!{res}", ret.as(not null)))
2199 return true
2200 else if pname == "<" then
2201 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2202 return true
2203 else if pname == ">" then
2204 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2205 return true
2206 else if pname == "<=" then
2207 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2208 return true
2209 else if pname == ">=" then
2210 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2211 return true
2212 else if pname == "to_i" then
2213 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2214 return true
2215 end
2216 else if cname == "NativeString" then
2217 if pname == "[]" then
2218 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2219 return true
2220 else if pname == "[]=" then
2221 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2222 return true
2223 else if pname == "copy_to" then
2224 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2225 return true
2226 else if pname == "atoi" then
2227 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2228 return true
2229 else if pname == "fast_cstring" then
2230 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2231 return true
2232 else if pname == "new" then
2233 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2234 return true
2235 end
2236 else if cname == "NativeArray" then
2237 v.native_array_def(pname, ret, arguments)
2238 return true
2239 end
2240 if pname == "exit" then
2241 v.add("exit({arguments[1]});")
2242 return true
2243 else if pname == "sys" then
2244 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2245 return true
2246 else if pname == "calloc_string" then
2247 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2248 return true
2249 else if pname == "calloc_array" then
2250 v.calloc_array(ret.as(not null), arguments)
2251 return true
2252 else if pname == "object_id" then
2253 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2254 return true
2255 else if pname == "is_same_type" then
2256 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2257 return true
2258 else if pname == "is_same_instance" then
2259 v.ret(v.equal_test(arguments[0], arguments[1]))
2260 return true
2261 else if pname == "output_class_name" then
2262 var nat = v.class_name_string(arguments.first)
2263 v.add("printf(\"%s\\n\", {nat});")
2264 return true
2265 else if pname == "native_class_name" then
2266 var nat = v.class_name_string(arguments.first)
2267 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2268 return true
2269 else if pname == "force_garbage_collection" then
2270 v.add("nit_gcollect();")
2271 return true
2272 else if pname == "native_argc" then
2273 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2274 return true
2275 else if pname == "native_argv" then
2276 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2277 return true
2278 end
2279 return false
2280 end
2281
2282 # Compile an extern method
2283 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2284 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2285 do
2286 var externname
2287 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2288 if at != null and at.n_args.length == 1 then
2289 externname = at.arg_as_string(v.compiler.modelbuilder)
2290 if externname == null then return false
2291 else
2292 return false
2293 end
2294 v.add_extern(mpropdef.mclassdef.mmodule)
2295 var res: nullable RuntimeVariable = null
2296 var ret = mpropdef.msignature.return_mtype
2297 if ret != null then
2298 ret = v.resolve_for(ret, arguments.first)
2299 res = v.new_var_extern(ret)
2300 end
2301 v.adapt_signature(mpropdef, arguments)
2302 v.unbox_signature_extern(mpropdef, arguments)
2303
2304 if res == null then
2305 v.add("{externname}({arguments.join(", ")});")
2306 else
2307 v.add("{res} = {externname}({arguments.join(", ")});")
2308 res = v.box_extern(res, ret.as(not null))
2309 v.ret(res)
2310 end
2311 return true
2312 end
2313
2314 # Compile an extern factory
2315 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2316 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2317 do
2318 var externname
2319 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2320 if at != null then
2321 externname = at.arg_as_string(v.compiler.modelbuilder)
2322 if externname == null then return false
2323 else
2324 return false
2325 end
2326 v.add_extern(mpropdef.mclassdef.mmodule)
2327 v.adapt_signature(mpropdef, arguments)
2328 v.unbox_signature_extern(mpropdef, arguments)
2329 var ret = arguments.first.mtype
2330 var res = v.new_var_extern(ret)
2331
2332 arguments.shift
2333
2334 v.add("{res} = {externname}({arguments.join(", ")});")
2335 res = v.box_extern(res, ret)
2336 v.ret(res)
2337 return true
2338 end
2339 end
2340
2341 redef class AAttrPropdef
2342 redef fun can_inline: Bool do return not is_lazy
2343
2344 redef fun compile_to_c(v, mpropdef, arguments)
2345 do
2346 if mpropdef == mreadpropdef then
2347 assert arguments.length == 1
2348 var recv = arguments.first
2349 var res
2350 if is_lazy then
2351 var set
2352 var ret = self.mpropdef.static_mtype
2353 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2354 var guard = self.mlazypropdef.mproperty
2355 if useiset then
2356 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2357 else
2358 set = v.read_attribute(guard, recv)
2359 end
2360 v.add("if(likely({set})) \{")
2361 res = v.read_attribute(self.mpropdef.mproperty, recv)
2362 v.add("\} else \{")
2363
2364 var value = evaluate_expr(v, recv)
2365
2366 v.assign(res, value)
2367 if not useiset then
2368 var true_v = v.bool_instance(true)
2369 v.write_attribute(guard, arguments.first, true_v)
2370 end
2371 v.add("\}")
2372 else
2373 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2374 end
2375 v.assign(v.frame.returnvar.as(not null), res)
2376 else if mpropdef == mwritepropdef then
2377 assert arguments.length == 2
2378 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2379 if is_lazy then
2380 var ret = self.mpropdef.static_mtype
2381 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2382 if not useiset then
2383 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2384 end
2385 end
2386 else
2387 abort
2388 end
2389 end
2390
2391 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2392 do
2393 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2394 end
2395
2396 # Evaluate, store and return the default value of the attribute
2397 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2398 do
2399 var oldnode = v.current_node
2400 v.current_node = self
2401 var old_frame = v.frame
2402 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
2403 v.frame = frame
2404
2405 var value
2406 var mtype = self.mpropdef.static_mtype
2407 assert mtype != null
2408
2409 var nexpr = self.n_expr
2410 var nblock = self.n_block
2411 if nexpr != null then
2412 value = v.expr(nexpr, mtype)
2413 else if nblock != null then
2414 value = v.new_var(mtype)
2415 frame.returnvar = value
2416 frame.returnlabel = v.get_name("RET_LABEL")
2417 v.add("\{")
2418 v.stmt(nblock)
2419 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2420 v.add("\}")
2421 else
2422 abort
2423 end
2424
2425 v.write_attribute(self.mpropdef.mproperty, recv, value)
2426
2427 v.frame = old_frame
2428 v.current_node = oldnode
2429
2430 return value
2431 end
2432
2433 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2434 do
2435 var nexpr = self.n_expr
2436 if nexpr != null then return
2437
2438 var oldnode = v.current_node
2439 v.current_node = self
2440 var old_frame = v.frame
2441 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2442 v.frame = frame
2443 # Force read to check the initialization
2444 v.read_attribute(self.mpropdef.mproperty, recv)
2445 v.frame = old_frame
2446 v.current_node = oldnode
2447 end
2448 end
2449
2450 redef class AClassdef
2451 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2452 do
2453 if mpropdef == self.mfree_init then
2454 assert mpropdef.mproperty.is_root_init
2455 assert arguments.length == 1
2456 if not mpropdef.is_intro then
2457 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2458 end
2459 return
2460 else
2461 abort
2462 end
2463 end
2464 end
2465
2466 redef class AExpr
2467 # Try to compile self as an expression
2468 # Do not call this method directly, use `v.expr` instead
2469 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2470 do
2471 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2472 var mtype = self.mtype
2473 if mtype == null then
2474 return null
2475 else
2476 var res = v.new_var(mtype)
2477 v.add("/* {res} = NOT YET {class_name} */")
2478 return res
2479 end
2480 end
2481
2482 # Try to compile self as a statement
2483 # Do not call this method directly, use `v.stmt` instead
2484 private fun stmt(v: AbstractCompilerVisitor)
2485 do
2486 expr(v)
2487 end
2488 end
2489
2490 redef class ABlockExpr
2491 redef fun stmt(v)
2492 do
2493 for e in self.n_expr do v.stmt(e)
2494 end
2495 redef fun expr(v)
2496 do
2497 var last = self.n_expr.last
2498 for e in self.n_expr do
2499 if e == last then break
2500 v.stmt(e)
2501 end
2502 return v.expr(last, null)
2503 end
2504 end
2505
2506 redef class AVardeclExpr
2507 redef fun stmt(v)
2508 do
2509 var variable = self.variable.as(not null)
2510 var ne = self.n_expr
2511 if ne != null then
2512 var i = v.expr(ne, variable.declared_type)
2513 v.assign(v.variable(variable), i)
2514 end
2515 end
2516 end
2517
2518 redef class AVarExpr
2519 redef fun expr(v)
2520 do
2521 var res = v.variable(self.variable.as(not null))
2522 var mtype = self.mtype.as(not null)
2523 return v.autoadapt(res, mtype)
2524 end
2525 end
2526
2527 redef class AVarAssignExpr
2528 redef fun expr(v)
2529 do
2530 var variable = self.variable.as(not null)
2531 var i = v.expr(self.n_value, variable.declared_type)
2532 v.assign(v.variable(variable), i)
2533 return i
2534 end
2535 end
2536
2537 redef class AVarReassignExpr
2538 redef fun stmt(v)
2539 do
2540 var variable = self.variable.as(not null)
2541 var vari = v.variable(variable)
2542 var value = v.expr(self.n_value, variable.declared_type)
2543 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2544 assert res != null
2545 v.assign(v.variable(variable), res)
2546 end
2547 end
2548
2549 redef class ASelfExpr
2550 redef fun expr(v) do return v.frame.arguments.first
2551 end
2552
2553 redef class AImplicitSelfExpr
2554 redef fun expr(v) do
2555 if not is_sys then return super
2556 return v.new_expr("glob_sys", mtype.as(not null))
2557 end
2558 end
2559
2560 redef class AEscapeExpr
2561 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2562 end
2563
2564 redef class AReturnExpr
2565 redef fun stmt(v)
2566 do
2567 var nexpr = self.n_expr
2568 if nexpr != null then
2569 var returnvar = v.frame.returnvar.as(not null)
2570 var i = v.expr(nexpr, returnvar.mtype)
2571 v.assign(returnvar, i)
2572 end
2573 v.add("goto {v.frame.returnlabel.as(not null)};")
2574 end
2575 end
2576
2577 redef class AAbortExpr
2578 redef fun stmt(v) do v.add_abort("Aborted")
2579 end
2580
2581 redef class AIfExpr
2582 redef fun stmt(v)
2583 do
2584 var cond = v.expr_bool(self.n_expr)
2585 v.add("if ({cond})\{")
2586 v.stmt(self.n_then)
2587 v.add("\} else \{")
2588 v.stmt(self.n_else)
2589 v.add("\}")
2590 end
2591
2592 redef fun expr(v)
2593 do
2594 var res = v.new_var(self.mtype.as(not null))
2595 var cond = v.expr_bool(self.n_expr)
2596 v.add("if ({cond})\{")
2597 v.assign(res, v.expr(self.n_then.as(not null), null))
2598 v.add("\} else \{")
2599 v.assign(res, v.expr(self.n_else.as(not null), null))
2600 v.add("\}")
2601 return res
2602 end
2603 end
2604
2605 redef class AIfexprExpr
2606 redef fun expr(v)
2607 do
2608 var res = v.new_var(self.mtype.as(not null))
2609 var cond = v.expr_bool(self.n_expr)
2610 v.add("if ({cond})\{")
2611 v.assign(res, v.expr(self.n_then, null))
2612 v.add("\} else \{")
2613 v.assign(res, v.expr(self.n_else, null))
2614 v.add("\}")
2615 return res
2616 end
2617 end
2618
2619 redef class ADoExpr
2620 redef fun stmt(v)
2621 do
2622 v.stmt(self.n_block)
2623 v.add_escape_label(break_mark)
2624 end
2625 end
2626
2627 redef class AWhileExpr
2628 redef fun stmt(v)
2629 do
2630 v.add("for(;;) \{")
2631 var cond = v.expr_bool(self.n_expr)
2632 v.add("if (!{cond}) break;")
2633 v.stmt(self.n_block)
2634 v.add_escape_label(continue_mark)
2635 v.add("\}")
2636 v.add_escape_label(break_mark)
2637 end
2638 end
2639
2640 redef class ALoopExpr
2641 redef fun stmt(v)
2642 do
2643 v.add("for(;;) \{")
2644 v.stmt(self.n_block)
2645 v.add_escape_label(continue_mark)
2646 v.add("\}")
2647 v.add_escape_label(break_mark)
2648 end
2649 end
2650
2651 redef class AForExpr
2652 redef fun stmt(v)
2653 do
2654 var cl = v.expr(self.n_expr, null)
2655 var it_meth = self.method_iterator
2656 assert it_meth != null
2657 var it = v.compile_callsite(it_meth, [cl])
2658 assert it != null
2659 v.add("for(;;) \{")
2660 var isok_meth = self.method_is_ok
2661 assert isok_meth != null
2662 var ok = v.compile_callsite(isok_meth, [it])
2663 assert ok != null
2664 v.add("if(!{ok}) break;")
2665 if self.variables.length == 1 then
2666 var item_meth = self.method_item
2667 assert item_meth != null
2668 var i = v.compile_callsite(item_meth, [it])
2669 assert i != null
2670 v.assign(v.variable(variables.first), i)
2671 else if self.variables.length == 2 then
2672 var key_meth = self.method_key
2673 assert key_meth != null
2674 var i = v.compile_callsite(key_meth, [it])
2675 assert i != null
2676 v.assign(v.variable(variables[0]), i)
2677 var item_meth = self.method_item
2678 assert item_meth != null
2679 i = v.compile_callsite(item_meth, [it])
2680 assert i != null
2681 v.assign(v.variable(variables[1]), i)
2682 else
2683 abort
2684 end
2685 v.stmt(self.n_block)
2686 v.add_escape_label(continue_mark)
2687 var next_meth = self.method_next
2688 assert next_meth != null
2689 v.compile_callsite(next_meth, [it])
2690 v.add("\}")
2691 v.add_escape_label(break_mark)
2692
2693 var method_finish = self.method_finish
2694 if method_finish != null then
2695 # TODO: Find a way to call this also in long escape (e.g. return)
2696 v.compile_callsite(method_finish, [it])
2697 end
2698 end
2699 end
2700
2701 redef class AAssertExpr
2702 redef fun stmt(v)
2703 do
2704 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2705
2706 var cond = v.expr_bool(self.n_expr)
2707 v.add("if (unlikely(!{cond})) \{")
2708 v.stmt(self.n_else)
2709 var nid = self.n_id
2710 if nid != null then
2711 v.add_abort("Assert '{nid.text}' failed")
2712 else
2713 v.add_abort("Assert failed")
2714 end
2715 v.add("\}")
2716 end
2717 end
2718
2719 redef class AOrExpr
2720 redef fun expr(v)
2721 do
2722 var res = v.new_var(self.mtype.as(not null))
2723 var i1 = v.expr_bool(self.n_expr)
2724 v.add("if ({i1}) \{")
2725 v.add("{res} = 1;")
2726 v.add("\} else \{")
2727 var i2 = v.expr_bool(self.n_expr2)
2728 v.add("{res} = {i2};")
2729 v.add("\}")
2730 return res
2731 end
2732 end
2733
2734 redef class AImpliesExpr
2735 redef fun expr(v)
2736 do
2737 var res = v.new_var(self.mtype.as(not null))
2738 var i1 = v.expr_bool(self.n_expr)
2739 v.add("if (!{i1}) \{")
2740 v.add("{res} = 1;")
2741 v.add("\} else \{")
2742 var i2 = v.expr_bool(self.n_expr2)
2743 v.add("{res} = {i2};")
2744 v.add("\}")
2745 return res
2746 end
2747 end
2748
2749 redef class AAndExpr
2750 redef fun expr(v)
2751 do
2752 var res = v.new_var(self.mtype.as(not null))
2753 var i1 = v.expr_bool(self.n_expr)
2754 v.add("if (!{i1}) \{")
2755 v.add("{res} = 0;")
2756 v.add("\} else \{")
2757 var i2 = v.expr_bool(self.n_expr2)
2758 v.add("{res} = {i2};")
2759 v.add("\}")
2760 return res
2761 end
2762 end
2763
2764 redef class ANotExpr
2765 redef fun expr(v)
2766 do
2767 var cond = v.expr_bool(self.n_expr)
2768 return v.new_expr("!{cond}", self.mtype.as(not null))
2769 end
2770 end
2771
2772 redef class AOrElseExpr
2773 redef fun expr(v)
2774 do
2775 var res = v.new_var(self.mtype.as(not null))
2776 var i1 = v.expr(self.n_expr, null)
2777 v.add("if ({i1}!=NULL) \{")
2778 v.assign(res, i1)
2779 v.add("\} else \{")
2780 var i2 = v.expr(self.n_expr2, null)
2781 v.assign(res, i2)
2782 v.add("\}")
2783 return res
2784 end
2785 end
2786
2787 redef class AIntExpr
2788 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2789 end
2790
2791 redef class AFloatExpr
2792 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2793 end
2794
2795 redef class ACharExpr
2796 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2797 end
2798
2799 redef class AArrayExpr
2800 redef fun expr(v)
2801 do
2802 var mtype = self.element_mtype.as(not null)
2803 var array = new Array[RuntimeVariable]
2804 var res = v.array_instance(array, mtype)
2805
2806 var old_comprehension = v.frame.comprehension
2807 v.frame.comprehension = res
2808 for nexpr in self.n_exprs do
2809 v.stmt(nexpr)
2810 end
2811 v.frame.comprehension = old_comprehension
2812
2813 return res
2814 end
2815 end
2816
2817 redef class AStringFormExpr
2818 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2819 end
2820
2821 redef class ASuperstringExpr
2822 redef fun expr(v)
2823 do
2824 var type_string = mtype.as(not null)
2825
2826 # Collect elements of the superstring
2827 var array = new Array[AExpr]
2828 for ne in self.n_exprs do
2829 # Drop literal empty string.
2830 # They appears in things like "{a}" that is ["", a, ""]
2831 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2832 array.add(ne)
2833 end
2834
2835 # Store the allocated native array in a static variable
2836 # For reusing later
2837 var varonce = v.get_name("varonce")
2838 v.add("if (unlikely({varonce}==NULL)) \{")
2839
2840 # The native array that will contains the elements to_s-ized.
2841 # For fast concatenation.
2842 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2843
2844 v.add_decl("static {a.mtype.ctype} {varonce};")
2845
2846 # Pre-fill the array with the literal string parts.
2847 # So they do not need to be filled again when reused
2848 for i in [0..array.length[ do
2849 var ne = array[i]
2850 if not ne isa AStringFormExpr then continue
2851 var e = v.expr(ne, null)
2852 v.native_array_set(a, i, e)
2853 end
2854
2855 v.add("\} else \{")
2856 # Take the native-array from the store.
2857 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2858 # WARNING: not thread safe! (FIXME?)
2859 v.add("{a} = {varonce};")
2860 v.add("{varonce} = NULL;")
2861 v.add("\}")
2862
2863 # Stringify the elements and put them in the native array
2864 var to_s_method = v.get_property("to_s", v.object_type)
2865 for i in [0..array.length[ do
2866 var ne = array[i]
2867 if ne isa AStringFormExpr then continue
2868 var e = v.expr(ne, null)
2869 # Skip the `to_s` if the element is already a String
2870 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
2871 e = v.send(to_s_method, [e]).as(not null)
2872 end
2873 v.native_array_set(a, i, e)
2874 end
2875
2876 # Fast join the native string to get the result
2877 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
2878
2879 # We finish to work with the native array,
2880 # so store it so that it can be reused
2881 v.add("{varonce} = {a};")
2882 return res
2883 end
2884 end
2885
2886 redef class ACrangeExpr
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 AOrangeExpr
2899 redef fun expr(v)
2900 do
2901 var i1 = v.expr(self.n_expr, null)
2902 var i2 = v.expr(self.n_expr2, null)
2903 var mtype = self.mtype.as(MClassType)
2904 var res = v.init_instance(mtype)
2905 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2906 return res
2907 end
2908 end
2909
2910 redef class ATrueExpr
2911 redef fun expr(v) do return v.bool_instance(true)
2912 end
2913
2914 redef class AFalseExpr
2915 redef fun expr(v) do return v.bool_instance(false)
2916 end
2917
2918 redef class ANullExpr
2919 redef fun expr(v) do return v.null_instance
2920 end
2921
2922 redef class AIsaExpr
2923 redef fun expr(v)
2924 do
2925 var i = v.expr(self.n_expr, null)
2926 return v.type_test(i, self.cast_type.as(not null), "isa")
2927 end
2928 end
2929
2930 redef class AAsCastExpr
2931 redef fun expr(v)
2932 do
2933 var i = v.expr(self.n_expr, null)
2934 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2935
2936 v.add_cast(i, self.mtype.as(not null), "as")
2937 return i
2938 end
2939 end
2940
2941 redef class AAsNotnullExpr
2942 redef fun expr(v)
2943 do
2944 var i = v.expr(self.n_expr, null)
2945 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2946
2947 if i.mtype.is_c_primitive then return i
2948
2949 v.add("if (unlikely({i} == NULL)) \{")
2950 v.add_abort("Cast failed")
2951 v.add("\}")
2952 return i
2953 end
2954 end
2955
2956 redef class AParExpr
2957 redef fun expr(v) do return v.expr(self.n_expr, null)
2958 end
2959
2960 redef class AOnceExpr
2961 redef fun expr(v)
2962 do
2963 var mtype = self.mtype.as(not null)
2964 var name = v.get_name("varonce")
2965 var guard = v.get_name(name + "_guard")
2966 v.add_decl("static {mtype.ctype} {name};")
2967 v.add_decl("static int {guard};")
2968 var res = v.new_var(mtype)
2969 v.add("if (likely({guard})) \{")
2970 v.add("{res} = {name};")
2971 v.add("\} else \{")
2972 var i = v.expr(self.n_expr, mtype)
2973 v.add("{res} = {i};")
2974 v.add("{name} = {res};")
2975 v.add("{guard} = 1;")
2976 v.add("\}")
2977 return res
2978 end
2979 end
2980
2981 redef class ASendExpr
2982 redef fun expr(v)
2983 do
2984 var recv = v.expr(self.n_expr, null)
2985 var callsite = self.callsite.as(not null)
2986 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
2987 return v.compile_callsite(callsite, args)
2988 end
2989 end
2990
2991 redef class ASendReassignFormExpr
2992 redef fun stmt(v)
2993 do
2994 var recv = v.expr(self.n_expr, null)
2995 var callsite = self.callsite.as(not null)
2996 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
2997
2998 var value = v.expr(self.n_value, null)
2999
3000 var left = v.compile_callsite(callsite, args)
3001 assert left != null
3002
3003 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3004 assert res != null
3005
3006 args.add(res)
3007 v.compile_callsite(self.write_callsite.as(not null), args)
3008 end
3009 end
3010
3011 redef class ASuperExpr
3012 redef fun expr(v)
3013 do
3014 var recv = v.frame.arguments.first
3015
3016 var callsite = self.callsite
3017 if callsite != null then
3018 var args
3019
3020 if self.n_args.n_exprs.is_empty then
3021 # Add automatic arguments for the super init call
3022 args = [recv]
3023 for i in [0..callsite.msignature.arity[ do
3024 args.add(v.frame.arguments[i+1])
3025 end
3026 else
3027 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3028 end
3029
3030 # Super init call
3031 var res = v.compile_callsite(callsite, args)
3032 return res
3033 end
3034
3035 var mpropdef = self.mpropdef.as(not null)
3036
3037 var args
3038 if self.n_args.n_exprs.is_empty then
3039 args = v.frame.arguments
3040 else
3041 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3042 end
3043
3044 # Standard call-next-method
3045 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3046 end
3047 end
3048
3049 redef class ANewExpr
3050 redef fun expr(v)
3051 do
3052 var mtype = self.recvtype
3053 assert mtype != null
3054
3055 if mtype.mclass.name == "NativeArray" then
3056 assert self.n_args.n_exprs.length == 1
3057 var l = v.expr(self.n_args.n_exprs.first, null)
3058 assert mtype isa MGenericType
3059 var elttype = mtype.arguments.first
3060 return v.native_array_instance(elttype, l)
3061 end
3062
3063 var recv = v.init_instance_or_extern(mtype)
3064
3065 var callsite = self.callsite
3066 if callsite == null then return recv
3067
3068 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3069 var res2 = v.compile_callsite(callsite, args)
3070 if res2 != null then
3071 #self.debug("got {res2} from {mproperty}. drop {recv}")
3072 return res2
3073 end
3074 return recv
3075 end
3076 end
3077
3078 redef class AAttrExpr
3079 redef fun expr(v)
3080 do
3081 var recv = v.expr(self.n_expr, null)
3082 var mproperty = self.mproperty.as(not null)
3083 return v.read_attribute(mproperty, recv)
3084 end
3085 end
3086
3087 redef class AAttrAssignExpr
3088 redef fun expr(v)
3089 do
3090 var recv = v.expr(self.n_expr, null)
3091 var i = v.expr(self.n_value, null)
3092 var mproperty = self.mproperty.as(not null)
3093 v.write_attribute(mproperty, recv, i)
3094 return i
3095 end
3096 end
3097
3098 redef class AAttrReassignExpr
3099 redef fun stmt(v)
3100 do
3101 var recv = v.expr(self.n_expr, null)
3102 var value = v.expr(self.n_value, null)
3103 var mproperty = self.mproperty.as(not null)
3104 var attr = v.read_attribute(mproperty, recv)
3105 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3106 assert res != null
3107 v.write_attribute(mproperty, recv, res)
3108 end
3109 end
3110
3111 redef class AIssetAttrExpr
3112 redef fun expr(v)
3113 do
3114 var recv = v.expr(self.n_expr, null)
3115 var mproperty = self.mproperty.as(not null)
3116 return v.isset_attribute(mproperty, recv)
3117 end
3118 end
3119
3120 redef class AVarargExpr
3121 redef fun expr(v)
3122 do
3123 return v.expr(self.n_expr, null)
3124 end
3125 end
3126
3127 redef class ANamedargExpr
3128 redef fun expr(v)
3129 do
3130 return v.expr(self.n_expr, null)
3131 end
3132 end
3133
3134 redef class ADebugTypeExpr
3135 redef fun stmt(v)
3136 do
3137 # do nothing
3138 end
3139 end
3140
3141 # Utils
3142
3143 redef class Array[E]
3144 # Return a new `Array` with the elements only contened in self and not in `o`
3145 fun -(o: Array[E]): Array[E] do
3146 var res = new Array[E]
3147 for e in self do if not o.has(e) then res.add(e)
3148 return res
3149 end
3150 end
3151
3152 redef class MModule
3153 # All `MProperty` associated to all `MClassDef` of `mclass`
3154 fun properties(mclass: MClass): Set[MProperty] do
3155 if not self.properties_cache.has_key(mclass) then
3156 var properties = new HashSet[MProperty]
3157 var parents = new Array[MClass]
3158 if self.flatten_mclass_hierarchy.has(mclass) then
3159 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3160 end
3161 for parent in parents do
3162 properties.add_all(self.properties(parent))
3163 end
3164 for mclassdef in mclass.mclassdefs do
3165 if not self.in_importation <= mclassdef.mmodule then continue
3166 for mprop in mclassdef.intro_mproperties do
3167 properties.add(mprop)
3168 end
3169 end
3170 self.properties_cache[mclass] = properties
3171 end
3172 return properties_cache[mclass]
3173 end
3174 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3175
3176 # Write FFI and nitni results to file
3177 fun finalize_ffi(c: AbstractCompiler) do end
3178
3179 # Give requided addinional system libraries (as given to LD_LIBS)
3180 # Note: can return null instead of an empty set
3181 fun collect_linker_libs: nullable Array[String] do return null
3182 end
3183
3184 # Create a tool context to handle options and paths
3185 var toolcontext = new ToolContext
3186
3187 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3188
3189 # We do not add other options, so process them now!
3190 toolcontext.process_options(args)
3191
3192 # We need a model to collect stufs
3193 var model = new Model
3194 # An a model builder to parse files
3195 var modelbuilder = new ModelBuilder(model, toolcontext)
3196
3197 var arguments = toolcontext.option_context.rest
3198 if arguments.length > 1 and toolcontext.opt_output.value != null then
3199 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3200 exit 1
3201 end
3202
3203 # Here we load an process all modules passed on the command line
3204 var mmodules = modelbuilder.parse(arguments)
3205
3206 if mmodules.is_empty then return
3207 modelbuilder.run_phases
3208
3209 for mmodule in mmodules do
3210 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3211 var ms = [mmodule]
3212 toolcontext.run_global_phases(ms)
3213 end