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