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