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