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