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