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