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