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