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