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