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