src: Modified compilers for the support of the new Integers
[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
1507 if value.ascii < 128 then
1508 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1509 else
1510 return new RuntimeVariable("{value.ascii}", t, t)
1511 end
1512 end
1513
1514 # Generate a float value
1515 #
1516 # FIXME pass a Float, not a string
1517 fun float_instance(value: String): RuntimeVariable
1518 do
1519 var t = mmodule.float_type
1520 var res = new RuntimeVariable("{value}", t, t)
1521 return res
1522 end
1523
1524 # Generate an integer value
1525 fun bool_instance(value: Bool): RuntimeVariable
1526 do
1527 var s = if value then "1" else "0"
1528 var res = new RuntimeVariable(s, bool_type, bool_type)
1529 return res
1530 end
1531
1532 # Generate the `null` value
1533 fun null_instance: RuntimeVariable
1534 do
1535 var t = compiler.mainmodule.model.null_type
1536 var res = new RuntimeVariable("((val*)NULL)", t, t)
1537 return res
1538 end
1539
1540 # Generate a string value
1541 fun string_instance(string: String): RuntimeVariable
1542 do
1543 var mtype = mmodule.string_type
1544 var name = self.get_name("varonce")
1545 self.add_decl("static {mtype.ctype} {name};")
1546 var res = self.new_var(mtype)
1547 self.add("if (likely({name}!=NULL)) \{")
1548 self.add("{res} = {name};")
1549 self.add("\} else \{")
1550 var native_mtype = mmodule.native_string_type
1551 var nat = self.new_var(native_mtype)
1552 self.add("{nat} = \"{string.escape_to_c}\";")
1553 var length = self.int_instance(string.bytelen)
1554 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1555 self.add("{name} = {res};")
1556 self.add("\}")
1557 return res
1558 end
1559
1560 fun value_instance(object: Object): RuntimeVariable
1561 do
1562 if object isa Int then
1563 return int_instance(object)
1564 else if object isa Bool then
1565 return bool_instance(object)
1566 else if object isa String then
1567 return string_instance(object)
1568 else
1569 abort
1570 end
1571 end
1572
1573 # Generate an array value
1574 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1575
1576 # Get an instance of a array for a vararg
1577 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1578
1579 # Code generation
1580
1581 # Add a line in the main part of the generated C
1582 fun add(s: String) do self.writer.lines.add(s)
1583
1584 # Add a line in the
1585 # (used for local or global declaration)
1586 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1587
1588 # Request the presence of a global declaration
1589 fun require_declaration(key: String)
1590 do
1591 var reqs = self.writer.file.required_declarations
1592 if reqs.has(key) then return
1593 reqs.add(key)
1594 var node = current_node
1595 if node != null then compiler.requirers_of_declarations[key] = node
1596 end
1597
1598 # Add a declaration in the local-header
1599 # The declaration is ensured to be present once
1600 fun declare_once(s: String)
1601 do
1602 self.compiler.provide_declaration(s, s)
1603 self.require_declaration(s)
1604 end
1605
1606 # Look for a needed .h and .c file for a given module
1607 # This is used for the legacy FFI
1608 fun add_extern(mmodule: MModule)
1609 do
1610 var file = mmodule.location.file.filename
1611 file = file.strip_extension(".nit")
1612 var tryfile = file + ".nit.h"
1613 if tryfile.file_exists then
1614 self.declare_once("#include \"{tryfile.basename}\"")
1615 self.compiler.files_to_copy.add(tryfile)
1616 end
1617 tryfile = file + "_nit.h"
1618 if tryfile.file_exists then
1619 self.declare_once("#include \"{tryfile.basename}\"")
1620 self.compiler.files_to_copy.add(tryfile)
1621 end
1622
1623 if self.compiler.seen_extern.has(file) then return
1624 self.compiler.seen_extern.add(file)
1625 tryfile = file + ".nit.c"
1626 if not tryfile.file_exists then
1627 tryfile = file + "_nit.c"
1628 if not tryfile.file_exists then return
1629 end
1630 var f = new ExternCFile(tryfile.basename, "")
1631 self.compiler.extern_bodies.add(f)
1632 self.compiler.files_to_copy.add(tryfile)
1633 end
1634
1635 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1636 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1637 do
1638 var res = new_var(mtype)
1639 self.add("{res} = {cexpr};")
1640 return res
1641 end
1642
1643 # Generate generic abort
1644 # used by aborts, asserts, casts, etc.
1645 fun add_abort(message: String)
1646 do
1647 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1648 add_raw_abort
1649 end
1650
1651 fun add_raw_abort
1652 do
1653 if self.current_node != null and self.current_node.location.file != null and
1654 self.current_node.location.file.mmodule != null then
1655 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1656 self.require_declaration(f)
1657 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1658 else
1659 self.add("PRINT_ERROR(\"\\n\");")
1660 end
1661 self.add("fatal_exit(1);")
1662 end
1663
1664 # Add a dynamic cast
1665 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1666 do
1667 var res = self.type_test(value, mtype, tag)
1668 self.add("if (unlikely(!{res})) \{")
1669 var cn = self.class_name_string(value)
1670 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1671 self.add_raw_abort
1672 self.add("\}")
1673 end
1674
1675 # Generate a return with the value `s`
1676 fun ret(s: RuntimeVariable)
1677 do
1678 self.assign(self.frame.returnvar.as(not null), s)
1679 self.add("goto {self.frame.returnlabel.as(not null)};")
1680 end
1681
1682 # Compile a statement (if any)
1683 fun stmt(nexpr: nullable AExpr)
1684 do
1685 if nexpr == null then return
1686 if nexpr.mtype == null and not nexpr.is_typed then
1687 # Untyped expression.
1688 # Might mean dead code or invalid code
1689 # so aborts
1690 add_abort("FATAL: bad statement executed.")
1691 return
1692 end
1693
1694 var narray = nexpr.comprehension
1695 if narray != null then
1696 var recv = frame.comprehension.as(not null)
1697 var val = expr(nexpr, narray.element_mtype)
1698 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1699 return
1700 end
1701
1702 var old = self.current_node
1703 self.current_node = nexpr
1704 nexpr.stmt(self)
1705 self.current_node = old
1706 end
1707
1708 # Compile an expression an return its result
1709 # `mtype` is the expected return type, pass null if no specific type is expected.
1710 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1711 do
1712 var old = self.current_node
1713 self.current_node = nexpr
1714
1715 var res = null
1716 if nexpr.mtype != null then
1717 res = nexpr.expr(self)
1718 end
1719
1720 if res == null then
1721 # Untyped expression.
1722 # Might mean dead code or invalid code.
1723 # so aborts
1724 add_abort("FATAL: bad expression executed.")
1725 # and return a placebo result to please the C compiler
1726 if mtype == null then mtype = compiler.mainmodule.object_type
1727 res = new_var(mtype)
1728
1729 self.current_node = old
1730 return res
1731 end
1732
1733 if mtype != null then
1734 mtype = self.anchor(mtype)
1735 res = self.autobox(res, mtype)
1736 end
1737 res = autoadapt(res, nexpr.mtype.as(not null))
1738 var implicit_cast_to = nexpr.implicit_cast_to
1739 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1740 add_cast(res, implicit_cast_to, "auto")
1741 res = autoadapt(res, implicit_cast_to)
1742 end
1743 self.current_node = old
1744 return res
1745 end
1746
1747 # Alias for `self.expr(nexpr, self.bool_type)`
1748 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1749
1750 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1751 fun debug(message: String)
1752 do
1753 var node = self.current_node
1754 if node == null then
1755 print "?: {message}"
1756 else
1757 node.debug(message)
1758 end
1759 self.add("/* DEBUG: {message} */")
1760 end
1761 end
1762
1763 # A C function associated to a Nit method
1764 # Because of customization, a given Nit method can be compiler more that once
1765 abstract class AbstractRuntimeFunction
1766
1767 type COMPILER: AbstractCompiler
1768 type VISITOR: AbstractCompilerVisitor
1769
1770 # The associated Nit method
1771 var mmethoddef: MMethodDef
1772
1773 # The mangled c name of the runtime_function
1774 # Subclasses should redefine `build_c_name` instead
1775 fun c_name: String
1776 do
1777 var res = self.c_name_cache
1778 if res != null then return res
1779 res = self.build_c_name
1780 self.c_name_cache = res
1781 return res
1782 end
1783
1784 # Non cached version of `c_name`
1785 protected fun build_c_name: String is abstract
1786
1787 protected var c_name_cache: nullable String = null is writable
1788
1789 # Implements a call of the runtime_function
1790 # May inline the body or generate a C function call
1791 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1792
1793 # Generate the code for the `AbstractRuntimeFunction`
1794 # Warning: compile more than once compilation makes CC unhappy
1795 fun compile_to_c(compiler: COMPILER) is abstract
1796 end
1797
1798 # A runtime variable hold a runtime value in C.
1799 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1800 #
1801 # 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.
1802 class RuntimeVariable
1803 # The name of the variable in the C code
1804 var name: String
1805
1806 # The static type of the variable (as declard in C)
1807 var mtype: MType
1808
1809 # The current casted type of the variable (as known in Nit)
1810 var mcasttype: MType is writable
1811
1812 # If the variable exaclty a mcasttype?
1813 # false (usual value) means that the variable is a mcasttype or a subtype.
1814 var is_exact: Bool = false is writable
1815
1816 init
1817 do
1818 assert not mtype.need_anchor
1819 assert not mcasttype.need_anchor
1820 end
1821
1822 redef fun to_s do return name
1823
1824 redef fun inspect
1825 do
1826 var exact_str
1827 if self.is_exact then
1828 exact_str = " exact"
1829 else
1830 exact_str = ""
1831 end
1832 var type_str
1833 if self.mtype == self.mcasttype then
1834 type_str = "{mtype}{exact_str}"
1835 else
1836 type_str = "{mtype}({mcasttype}{exact_str})"
1837 end
1838 return "<{name}:{type_str}>"
1839 end
1840 end
1841
1842 # The static context of a visited property in a `AbstractCompilerVisitor`
1843 class StaticFrame
1844
1845 type VISITOR: AbstractCompilerVisitor
1846
1847 # The associated visitor
1848 var visitor: VISITOR
1849
1850 # The executed property.
1851 # A Method in case of a call, an attribute in case of a default initialization.
1852 var mpropdef: MPropDef
1853
1854 # The static type of the receiver
1855 var receiver: MClassType
1856
1857 # Arguments of the method (the first is the receiver)
1858 var arguments: Array[RuntimeVariable]
1859
1860 # The runtime_variable associated to the return (in a function)
1861 var returnvar: nullable RuntimeVariable = null is writable
1862
1863 # The label at the end of the property
1864 var returnlabel: nullable String = null is writable
1865
1866 # Labels associated to a each escapemarks.
1867 # Because of inlinings, escape-marks must be associated to their context (the frame)
1868 private var escapemark_names = new HashMap[EscapeMark, String]
1869
1870 # The array comprehension currently filled, if any
1871 private var comprehension: nullable RuntimeVariable = null
1872 end
1873
1874 redef class MType
1875 # Return the C type associated to a given Nit static type
1876 fun ctype: String do return "val*"
1877
1878 # C type outside of the compiler code and in boxes
1879 fun ctype_extern: String do return "val*"
1880
1881 # Short name of the `ctype` to use in unions
1882 fun ctypename: String do return "val"
1883
1884 # Is the associated C type a primitive one?
1885 #
1886 # ENSURE `result == (ctype != "val*")`
1887 fun is_c_primitive: Bool do return false
1888 end
1889
1890 redef class MClassType
1891
1892 redef var ctype is lazy do
1893 if mclass.name == "Int" then
1894 return "long"
1895 else if mclass.name == "Bool" then
1896 return "short int"
1897 else if mclass.name == "Char" then
1898 return "uint32_t"
1899 else if mclass.name == "Float" then
1900 return "double"
1901 else if mclass.name == "Byte" then
1902 return "unsigned char"
1903 else if mclass.name == "NativeString" then
1904 return "char*"
1905 else if mclass.name == "NativeArray" then
1906 return "val*"
1907 else
1908 return "val*"
1909 end
1910 end
1911
1912 redef var is_c_primitive is lazy do return ctype != "val*"
1913
1914 redef fun ctype_extern: String
1915 do
1916 if mclass.kind == extern_kind then
1917 return "void*"
1918 else
1919 return ctype
1920 end
1921 end
1922
1923 redef fun ctypename: String
1924 do
1925 if mclass.name == "Int" then
1926 return "l"
1927 else if mclass.name == "Bool" then
1928 return "s"
1929 else if mclass.name == "Char" then
1930 return "c"
1931 else if mclass.name == "Float" then
1932 return "d"
1933 else if mclass.name == "Byte" then
1934 return "b"
1935 else if mclass.name == "NativeString" then
1936 return "str"
1937 else if mclass.name == "NativeArray" then
1938 #return "{self.arguments.first.ctype}*"
1939 return "val"
1940 else
1941 return "val"
1942 end
1943 end
1944 end
1945
1946 redef class MPropDef
1947 type VISITOR: AbstractCompilerVisitor
1948 end
1949
1950 redef class MMethodDef
1951 # Can the body be inlined?
1952 fun can_inline(v: VISITOR): Bool
1953 do
1954 if is_abstract then return true
1955 var modelbuilder = v.compiler.modelbuilder
1956 var node = modelbuilder.mpropdef2node(self)
1957 if node isa APropdef then
1958 return node.can_inline
1959 else if node isa AClassdef then
1960 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1961 return true
1962 else
1963 abort
1964 end
1965 end
1966
1967 # Inline the body in another visitor
1968 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1969 do
1970 var modelbuilder = v.compiler.modelbuilder
1971 var val = constant_value
1972 var node = modelbuilder.mpropdef2node(self)
1973
1974 if is_abstract then
1975 var cn = v.class_name_string(arguments.first)
1976 v.current_node = node
1977 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1978 v.add_raw_abort
1979 return null
1980 end
1981
1982 if node isa APropdef then
1983 var oldnode = v.current_node
1984 v.current_node = node
1985 self.compile_parameter_check(v, arguments)
1986 node.compile_to_c(v, self, arguments)
1987 v.current_node = oldnode
1988 else if node isa AClassdef then
1989 var oldnode = v.current_node
1990 v.current_node = node
1991 self.compile_parameter_check(v, arguments)
1992 node.compile_to_c(v, self, arguments)
1993 v.current_node = oldnode
1994 else if val != null then
1995 v.ret(v.value_instance(val))
1996 else
1997 abort
1998 end
1999 return null
2000 end
2001
2002 # Generate type checks in the C code to check covariant parameters
2003 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2004 do
2005 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2006
2007 for i in [0..msignature.arity[ do
2008 # skip test for vararg since the array is instantiated with the correct polymorphic type
2009 if msignature.vararg_rank == i then continue
2010
2011 # skip if the cast is not required
2012 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2013 if not origmtype.need_anchor then continue
2014
2015 # get the parameter type
2016 var mtype = self.msignature.mparameters[i].mtype
2017
2018 # generate the cast
2019 # note that v decides if and how to implements the cast
2020 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
2021 v.add_cast(arguments[i+1], mtype, "covariance")
2022 end
2023 end
2024 end
2025
2026 # Node visit
2027
2028 redef class APropdef
2029 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2030 do
2031 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2032 debug("Not yet implemented")
2033 end
2034
2035 fun can_inline: Bool do return true
2036 end
2037
2038 redef class AMethPropdef
2039 redef fun compile_to_c(v, mpropdef, arguments)
2040 do
2041 # Call the implicit super-init
2042 var auto_super_inits = self.auto_super_inits
2043 if auto_super_inits != null then
2044 var args = [arguments.first]
2045 for auto_super_init in auto_super_inits do
2046 assert auto_super_init.mproperty != mpropdef.mproperty
2047 args.clear
2048 for i in [0..auto_super_init.msignature.arity+1[ do
2049 args.add(arguments[i])
2050 end
2051 assert auto_super_init.mproperty != mpropdef.mproperty
2052 v.compile_callsite(auto_super_init, args)
2053 end
2054 end
2055 if auto_super_call then
2056 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2057 end
2058
2059 # Try special compilation
2060 if mpropdef.is_intern then
2061 if compile_intern_to_c(v, mpropdef, arguments) then return
2062 else if mpropdef.is_extern then
2063 if mpropdef.mproperty.is_init then
2064 if compile_externinit_to_c(v, mpropdef, arguments) then return
2065 else
2066 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2067 end
2068 end
2069
2070 # Compile block if any
2071 var n_block = n_block
2072 if n_block != null then
2073 for i in [0..mpropdef.msignature.arity[ do
2074 var variable = self.n_signature.n_params[i].variable.as(not null)
2075 v.assign(v.variable(variable), arguments[i+1])
2076 end
2077 v.stmt(n_block)
2078 return
2079 end
2080
2081 # We have a problem
2082 var cn = v.class_name_string(arguments.first)
2083 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2084 v.add_raw_abort
2085 end
2086
2087 redef fun can_inline
2088 do
2089 if self.auto_super_inits != null then return false
2090 var nblock = self.n_block
2091 if nblock == null then return true
2092 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2093 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2094 return false
2095 end
2096
2097 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2098 do
2099 var pname = mpropdef.mproperty.name
2100 var cname = mpropdef.mclassdef.mclass.name
2101 var ret = mpropdef.msignature.return_mtype
2102 if ret != null then
2103 ret = v.resolve_for(ret, arguments.first)
2104 end
2105 if pname != "==" and pname != "!=" then
2106 v.adapt_signature(mpropdef, arguments)
2107 v.unbox_signature_extern(mpropdef, arguments)
2108 end
2109 if cname == "Int" then
2110 if pname == "output" then
2111 v.add("printf(\"%ld\\n\", {arguments.first});")
2112 return true
2113 else if pname == "object_id" then
2114 v.ret(arguments.first)
2115 return true
2116 else if pname == "+" then
2117 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2118 return true
2119 else if pname == "-" then
2120 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2121 return true
2122 else if pname == "unary -" then
2123 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2124 return true
2125 else if pname == "unary +" then
2126 v.ret(arguments[0])
2127 return true
2128 else if pname == "*" then
2129 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2130 return true
2131 else if pname == "/" then
2132 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2133 return true
2134 else if pname == "%" then
2135 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2136 return true
2137 else if pname == "lshift" then
2138 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2139 return true
2140 else if pname == "rshift" then
2141 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2142 return true
2143 else if pname == "==" then
2144 v.ret(v.equal_test(arguments[0], arguments[1]))
2145 return true
2146 else if pname == "!=" then
2147 var res = v.equal_test(arguments[0], arguments[1])
2148 v.ret(v.new_expr("!{res}", ret.as(not null)))
2149 return true
2150 else if pname == "<" then
2151 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2152 return true
2153 else if pname == ">" then
2154 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2155 return true
2156 else if pname == "<=" then
2157 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2158 return true
2159 else if pname == ">=" then
2160 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2161 return true
2162 else if pname == "to_f" then
2163 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2164 return true
2165 else if pname == "to_b" then
2166 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2167 return true
2168 else if pname == "ascii" then
2169 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2170 return true
2171 end
2172 else if cname == "Char" then
2173 if pname == "object_id" then
2174 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2175 return true
2176 else if pname == "successor" then
2177 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2178 return true
2179 else if pname == "predecessor" then
2180 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2181 return true
2182 else if pname == "==" then
2183 v.ret(v.equal_test(arguments[0], arguments[1]))
2184 return true
2185 else if pname == "!=" then
2186 var res = v.equal_test(arguments[0], arguments[1])
2187 v.ret(v.new_expr("!{res}", ret.as(not null)))
2188 return true
2189 else if pname == "<" then
2190 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2191 return true
2192 else if pname == ">" then
2193 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2194 return true
2195 else if pname == "<=" then
2196 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2197 return true
2198 else if pname == ">=" then
2199 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2200 return true
2201 else if pname == "to_i" then
2202 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2203 return true
2204 else if pname == "ascii" then
2205 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2206 return true
2207 end
2208 else if cname == "Byte" then
2209 if pname == "output" then
2210 v.add("printf(\"%x\\n\", {arguments.first});")
2211 return true
2212 else if pname == "object_id" then
2213 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2214 return true
2215 else if pname == "+" then
2216 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2217 return true
2218 else if pname == "-" then
2219 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2220 return true
2221 else if pname == "unary -" then
2222 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2223 return true
2224 else if pname == "unary +" then
2225 v.ret(arguments[0])
2226 return true
2227 else if pname == "*" then
2228 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2229 return true
2230 else if pname == "/" then
2231 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2232 return true
2233 else if pname == "%" then
2234 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2235 return true
2236 else if pname == "lshift" then
2237 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2238 return true
2239 else if pname == "rshift" then
2240 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2241 return true
2242 else if pname == "==" then
2243 v.ret(v.equal_test(arguments[0], arguments[1]))
2244 return true
2245 else if pname == "!=" then
2246 var res = v.equal_test(arguments[0], arguments[1])
2247 v.ret(v.new_expr("!{res}", ret.as(not null)))
2248 return true
2249 else if pname == "<" then
2250 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2251 return true
2252 else if pname == ">" then
2253 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2254 return true
2255 else if pname == "<=" then
2256 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2257 return true
2258 else if pname == ">=" then
2259 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2260 return true
2261 else if pname == "to_i" then
2262 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2263 return true
2264 else if pname == "to_f" then
2265 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2266 return true
2267 else if pname == "ascii" then
2268 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2269 return true
2270 end
2271 else if cname == "Bool" then
2272 if pname == "output" then
2273 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2274 return true
2275 else if pname == "object_id" then
2276 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2277 return true
2278 else if pname == "==" then
2279 v.ret(v.equal_test(arguments[0], arguments[1]))
2280 return true
2281 else if pname == "!=" then
2282 var res = v.equal_test(arguments[0], arguments[1])
2283 v.ret(v.new_expr("!{res}", ret.as(not null)))
2284 return true
2285 end
2286 else if cname == "Float" then
2287 if pname == "output" then
2288 v.add("printf(\"%f\\n\", {arguments.first});")
2289 return true
2290 else if pname == "object_id" then
2291 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2292 return true
2293 else if pname == "+" then
2294 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2295 return true
2296 else if pname == "-" then
2297 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2298 return true
2299 else if pname == "unary -" then
2300 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2301 return true
2302 else if pname == "unary +" then
2303 v.ret(arguments[0])
2304 return true
2305 else if pname == "succ" then
2306 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2307 return true
2308 else if pname == "prec" then
2309 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2310 return true
2311 else if pname == "*" then
2312 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2313 return true
2314 else if pname == "/" then
2315 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2316 return true
2317 else if pname == "==" then
2318 v.ret(v.equal_test(arguments[0], arguments[1]))
2319 return true
2320 else if pname == "!=" then
2321 var res = v.equal_test(arguments[0], arguments[1])
2322 v.ret(v.new_expr("!{res}", ret.as(not null)))
2323 return true
2324 else if pname == "<" then
2325 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2326 return true
2327 else if pname == ">" then
2328 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2329 return true
2330 else if pname == "<=" then
2331 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2332 return true
2333 else if pname == ">=" then
2334 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2335 return true
2336 else if pname == "to_i" then
2337 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2338 return true
2339 else if pname == "to_b" then
2340 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2341 return true
2342 end
2343 else if cname == "NativeString" then
2344 if pname == "[]" then
2345 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2346 return true
2347 else if pname == "[]=" then
2348 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2349 return true
2350 else if pname == "copy_to" then
2351 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2352 return true
2353 else if pname == "atoi" then
2354 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2355 return true
2356 else if pname == "fast_cstring" then
2357 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2358 return true
2359 else if pname == "new" then
2360 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2361 return true
2362 end
2363 else if cname == "NativeArray" then
2364 v.native_array_def(pname, ret, arguments)
2365 return true
2366 end
2367 if pname == "exit" then
2368 v.add("exit({arguments[1]});")
2369 return true
2370 else if pname == "sys" then
2371 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2372 return true
2373 else if pname == "calloc_string" then
2374 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2375 return true
2376 else if pname == "calloc_array" then
2377 v.calloc_array(ret.as(not null), arguments)
2378 return true
2379 else if pname == "object_id" then
2380 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2381 return true
2382 else if pname == "is_same_type" then
2383 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2384 return true
2385 else if pname == "is_same_instance" then
2386 v.ret(v.equal_test(arguments[0], arguments[1]))
2387 return true
2388 else if pname == "output_class_name" then
2389 var nat = v.class_name_string(arguments.first)
2390 v.add("printf(\"%s\\n\", {nat});")
2391 return true
2392 else if pname == "native_class_name" then
2393 var nat = v.class_name_string(arguments.first)
2394 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2395 return true
2396 else if pname == "force_garbage_collection" then
2397 v.add("nit_gcollect();")
2398 return true
2399 else if pname == "native_argc" then
2400 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2401 return true
2402 else if pname == "native_argv" then
2403 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2404 return true
2405 end
2406 return false
2407 end
2408
2409 # Compile an extern method
2410 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2411 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2412 do
2413 var externname
2414 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2415 if at != null and at.n_args.length == 1 then
2416 externname = at.arg_as_string(v.compiler.modelbuilder)
2417 if externname == null then return false
2418 else
2419 return false
2420 end
2421 v.add_extern(mpropdef.mclassdef.mmodule)
2422 var res: nullable RuntimeVariable = null
2423 var ret = mpropdef.msignature.return_mtype
2424 if ret != null then
2425 ret = v.resolve_for(ret, arguments.first)
2426 res = v.new_var_extern(ret)
2427 end
2428 v.adapt_signature(mpropdef, arguments)
2429 v.unbox_signature_extern(mpropdef, arguments)
2430
2431 if res == null then
2432 v.add("{externname}({arguments.join(", ")});")
2433 else
2434 v.add("{res} = {externname}({arguments.join(", ")});")
2435 res = v.box_extern(res, ret.as(not null))
2436 v.ret(res)
2437 end
2438 return true
2439 end
2440
2441 # Compile an extern factory
2442 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2443 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2444 do
2445 var externname
2446 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2447 if at != null then
2448 externname = at.arg_as_string(v.compiler.modelbuilder)
2449 if externname == null then return false
2450 else
2451 return false
2452 end
2453 v.add_extern(mpropdef.mclassdef.mmodule)
2454 v.adapt_signature(mpropdef, arguments)
2455 v.unbox_signature_extern(mpropdef, arguments)
2456 var ret = arguments.first.mtype
2457 var res = v.new_var_extern(ret)
2458
2459 arguments.shift
2460
2461 v.add("{res} = {externname}({arguments.join(", ")});")
2462 res = v.box_extern(res, ret)
2463 v.ret(res)
2464 return true
2465 end
2466 end
2467
2468 redef class AAttrPropdef
2469 redef fun can_inline: Bool do return not is_lazy
2470
2471 redef fun compile_to_c(v, mpropdef, arguments)
2472 do
2473 if mpropdef == mreadpropdef then
2474 assert arguments.length == 1
2475 var recv = arguments.first
2476 var res
2477 if is_lazy then
2478 var set
2479 var ret = self.mtype
2480 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2481 var guard = self.mlazypropdef.mproperty
2482 if useiset then
2483 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2484 else
2485 set = v.read_attribute(guard, recv)
2486 end
2487 v.add("if(likely({set})) \{")
2488 res = v.read_attribute(self.mpropdef.mproperty, recv)
2489 v.add("\} else \{")
2490
2491 var value = evaluate_expr(v, recv)
2492
2493 v.assign(res, value)
2494 if not useiset then
2495 var true_v = v.bool_instance(true)
2496 v.write_attribute(guard, arguments.first, true_v)
2497 end
2498 v.add("\}")
2499 else
2500 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2501 end
2502 v.assign(v.frame.returnvar.as(not null), res)
2503 else if mpropdef == mwritepropdef then
2504 assert arguments.length == 2
2505 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2506 if is_lazy then
2507 var ret = self.mtype
2508 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2509 if not useiset then
2510 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2511 end
2512 end
2513 else
2514 abort
2515 end
2516 end
2517
2518 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2519 do
2520 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2521 end
2522
2523 # Evaluate, store and return the default value of the attribute
2524 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2525 do
2526 var oldnode = v.current_node
2527 v.current_node = self
2528 var old_frame = v.frame
2529 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
2530 v.frame = frame
2531
2532 var value
2533 var mtype = self.mtype
2534 assert mtype != null
2535
2536 var nexpr = self.n_expr
2537 var nblock = self.n_block
2538 if nexpr != null then
2539 value = v.expr(nexpr, mtype)
2540 else if nblock != null then
2541 value = v.new_var(mtype)
2542 frame.returnvar = value
2543 frame.returnlabel = v.get_name("RET_LABEL")
2544 v.add("\{")
2545 v.stmt(nblock)
2546 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2547 v.add("\}")
2548 else
2549 abort
2550 end
2551
2552 v.write_attribute(self.mpropdef.mproperty, recv, value)
2553
2554 v.frame = old_frame
2555 v.current_node = oldnode
2556
2557 return value
2558 end
2559
2560 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2561 do
2562 var nexpr = self.n_expr
2563 if nexpr != null then return
2564
2565 var oldnode = v.current_node
2566 v.current_node = self
2567 var old_frame = v.frame
2568 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2569 v.frame = frame
2570 # Force read to check the initialization
2571 v.read_attribute(self.mpropdef.mproperty, recv)
2572 v.frame = old_frame
2573 v.current_node = oldnode
2574 end
2575 end
2576
2577 redef class AClassdef
2578 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2579 do
2580 if mpropdef == self.mfree_init then
2581 assert mpropdef.mproperty.is_root_init
2582 assert arguments.length == 1
2583 if not mpropdef.is_intro then
2584 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2585 end
2586 return
2587 else
2588 abort
2589 end
2590 end
2591 end
2592
2593 redef class AExpr
2594 # Try to compile self as an expression
2595 # Do not call this method directly, use `v.expr` instead
2596 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2597 do
2598 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2599 var mtype = self.mtype
2600 if mtype == null then
2601 return null
2602 else
2603 var res = v.new_var(mtype)
2604 v.add("/* {res} = NOT YET {class_name} */")
2605 return res
2606 end
2607 end
2608
2609 # Try to compile self as a statement
2610 # Do not call this method directly, use `v.stmt` instead
2611 private fun stmt(v: AbstractCompilerVisitor)
2612 do
2613 expr(v)
2614 end
2615 end
2616
2617 redef class ABlockExpr
2618 redef fun stmt(v)
2619 do
2620 for e in self.n_expr do v.stmt(e)
2621 end
2622 redef fun expr(v)
2623 do
2624 var last = self.n_expr.last
2625 for e in self.n_expr do
2626 if e == last then break
2627 v.stmt(e)
2628 end
2629 return v.expr(last, null)
2630 end
2631 end
2632
2633 redef class AVardeclExpr
2634 redef fun stmt(v)
2635 do
2636 var variable = self.variable.as(not null)
2637 var ne = self.n_expr
2638 if ne != null then
2639 var i = v.expr(ne, variable.declared_type)
2640 v.assign(v.variable(variable), i)
2641 end
2642 end
2643 end
2644
2645 redef class AVarExpr
2646 redef fun expr(v)
2647 do
2648 var res = v.variable(self.variable.as(not null))
2649 var mtype = self.mtype.as(not null)
2650 return v.autoadapt(res, mtype)
2651 end
2652 end
2653
2654 redef class AVarAssignExpr
2655 redef fun expr(v)
2656 do
2657 var variable = self.variable.as(not null)
2658 var i = v.expr(self.n_value, variable.declared_type)
2659 v.assign(v.variable(variable), i)
2660 return i
2661 end
2662 end
2663
2664 redef class AVarReassignExpr
2665 redef fun stmt(v)
2666 do
2667 var variable = self.variable.as(not null)
2668 var vari = v.variable(variable)
2669 var value = v.expr(self.n_value, variable.declared_type)
2670 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2671 assert res != null
2672 v.assign(v.variable(variable), res)
2673 end
2674 end
2675
2676 redef class ASelfExpr
2677 redef fun expr(v) do return v.frame.arguments.first
2678 end
2679
2680 redef class AImplicitSelfExpr
2681 redef fun expr(v) do
2682 if not is_sys then return super
2683 return v.new_expr("glob_sys", mtype.as(not null))
2684 end
2685 end
2686
2687 redef class AEscapeExpr
2688 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2689 end
2690
2691 redef class AReturnExpr
2692 redef fun stmt(v)
2693 do
2694 var nexpr = self.n_expr
2695 if nexpr != null then
2696 var returnvar = v.frame.returnvar.as(not null)
2697 var i = v.expr(nexpr, returnvar.mtype)
2698 v.assign(returnvar, i)
2699 end
2700 v.add("goto {v.frame.returnlabel.as(not null)};")
2701 end
2702 end
2703
2704 redef class AAbortExpr
2705 redef fun stmt(v) do v.add_abort("Aborted")
2706 end
2707
2708 redef class AIfExpr
2709 redef fun stmt(v)
2710 do
2711 var cond = v.expr_bool(self.n_expr)
2712 v.add("if ({cond})\{")
2713 v.stmt(self.n_then)
2714 v.add("\} else \{")
2715 v.stmt(self.n_else)
2716 v.add("\}")
2717 end
2718
2719 redef fun expr(v)
2720 do
2721 var res = v.new_var(self.mtype.as(not null))
2722 var cond = v.expr_bool(self.n_expr)
2723 v.add("if ({cond})\{")
2724 v.assign(res, v.expr(self.n_then.as(not null), null))
2725 v.add("\} else \{")
2726 v.assign(res, v.expr(self.n_else.as(not null), null))
2727 v.add("\}")
2728 return res
2729 end
2730 end
2731
2732 redef class AIfexprExpr
2733 redef fun expr(v)
2734 do
2735 var res = v.new_var(self.mtype.as(not null))
2736 var cond = v.expr_bool(self.n_expr)
2737 v.add("if ({cond})\{")
2738 v.assign(res, v.expr(self.n_then, null))
2739 v.add("\} else \{")
2740 v.assign(res, v.expr(self.n_else, null))
2741 v.add("\}")
2742 return res
2743 end
2744 end
2745
2746 redef class ADoExpr
2747 redef fun stmt(v)
2748 do
2749 v.stmt(self.n_block)
2750 v.add_escape_label(break_mark)
2751 end
2752 end
2753
2754 redef class AWhileExpr
2755 redef fun stmt(v)
2756 do
2757 v.add("for(;;) \{")
2758 var cond = v.expr_bool(self.n_expr)
2759 v.add("if (!{cond}) break;")
2760 v.stmt(self.n_block)
2761 v.add_escape_label(continue_mark)
2762 v.add("\}")
2763 v.add_escape_label(break_mark)
2764 end
2765 end
2766
2767 redef class ALoopExpr
2768 redef fun stmt(v)
2769 do
2770 v.add("for(;;) \{")
2771 v.stmt(self.n_block)
2772 v.add_escape_label(continue_mark)
2773 v.add("\}")
2774 v.add_escape_label(break_mark)
2775 end
2776 end
2777
2778 redef class AForExpr
2779 redef fun stmt(v)
2780 do
2781 var cl = v.expr(self.n_expr, null)
2782 var it_meth = self.method_iterator
2783 assert it_meth != null
2784 var it = v.compile_callsite(it_meth, [cl])
2785 assert it != null
2786 v.add("for(;;) \{")
2787 var isok_meth = self.method_is_ok
2788 assert isok_meth != null
2789 var ok = v.compile_callsite(isok_meth, [it])
2790 assert ok != null
2791 v.add("if(!{ok}) break;")
2792 if self.variables.length == 1 then
2793 var item_meth = self.method_item
2794 assert item_meth != null
2795 var i = v.compile_callsite(item_meth, [it])
2796 assert i != null
2797 v.assign(v.variable(variables.first), i)
2798 else if self.variables.length == 2 then
2799 var key_meth = self.method_key
2800 assert key_meth != null
2801 var i = v.compile_callsite(key_meth, [it])
2802 assert i != null
2803 v.assign(v.variable(variables[0]), i)
2804 var item_meth = self.method_item
2805 assert item_meth != null
2806 i = v.compile_callsite(item_meth, [it])
2807 assert i != null
2808 v.assign(v.variable(variables[1]), i)
2809 else
2810 abort
2811 end
2812 v.stmt(self.n_block)
2813 v.add_escape_label(continue_mark)
2814 var next_meth = self.method_next
2815 assert next_meth != null
2816 v.compile_callsite(next_meth, [it])
2817 v.add("\}")
2818 v.add_escape_label(break_mark)
2819
2820 var method_finish = self.method_finish
2821 if method_finish != null then
2822 # TODO: Find a way to call this also in long escape (e.g. return)
2823 v.compile_callsite(method_finish, [it])
2824 end
2825 end
2826 end
2827
2828 redef class AAssertExpr
2829 redef fun stmt(v)
2830 do
2831 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2832
2833 var cond = v.expr_bool(self.n_expr)
2834 v.add("if (unlikely(!{cond})) \{")
2835 v.stmt(self.n_else)
2836 var nid = self.n_id
2837 if nid != null then
2838 v.add_abort("Assert '{nid.text}' failed")
2839 else
2840 v.add_abort("Assert failed")
2841 end
2842 v.add("\}")
2843 end
2844 end
2845
2846 redef class AOrExpr
2847 redef fun expr(v)
2848 do
2849 var res = v.new_var(self.mtype.as(not null))
2850 var i1 = v.expr_bool(self.n_expr)
2851 v.add("if ({i1}) \{")
2852 v.add("{res} = 1;")
2853 v.add("\} else \{")
2854 var i2 = v.expr_bool(self.n_expr2)
2855 v.add("{res} = {i2};")
2856 v.add("\}")
2857 return res
2858 end
2859 end
2860
2861 redef class AImpliesExpr
2862 redef fun expr(v)
2863 do
2864 var res = v.new_var(self.mtype.as(not null))
2865 var i1 = v.expr_bool(self.n_expr)
2866 v.add("if (!{i1}) \{")
2867 v.add("{res} = 1;")
2868 v.add("\} else \{")
2869 var i2 = v.expr_bool(self.n_expr2)
2870 v.add("{res} = {i2};")
2871 v.add("\}")
2872 return res
2873 end
2874 end
2875
2876 redef class AAndExpr
2877 redef fun expr(v)
2878 do
2879 var res = v.new_var(self.mtype.as(not null))
2880 var i1 = v.expr_bool(self.n_expr)
2881 v.add("if (!{i1}) \{")
2882 v.add("{res} = 0;")
2883 v.add("\} else \{")
2884 var i2 = v.expr_bool(self.n_expr2)
2885 v.add("{res} = {i2};")
2886 v.add("\}")
2887 return res
2888 end
2889 end
2890
2891 redef class ANotExpr
2892 redef fun expr(v)
2893 do
2894 var cond = v.expr_bool(self.n_expr)
2895 return v.new_expr("!{cond}", self.mtype.as(not null))
2896 end
2897 end
2898
2899 redef class AOrElseExpr
2900 redef fun expr(v)
2901 do
2902 var res = v.new_var(self.mtype.as(not null))
2903 var i1 = v.expr(self.n_expr, null)
2904 v.add("if ({i1}!=NULL) \{")
2905 v.assign(res, i1)
2906 v.add("\} else \{")
2907 var i2 = v.expr(self.n_expr2, null)
2908 v.assign(res, i2)
2909 v.add("\}")
2910 return res
2911 end
2912 end
2913
2914 redef class AIntegerExpr
2915 redef fun expr(v) do
2916 if value isa Int then return v.int_instance(value.as(Int))
2917 if value isa Byte then return v.byte_instance(value.as(Byte))
2918 # Should never happen
2919 abort
2920 end
2921 end
2922
2923 redef class AFloatExpr
2924 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2925 end
2926
2927 redef class ACharExpr
2928 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2929 end
2930
2931 redef class AArrayExpr
2932 redef fun expr(v)
2933 do
2934 var mtype = self.element_mtype.as(not null)
2935 var array = new Array[RuntimeVariable]
2936 var res = v.array_instance(array, mtype)
2937
2938 var old_comprehension = v.frame.comprehension
2939 v.frame.comprehension = res
2940 for nexpr in self.n_exprs do
2941 v.stmt(nexpr)
2942 end
2943 v.frame.comprehension = old_comprehension
2944
2945 return res
2946 end
2947 end
2948
2949 redef class AStringFormExpr
2950 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2951 end
2952
2953 redef class ASuperstringExpr
2954 redef fun expr(v)
2955 do
2956 var type_string = mtype.as(not null)
2957
2958 # Collect elements of the superstring
2959 var array = new Array[AExpr]
2960 for ne in self.n_exprs do
2961 # Drop literal empty string.
2962 # They appears in things like "{a}" that is ["", a, ""]
2963 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2964 array.add(ne)
2965 end
2966
2967 # Store the allocated native array in a static variable
2968 # For reusing later
2969 var varonce = v.get_name("varonce")
2970 v.add("if (unlikely({varonce}==NULL)) \{")
2971
2972 # The native array that will contains the elements to_s-ized.
2973 # For fast concatenation.
2974 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2975
2976 v.add_decl("static {a.mtype.ctype} {varonce};")
2977
2978 # Pre-fill the array with the literal string parts.
2979 # So they do not need to be filled again when reused
2980 for i in [0..array.length[ do
2981 var ne = array[i]
2982 if not ne isa AStringFormExpr then continue
2983 var e = v.expr(ne, null)
2984 v.native_array_set(a, i, e)
2985 end
2986
2987 v.add("\} else \{")
2988 # Take the native-array from the store.
2989 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2990 # WARNING: not thread safe! (FIXME?)
2991 v.add("{a} = {varonce};")
2992 v.add("{varonce} = NULL;")
2993 v.add("\}")
2994
2995 # Stringify the elements and put them in the native array
2996 var to_s_method = v.get_property("to_s", v.object_type)
2997 for i in [0..array.length[ do
2998 var ne = array[i]
2999 if ne isa AStringFormExpr then continue
3000 var e = v.expr(ne, null)
3001 # Skip the `to_s` if the element is already a String
3002 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3003 e = v.send(to_s_method, [e]).as(not null)
3004 end
3005 v.native_array_set(a, i, e)
3006 end
3007
3008 # Fast join the native string to get the result
3009 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3010
3011 # We finish to work with the native array,
3012 # so store it so that it can be reused
3013 v.add("{varonce} = {a};")
3014 return res
3015 end
3016 end
3017
3018 redef class ACrangeExpr
3019 redef fun expr(v)
3020 do
3021 var i1 = v.expr(self.n_expr, null)
3022 var i2 = v.expr(self.n_expr2, null)
3023 var mtype = self.mtype.as(MClassType)
3024 var res = v.init_instance(mtype)
3025 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3026 return res
3027 end
3028 end
3029
3030 redef class AOrangeExpr
3031 redef fun expr(v)
3032 do
3033 var i1 = v.expr(self.n_expr, null)
3034 var i2 = v.expr(self.n_expr2, null)
3035 var mtype = self.mtype.as(MClassType)
3036 var res = v.init_instance(mtype)
3037 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3038 return res
3039 end
3040 end
3041
3042 redef class ATrueExpr
3043 redef fun expr(v) do return v.bool_instance(true)
3044 end
3045
3046 redef class AFalseExpr
3047 redef fun expr(v) do return v.bool_instance(false)
3048 end
3049
3050 redef class ANullExpr
3051 redef fun expr(v) do return v.null_instance
3052 end
3053
3054 redef class AIsaExpr
3055 redef fun expr(v)
3056 do
3057 var i = v.expr(self.n_expr, null)
3058 var cast_type = self.cast_type
3059 if cast_type == null then return null # no-no on broken node
3060 return v.type_test(i, cast_type, "isa")
3061 end
3062 end
3063
3064 redef class AAsCastExpr
3065 redef fun expr(v)
3066 do
3067 var i = v.expr(self.n_expr, null)
3068 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3069
3070 v.add_cast(i, self.mtype.as(not null), "as")
3071 return i
3072 end
3073 end
3074
3075 redef class AAsNotnullExpr
3076 redef fun expr(v)
3077 do
3078 var i = v.expr(self.n_expr, null)
3079 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3080
3081 if i.mtype.is_c_primitive then return i
3082
3083 v.add("if (unlikely({i} == NULL)) \{")
3084 v.add_abort("Cast failed")
3085 v.add("\}")
3086 return i
3087 end
3088 end
3089
3090 redef class AParExpr
3091 redef fun expr(v) do return v.expr(self.n_expr, null)
3092 end
3093
3094 redef class AOnceExpr
3095 redef fun expr(v)
3096 do
3097 var mtype = self.mtype.as(not null)
3098 var name = v.get_name("varonce")
3099 var guard = v.get_name(name + "_guard")
3100 v.add_decl("static {mtype.ctype} {name};")
3101 v.add_decl("static int {guard};")
3102 var res = v.new_var(mtype)
3103 v.add("if (likely({guard})) \{")
3104 v.add("{res} = {name};")
3105 v.add("\} else \{")
3106 var i = v.expr(self.n_expr, mtype)
3107 v.add("{res} = {i};")
3108 v.add("{name} = {res};")
3109 v.add("{guard} = 1;")
3110 v.add("\}")
3111 return res
3112 end
3113 end
3114
3115 redef class ASendExpr
3116 redef fun expr(v)
3117 do
3118 var recv = v.expr(self.n_expr, null)
3119 var callsite = self.callsite.as(not null)
3120 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3121 return v.compile_callsite(callsite, args)
3122 end
3123 end
3124
3125 redef class ASendReassignFormExpr
3126 redef fun stmt(v)
3127 do
3128 var recv = v.expr(self.n_expr, null)
3129 var callsite = self.callsite.as(not null)
3130 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3131
3132 var value = v.expr(self.n_value, null)
3133
3134 var left = v.compile_callsite(callsite, args)
3135 assert left != null
3136
3137 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3138 assert res != null
3139
3140 args.add(res)
3141 v.compile_callsite(self.write_callsite.as(not null), args)
3142 end
3143 end
3144
3145 redef class ASuperExpr
3146 redef fun expr(v)
3147 do
3148 var recv = v.frame.arguments.first
3149
3150 var callsite = self.callsite
3151 if callsite != null then
3152 var args
3153
3154 if self.n_args.n_exprs.is_empty then
3155 # Add automatic arguments for the super init call
3156 args = [recv]
3157 for i in [0..callsite.msignature.arity[ do
3158 args.add(v.frame.arguments[i+1])
3159 end
3160 else
3161 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3162 end
3163
3164 # Super init call
3165 var res = v.compile_callsite(callsite, args)
3166 return res
3167 end
3168
3169 var mpropdef = self.mpropdef.as(not null)
3170
3171 var args
3172 if self.n_args.n_exprs.is_empty then
3173 args = v.frame.arguments
3174 else
3175 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3176 end
3177
3178 # Standard call-next-method
3179 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3180 end
3181 end
3182
3183 redef class ANewExpr
3184 redef fun expr(v)
3185 do
3186 var mtype = self.recvtype
3187 assert mtype != null
3188
3189 if mtype.mclass.name == "NativeArray" then
3190 assert self.n_args.n_exprs.length == 1
3191 var l = v.expr(self.n_args.n_exprs.first, null)
3192 assert mtype isa MGenericType
3193 var elttype = mtype.arguments.first
3194 return v.native_array_instance(elttype, l)
3195 end
3196
3197 var recv = v.init_instance_or_extern(mtype)
3198
3199 var callsite = self.callsite
3200 if callsite == null then return recv
3201
3202 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3203 var res2 = v.compile_callsite(callsite, args)
3204 if res2 != null then
3205 #self.debug("got {res2} from {mproperty}. drop {recv}")
3206 return res2
3207 end
3208 return recv
3209 end
3210 end
3211
3212 redef class AAttrExpr
3213 redef fun expr(v)
3214 do
3215 var recv = v.expr(self.n_expr, null)
3216 var mproperty = self.mproperty.as(not null)
3217 return v.read_attribute(mproperty, recv)
3218 end
3219 end
3220
3221 redef class AAttrAssignExpr
3222 redef fun expr(v)
3223 do
3224 var recv = v.expr(self.n_expr, null)
3225 var i = v.expr(self.n_value, null)
3226 var mproperty = self.mproperty.as(not null)
3227 v.write_attribute(mproperty, recv, i)
3228 return i
3229 end
3230 end
3231
3232 redef class AAttrReassignExpr
3233 redef fun stmt(v)
3234 do
3235 var recv = v.expr(self.n_expr, null)
3236 var value = v.expr(self.n_value, null)
3237 var mproperty = self.mproperty.as(not null)
3238 var attr = v.read_attribute(mproperty, recv)
3239 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3240 assert res != null
3241 v.write_attribute(mproperty, recv, res)
3242 end
3243 end
3244
3245 redef class AIssetAttrExpr
3246 redef fun expr(v)
3247 do
3248 var recv = v.expr(self.n_expr, null)
3249 var mproperty = self.mproperty.as(not null)
3250 return v.isset_attribute(mproperty, recv)
3251 end
3252 end
3253
3254 redef class AVarargExpr
3255 redef fun expr(v)
3256 do
3257 return v.expr(self.n_expr, null)
3258 end
3259 end
3260
3261 redef class ANamedargExpr
3262 redef fun expr(v)
3263 do
3264 return v.expr(self.n_expr, null)
3265 end
3266 end
3267
3268 redef class ADebugTypeExpr
3269 redef fun stmt(v)
3270 do
3271 # do nothing
3272 end
3273 end
3274
3275 # Utils
3276
3277 redef class Array[E]
3278 # Return a new `Array` with the elements only contened in self and not in `o`
3279 fun -(o: Array[E]): Array[E] do
3280 var res = new Array[E]
3281 for e in self do if not o.has(e) then res.add(e)
3282 return res
3283 end
3284 end
3285
3286 redef class MModule
3287 # All `MProperty` associated to all `MClassDef` of `mclass`
3288 fun properties(mclass: MClass): Set[MProperty] do
3289 if not self.properties_cache.has_key(mclass) then
3290 var properties = new HashSet[MProperty]
3291 var parents = new Array[MClass]
3292 if self.flatten_mclass_hierarchy.has(mclass) then
3293 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3294 end
3295 for parent in parents do
3296 properties.add_all(self.properties(parent))
3297 end
3298 for mclassdef in mclass.mclassdefs do
3299 if not self.in_importation <= mclassdef.mmodule then continue
3300 for mprop in mclassdef.intro_mproperties do
3301 properties.add(mprop)
3302 end
3303 end
3304 self.properties_cache[mclass] = properties
3305 end
3306 return properties_cache[mclass]
3307 end
3308 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3309
3310 # Write FFI and nitni results to file
3311 fun finalize_ffi(c: AbstractCompiler) do end
3312
3313 # Give requided addinional system libraries (as given to LD_LIBS)
3314 # Note: can return null instead of an empty set
3315 fun collect_linker_libs: nullable Array[String] do return null
3316 end
3317
3318 # Create a tool context to handle options and paths
3319 var toolcontext = new ToolContext
3320
3321 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3322
3323 # We do not add other options, so process them now!
3324 toolcontext.process_options(args)
3325
3326 # We need a model to collect stufs
3327 var model = new Model
3328 # An a model builder to parse files
3329 var modelbuilder = new ModelBuilder(model, toolcontext)
3330
3331 var arguments = toolcontext.option_context.rest
3332 if arguments.length > 1 and toolcontext.opt_output.value != null then
3333 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3334 exit 1
3335 end
3336
3337 # Here we load an process all modules passed on the command line
3338 var mmodules = modelbuilder.parse(arguments)
3339
3340 if mmodules.is_empty then return
3341 modelbuilder.run_phases
3342
3343 for mmodule in mmodules do
3344 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3345 var ms = [mmodule]
3346 toolcontext.run_global_phases(ms)
3347 end