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