Merge: Add debug option to compiler
[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 == "lshift" then
2145 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2146 return true
2147 else if pname == "rshift" then
2148 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2149 return true
2150 else if pname == "==" then
2151 v.ret(v.equal_test(arguments[0], arguments[1]))
2152 return true
2153 else if pname == "!=" then
2154 var res = v.equal_test(arguments[0], arguments[1])
2155 v.ret(v.new_expr("!{res}", 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 == "<=" then
2164 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2165 return true
2166 else if pname == ">=" then
2167 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2168 return true
2169 else if pname == "to_f" then
2170 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2171 return true
2172 else if pname == "to_b" then
2173 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2174 return true
2175 else if pname == "ascii" then
2176 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2177 return true
2178 end
2179 else if cname == "Char" then
2180 if pname == "object_id" then
2181 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2182 return true
2183 else if pname == "successor" then
2184 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2185 return true
2186 else if pname == "predecessor" then
2187 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2188 return true
2189 else if pname == "==" then
2190 v.ret(v.equal_test(arguments[0], arguments[1]))
2191 return true
2192 else if pname == "!=" then
2193 var res = v.equal_test(arguments[0], arguments[1])
2194 v.ret(v.new_expr("!{res}", 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 == "<=" then
2203 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2204 return true
2205 else if pname == ">=" then
2206 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2207 return true
2208 else if pname == "to_i" then
2209 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2210 return true
2211 else if pname == "ascii" then
2212 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2213 return true
2214 end
2215 else if cname == "Byte" then
2216 if pname == "output" then
2217 v.add("printf(\"%x\\n\", {arguments.first});")
2218 return true
2219 else if pname == "object_id" then
2220 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2221 return true
2222 else if pname == "+" then
2223 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2224 return true
2225 else if pname == "-" then
2226 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2227 return true
2228 else if pname == "unary -" then
2229 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2230 return true
2231 else if pname == "unary +" then
2232 v.ret(arguments[0])
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.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2239 return true
2240 else if pname == "%" then
2241 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2242 return true
2243 else if pname == "lshift" then
2244 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2245 return true
2246 else if pname == "rshift" then
2247 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2248 return true
2249 else if pname == "==" then
2250 v.ret(v.equal_test(arguments[0], arguments[1]))
2251 return true
2252 else if pname == "!=" then
2253 var res = v.equal_test(arguments[0], arguments[1])
2254 v.ret(v.new_expr("!{res}", ret.as(not null)))
2255 return true
2256 else if pname == "<" then
2257 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2258 return true
2259 else if pname == ">" then
2260 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2261 return true
2262 else if pname == "<=" then
2263 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2264 return true
2265 else if pname == ">=" then
2266 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2267 return true
2268 else if pname == "to_i" then
2269 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2270 return true
2271 else if pname == "to_f" then
2272 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2273 return true
2274 else if pname == "ascii" then
2275 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2276 return true
2277 end
2278 else if cname == "Bool" then
2279 if pname == "output" then
2280 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2281 return true
2282 else if pname == "object_id" then
2283 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2284 return true
2285 else if pname == "==" then
2286 v.ret(v.equal_test(arguments[0], arguments[1]))
2287 return true
2288 else if pname == "!=" then
2289 var res = v.equal_test(arguments[0], arguments[1])
2290 v.ret(v.new_expr("!{res}", ret.as(not null)))
2291 return true
2292 end
2293 else if cname == "Float" then
2294 if pname == "output" then
2295 v.add("printf(\"%f\\n\", {arguments.first});")
2296 return true
2297 else if pname == "object_id" then
2298 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2299 return true
2300 else if pname == "+" then
2301 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2302 return true
2303 else if pname == "-" then
2304 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2305 return true
2306 else if pname == "unary -" then
2307 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2308 return true
2309 else if pname == "unary +" then
2310 v.ret(arguments[0])
2311 return true
2312 else if pname == "succ" then
2313 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2314 return true
2315 else if pname == "prec" then
2316 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2317 return true
2318 else if pname == "*" then
2319 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2320 return true
2321 else if pname == "/" then
2322 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2323 return true
2324 else if pname == "==" then
2325 v.ret(v.equal_test(arguments[0], arguments[1]))
2326 return true
2327 else if pname == "!=" then
2328 var res = v.equal_test(arguments[0], arguments[1])
2329 v.ret(v.new_expr("!{res}", ret.as(not null)))
2330 return true
2331 else if pname == "<" then
2332 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2333 return true
2334 else if pname == ">" then
2335 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2336 return true
2337 else if pname == "<=" then
2338 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2339 return true
2340 else if pname == ">=" then
2341 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2342 return true
2343 else if pname == "to_i" then
2344 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2345 return true
2346 else if pname == "to_b" then
2347 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2348 return true
2349 end
2350 else if cname == "NativeString" then
2351 if pname == "[]" then
2352 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2353 return true
2354 else if pname == "[]=" then
2355 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2356 return true
2357 else if pname == "copy_to" then
2358 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2359 return true
2360 else if pname == "atoi" then
2361 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2362 return true
2363 else if pname == "fast_cstring" then
2364 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2365 return true
2366 else if pname == "new" then
2367 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2368 return true
2369 end
2370 else if cname == "NativeArray" then
2371 v.native_array_def(pname, ret, arguments)
2372 return true
2373 end
2374 if pname == "exit" then
2375 v.add("exit({arguments[1]});")
2376 return true
2377 else if pname == "sys" then
2378 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2379 return true
2380 else if pname == "calloc_string" then
2381 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2382 return true
2383 else if pname == "calloc_array" then
2384 v.calloc_array(ret.as(not null), arguments)
2385 return true
2386 else if pname == "object_id" then
2387 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2388 return true
2389 else if pname == "is_same_type" then
2390 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2391 return true
2392 else if pname == "is_same_instance" then
2393 v.ret(v.equal_test(arguments[0], arguments[1]))
2394 return true
2395 else if pname == "output_class_name" then
2396 var nat = v.class_name_string(arguments.first)
2397 v.add("printf(\"%s\\n\", {nat});")
2398 return true
2399 else if pname == "native_class_name" then
2400 var nat = v.class_name_string(arguments.first)
2401 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2402 return true
2403 else if pname == "force_garbage_collection" then
2404 v.add("nit_gcollect();")
2405 return true
2406 else if pname == "native_argc" then
2407 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2408 return true
2409 else if pname == "native_argv" then
2410 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2411 return true
2412 end
2413 return false
2414 end
2415
2416 # Compile an extern method
2417 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2418 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2419 do
2420 var externname
2421 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2422 if at != null and at.n_args.length == 1 then
2423 externname = at.arg_as_string(v.compiler.modelbuilder)
2424 if externname == null then return false
2425 else
2426 return false
2427 end
2428 v.add_extern(mpropdef.mclassdef.mmodule)
2429 var res: nullable RuntimeVariable = null
2430 var ret = mpropdef.msignature.return_mtype
2431 if ret != null then
2432 ret = v.resolve_for(ret, arguments.first)
2433 res = v.new_var_extern(ret)
2434 end
2435 v.adapt_signature(mpropdef, arguments)
2436 v.unbox_signature_extern(mpropdef, arguments)
2437
2438 if res == null then
2439 v.add("{externname}({arguments.join(", ")});")
2440 else
2441 v.add("{res} = {externname}({arguments.join(", ")});")
2442 res = v.box_extern(res, ret.as(not null))
2443 v.ret(res)
2444 end
2445 return true
2446 end
2447
2448 # Compile an extern factory
2449 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2450 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2451 do
2452 var externname
2453 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2454 if at != null then
2455 externname = at.arg_as_string(v.compiler.modelbuilder)
2456 if externname == null then return false
2457 else
2458 return false
2459 end
2460 v.add_extern(mpropdef.mclassdef.mmodule)
2461 v.adapt_signature(mpropdef, arguments)
2462 v.unbox_signature_extern(mpropdef, arguments)
2463 var ret = arguments.first.mtype
2464 var res = v.new_var_extern(ret)
2465
2466 arguments.shift
2467
2468 v.add("{res} = {externname}({arguments.join(", ")});")
2469 res = v.box_extern(res, ret)
2470 v.ret(res)
2471 return true
2472 end
2473 end
2474
2475 redef class AAttrPropdef
2476 redef fun can_inline: Bool do return not is_lazy
2477
2478 redef fun compile_to_c(v, mpropdef, arguments)
2479 do
2480 if mpropdef == mreadpropdef then
2481 assert arguments.length == 1
2482 var recv = arguments.first
2483 var res
2484 if is_lazy then
2485 var set
2486 var ret = self.mtype
2487 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2488 var guard = self.mlazypropdef.mproperty
2489 if useiset then
2490 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2491 else
2492 set = v.read_attribute(guard, recv)
2493 end
2494 v.add("if(likely({set})) \{")
2495 res = v.read_attribute(self.mpropdef.mproperty, recv)
2496 v.add("\} else \{")
2497
2498 var value = evaluate_expr(v, recv)
2499
2500 v.assign(res, value)
2501 if not useiset then
2502 var true_v = v.bool_instance(true)
2503 v.write_attribute(guard, arguments.first, true_v)
2504 end
2505 v.add("\}")
2506 else
2507 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2508 end
2509 v.assign(v.frame.returnvar.as(not null), res)
2510 else if mpropdef == mwritepropdef then
2511 assert arguments.length == 2
2512 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2513 if is_lazy then
2514 var ret = self.mtype
2515 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2516 if not useiset then
2517 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2518 end
2519 end
2520 else
2521 abort
2522 end
2523 end
2524
2525 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2526 do
2527 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2528 end
2529
2530 # Evaluate, store and return the default value of the attribute
2531 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2532 do
2533 var oldnode = v.current_node
2534 v.current_node = self
2535 var old_frame = v.frame
2536 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
2537 v.frame = frame
2538
2539 var value
2540 var mtype = self.mtype
2541 assert mtype != null
2542
2543 var nexpr = self.n_expr
2544 var nblock = self.n_block
2545 if nexpr != null then
2546 value = v.expr(nexpr, mtype)
2547 else if nblock != null then
2548 value = v.new_var(mtype)
2549 frame.returnvar = value
2550 frame.returnlabel = v.get_name("RET_LABEL")
2551 v.add("\{")
2552 v.stmt(nblock)
2553 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2554 v.add("\}")
2555 else
2556 abort
2557 end
2558
2559 v.write_attribute(self.mpropdef.mproperty, recv, value)
2560
2561 v.frame = old_frame
2562 v.current_node = oldnode
2563
2564 return value
2565 end
2566
2567 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2568 do
2569 var nexpr = self.n_expr
2570 if nexpr != null then return
2571
2572 var oldnode = v.current_node
2573 v.current_node = self
2574 var old_frame = v.frame
2575 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2576 v.frame = frame
2577 # Force read to check the initialization
2578 v.read_attribute(self.mpropdef.mproperty, recv)
2579 v.frame = old_frame
2580 v.current_node = oldnode
2581 end
2582 end
2583
2584 redef class AClassdef
2585 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2586 do
2587 if mpropdef == self.mfree_init then
2588 assert mpropdef.mproperty.is_root_init
2589 assert arguments.length == 1
2590 if not mpropdef.is_intro then
2591 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2592 end
2593 return
2594 else
2595 abort
2596 end
2597 end
2598 end
2599
2600 redef class AExpr
2601 # Try to compile self as an expression
2602 # Do not call this method directly, use `v.expr` instead
2603 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2604 do
2605 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2606 var mtype = self.mtype
2607 if mtype == null then
2608 return null
2609 else
2610 var res = v.new_var(mtype)
2611 v.add("/* {res} = NOT YET {class_name} */")
2612 return res
2613 end
2614 end
2615
2616 # Try to compile self as a statement
2617 # Do not call this method directly, use `v.stmt` instead
2618 private fun stmt(v: AbstractCompilerVisitor)
2619 do
2620 expr(v)
2621 end
2622 end
2623
2624 redef class ABlockExpr
2625 redef fun stmt(v)
2626 do
2627 for e in self.n_expr do v.stmt(e)
2628 end
2629 redef fun expr(v)
2630 do
2631 var last = self.n_expr.last
2632 for e in self.n_expr do
2633 if e == last then break
2634 v.stmt(e)
2635 end
2636 return v.expr(last, null)
2637 end
2638 end
2639
2640 redef class AVardeclExpr
2641 redef fun stmt(v)
2642 do
2643 var variable = self.variable.as(not null)
2644 var ne = self.n_expr
2645 if ne != null then
2646 var i = v.expr(ne, variable.declared_type)
2647 v.assign(v.variable(variable), i)
2648 end
2649 end
2650 end
2651
2652 redef class AVarExpr
2653 redef fun expr(v)
2654 do
2655 var res = v.variable(self.variable.as(not null))
2656 var mtype = self.mtype.as(not null)
2657 return v.autoadapt(res, mtype)
2658 end
2659 end
2660
2661 redef class AVarAssignExpr
2662 redef fun expr(v)
2663 do
2664 var variable = self.variable.as(not null)
2665 var i = v.expr(self.n_value, variable.declared_type)
2666 v.assign(v.variable(variable), i)
2667 return i
2668 end
2669 end
2670
2671 redef class AVarReassignExpr
2672 redef fun stmt(v)
2673 do
2674 var variable = self.variable.as(not null)
2675 var vari = v.variable(variable)
2676 var value = v.expr(self.n_value, variable.declared_type)
2677 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2678 assert res != null
2679 v.assign(v.variable(variable), res)
2680 end
2681 end
2682
2683 redef class ASelfExpr
2684 redef fun expr(v) do return v.frame.arguments.first
2685 end
2686
2687 redef class AImplicitSelfExpr
2688 redef fun expr(v) do
2689 if not is_sys then return super
2690 return v.new_expr("glob_sys", mtype.as(not null))
2691 end
2692 end
2693
2694 redef class AEscapeExpr
2695 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2696 end
2697
2698 redef class AReturnExpr
2699 redef fun stmt(v)
2700 do
2701 var nexpr = self.n_expr
2702 if nexpr != null then
2703 var returnvar = v.frame.returnvar.as(not null)
2704 var i = v.expr(nexpr, returnvar.mtype)
2705 v.assign(returnvar, i)
2706 end
2707 v.add("goto {v.frame.returnlabel.as(not null)};")
2708 end
2709 end
2710
2711 redef class AAbortExpr
2712 redef fun stmt(v) do v.add_abort("Aborted")
2713 end
2714
2715 redef class AIfExpr
2716 redef fun stmt(v)
2717 do
2718 var cond = v.expr_bool(self.n_expr)
2719 v.add("if ({cond})\{")
2720 v.stmt(self.n_then)
2721 v.add("\} else \{")
2722 v.stmt(self.n_else)
2723 v.add("\}")
2724 end
2725
2726 redef fun expr(v)
2727 do
2728 var res = v.new_var(self.mtype.as(not null))
2729 var cond = v.expr_bool(self.n_expr)
2730 v.add("if ({cond})\{")
2731 v.assign(res, v.expr(self.n_then.as(not null), null))
2732 v.add("\} else \{")
2733 v.assign(res, v.expr(self.n_else.as(not null), null))
2734 v.add("\}")
2735 return res
2736 end
2737 end
2738
2739 redef class AIfexprExpr
2740 redef fun expr(v)
2741 do
2742 var res = v.new_var(self.mtype.as(not null))
2743 var cond = v.expr_bool(self.n_expr)
2744 v.add("if ({cond})\{")
2745 v.assign(res, v.expr(self.n_then, null))
2746 v.add("\} else \{")
2747 v.assign(res, v.expr(self.n_else, null))
2748 v.add("\}")
2749 return res
2750 end
2751 end
2752
2753 redef class ADoExpr
2754 redef fun stmt(v)
2755 do
2756 v.stmt(self.n_block)
2757 v.add_escape_label(break_mark)
2758 end
2759 end
2760
2761 redef class AWhileExpr
2762 redef fun stmt(v)
2763 do
2764 v.add("for(;;) \{")
2765 var cond = v.expr_bool(self.n_expr)
2766 v.add("if (!{cond}) break;")
2767 v.stmt(self.n_block)
2768 v.add_escape_label(continue_mark)
2769 v.add("\}")
2770 v.add_escape_label(break_mark)
2771 end
2772 end
2773
2774 redef class ALoopExpr
2775 redef fun stmt(v)
2776 do
2777 v.add("for(;;) \{")
2778 v.stmt(self.n_block)
2779 v.add_escape_label(continue_mark)
2780 v.add("\}")
2781 v.add_escape_label(break_mark)
2782 end
2783 end
2784
2785 redef class AForExpr
2786 redef fun stmt(v)
2787 do
2788 var cl = v.expr(self.n_expr, null)
2789 var it_meth = self.method_iterator
2790 assert it_meth != null
2791 var it = v.compile_callsite(it_meth, [cl])
2792 assert it != null
2793 v.add("for(;;) \{")
2794 var isok_meth = self.method_is_ok
2795 assert isok_meth != null
2796 var ok = v.compile_callsite(isok_meth, [it])
2797 assert ok != null
2798 v.add("if(!{ok}) break;")
2799 if self.variables.length == 1 then
2800 var item_meth = self.method_item
2801 assert item_meth != null
2802 var i = v.compile_callsite(item_meth, [it])
2803 assert i != null
2804 v.assign(v.variable(variables.first), i)
2805 else if self.variables.length == 2 then
2806 var key_meth = self.method_key
2807 assert key_meth != null
2808 var i = v.compile_callsite(key_meth, [it])
2809 assert i != null
2810 v.assign(v.variable(variables[0]), i)
2811 var item_meth = self.method_item
2812 assert item_meth != null
2813 i = v.compile_callsite(item_meth, [it])
2814 assert i != null
2815 v.assign(v.variable(variables[1]), i)
2816 else
2817 abort
2818 end
2819 v.stmt(self.n_block)
2820 v.add_escape_label(continue_mark)
2821 var next_meth = self.method_next
2822 assert next_meth != null
2823 v.compile_callsite(next_meth, [it])
2824 v.add("\}")
2825 v.add_escape_label(break_mark)
2826
2827 var method_finish = self.method_finish
2828 if method_finish != null then
2829 # TODO: Find a way to call this also in long escape (e.g. return)
2830 v.compile_callsite(method_finish, [it])
2831 end
2832 end
2833 end
2834
2835 redef class AAssertExpr
2836 redef fun stmt(v)
2837 do
2838 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2839
2840 var cond = v.expr_bool(self.n_expr)
2841 v.add("if (unlikely(!{cond})) \{")
2842 v.stmt(self.n_else)
2843 var nid = self.n_id
2844 if nid != null then
2845 v.add_abort("Assert '{nid.text}' failed")
2846 else
2847 v.add_abort("Assert failed")
2848 end
2849 v.add("\}")
2850 end
2851 end
2852
2853 redef class AOrExpr
2854 redef fun expr(v)
2855 do
2856 var res = v.new_var(self.mtype.as(not null))
2857 var i1 = v.expr_bool(self.n_expr)
2858 v.add("if ({i1}) \{")
2859 v.add("{res} = 1;")
2860 v.add("\} else \{")
2861 var i2 = v.expr_bool(self.n_expr2)
2862 v.add("{res} = {i2};")
2863 v.add("\}")
2864 return res
2865 end
2866 end
2867
2868 redef class AImpliesExpr
2869 redef fun expr(v)
2870 do
2871 var res = v.new_var(self.mtype.as(not null))
2872 var i1 = v.expr_bool(self.n_expr)
2873 v.add("if (!{i1}) \{")
2874 v.add("{res} = 1;")
2875 v.add("\} else \{")
2876 var i2 = v.expr_bool(self.n_expr2)
2877 v.add("{res} = {i2};")
2878 v.add("\}")
2879 return res
2880 end
2881 end
2882
2883 redef class AAndExpr
2884 redef fun expr(v)
2885 do
2886 var res = v.new_var(self.mtype.as(not null))
2887 var i1 = v.expr_bool(self.n_expr)
2888 v.add("if (!{i1}) \{")
2889 v.add("{res} = 0;")
2890 v.add("\} else \{")
2891 var i2 = v.expr_bool(self.n_expr2)
2892 v.add("{res} = {i2};")
2893 v.add("\}")
2894 return res
2895 end
2896 end
2897
2898 redef class ANotExpr
2899 redef fun expr(v)
2900 do
2901 var cond = v.expr_bool(self.n_expr)
2902 return v.new_expr("!{cond}", self.mtype.as(not null))
2903 end
2904 end
2905
2906 redef class AOrElseExpr
2907 redef fun expr(v)
2908 do
2909 var res = v.new_var(self.mtype.as(not null))
2910 var i1 = v.expr(self.n_expr, null)
2911 v.add("if ({i1}!=NULL) \{")
2912 v.assign(res, i1)
2913 v.add("\} else \{")
2914 var i2 = v.expr(self.n_expr2, null)
2915 v.assign(res, i2)
2916 v.add("\}")
2917 return res
2918 end
2919 end
2920
2921 redef class AIntegerExpr
2922 redef fun expr(v) do
2923 if value isa Int then return v.int_instance(value.as(Int))
2924 if value isa Byte then return v.byte_instance(value.as(Byte))
2925 # Should never happen
2926 abort
2927 end
2928 end
2929
2930 redef class AFloatExpr
2931 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2932 end
2933
2934 redef class ACharExpr
2935 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2936 end
2937
2938 redef class AArrayExpr
2939 redef fun expr(v)
2940 do
2941 var mtype = self.element_mtype.as(not null)
2942 var array = new Array[RuntimeVariable]
2943 var res = v.array_instance(array, mtype)
2944
2945 var old_comprehension = v.frame.comprehension
2946 v.frame.comprehension = res
2947 for nexpr in self.n_exprs do
2948 v.stmt(nexpr)
2949 end
2950 v.frame.comprehension = old_comprehension
2951
2952 return res
2953 end
2954 end
2955
2956 redef class AStringFormExpr
2957 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2958 end
2959
2960 redef class ASuperstringExpr
2961 redef fun expr(v)
2962 do
2963 var type_string = mtype.as(not null)
2964
2965 # Collect elements of the superstring
2966 var array = new Array[AExpr]
2967 for ne in self.n_exprs do
2968 # Drop literal empty string.
2969 # They appears in things like "{a}" that is ["", a, ""]
2970 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2971 array.add(ne)
2972 end
2973
2974 # Store the allocated native array in a static variable
2975 # For reusing later
2976 var varonce = v.get_name("varonce")
2977 v.add("if (unlikely({varonce}==NULL)) \{")
2978
2979 # The native array that will contains the elements to_s-ized.
2980 # For fast concatenation.
2981 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2982
2983 v.add_decl("static {a.mtype.ctype} {varonce};")
2984
2985 # Pre-fill the array with the literal string parts.
2986 # So they do not need to be filled again when reused
2987 for i in [0..array.length[ do
2988 var ne = array[i]
2989 if not ne isa AStringFormExpr then continue
2990 var e = v.expr(ne, null)
2991 v.native_array_set(a, i, e)
2992 end
2993
2994 v.add("\} else \{")
2995 # Take the native-array from the store.
2996 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2997 # WARNING: not thread safe! (FIXME?)
2998 v.add("{a} = {varonce};")
2999 v.add("{varonce} = NULL;")
3000 v.add("\}")
3001
3002 # Stringify the elements and put them in the native array
3003 var to_s_method = v.get_property("to_s", v.object_type)
3004 for i in [0..array.length[ do
3005 var ne = array[i]
3006 if ne isa AStringFormExpr then continue
3007 var e = v.expr(ne, null)
3008 # Skip the `to_s` if the element is already a String
3009 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3010 e = v.send(to_s_method, [e]).as(not null)
3011 end
3012 v.native_array_set(a, i, e)
3013 end
3014
3015 # Fast join the native string to get the result
3016 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3017
3018 # We finish to work with the native array,
3019 # so store it so that it can be reused
3020 v.add("{varonce} = {a};")
3021 return res
3022 end
3023 end
3024
3025 redef class ACrangeExpr
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 AOrangeExpr
3038 redef fun expr(v)
3039 do
3040 var i1 = v.expr(self.n_expr, null)
3041 var i2 = v.expr(self.n_expr2, null)
3042 var mtype = self.mtype.as(MClassType)
3043 var res = v.init_instance(mtype)
3044 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3045 return res
3046 end
3047 end
3048
3049 redef class ATrueExpr
3050 redef fun expr(v) do return v.bool_instance(true)
3051 end
3052
3053 redef class AFalseExpr
3054 redef fun expr(v) do return v.bool_instance(false)
3055 end
3056
3057 redef class ANullExpr
3058 redef fun expr(v) do return v.null_instance
3059 end
3060
3061 redef class AIsaExpr
3062 redef fun expr(v)
3063 do
3064 var i = v.expr(self.n_expr, null)
3065 var cast_type = self.cast_type
3066 if cast_type == null then return null # no-no on broken node
3067 return v.type_test(i, cast_type, "isa")
3068 end
3069 end
3070
3071 redef class AAsCastExpr
3072 redef fun expr(v)
3073 do
3074 var i = v.expr(self.n_expr, null)
3075 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3076
3077 v.add_cast(i, self.mtype.as(not null), "as")
3078 return i
3079 end
3080 end
3081
3082 redef class AAsNotnullExpr
3083 redef fun expr(v)
3084 do
3085 var i = v.expr(self.n_expr, null)
3086 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3087
3088 if i.mtype.is_c_primitive then return i
3089
3090 v.add("if (unlikely({i} == NULL)) \{")
3091 v.add_abort("Cast failed")
3092 v.add("\}")
3093 return i
3094 end
3095 end
3096
3097 redef class AParExpr
3098 redef fun expr(v) do return v.expr(self.n_expr, null)
3099 end
3100
3101 redef class AOnceExpr
3102 redef fun expr(v)
3103 do
3104 var mtype = self.mtype.as(not null)
3105 var name = v.get_name("varonce")
3106 var guard = v.get_name(name + "_guard")
3107 v.add_decl("static {mtype.ctype} {name};")
3108 v.add_decl("static int {guard};")
3109 var res = v.new_var(mtype)
3110 v.add("if (likely({guard})) \{")
3111 v.add("{res} = {name};")
3112 v.add("\} else \{")
3113 var i = v.expr(self.n_expr, mtype)
3114 v.add("{res} = {i};")
3115 v.add("{name} = {res};")
3116 v.add("{guard} = 1;")
3117 v.add("\}")
3118 return res
3119 end
3120 end
3121
3122 redef class ASendExpr
3123 redef fun expr(v)
3124 do
3125 var recv = v.expr(self.n_expr, null)
3126 var callsite = self.callsite.as(not null)
3127 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3128 return v.compile_callsite(callsite, args)
3129 end
3130 end
3131
3132 redef class ASendReassignFormExpr
3133 redef fun stmt(v)
3134 do
3135 var recv = v.expr(self.n_expr, null)
3136 var callsite = self.callsite.as(not null)
3137 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3138
3139 var value = v.expr(self.n_value, null)
3140
3141 var left = v.compile_callsite(callsite, args)
3142 assert left != null
3143
3144 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3145 assert res != null
3146
3147 args.add(res)
3148 v.compile_callsite(self.write_callsite.as(not null), args)
3149 end
3150 end
3151
3152 redef class ASuperExpr
3153 redef fun expr(v)
3154 do
3155 var recv = v.frame.arguments.first
3156
3157 var callsite = self.callsite
3158 if callsite != null then
3159 var args
3160
3161 if self.n_args.n_exprs.is_empty then
3162 # Add automatic arguments for the super init call
3163 args = [recv]
3164 for i in [0..callsite.msignature.arity[ do
3165 args.add(v.frame.arguments[i+1])
3166 end
3167 else
3168 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3169 end
3170
3171 # Super init call
3172 var res = v.compile_callsite(callsite, args)
3173 return res
3174 end
3175
3176 var mpropdef = self.mpropdef.as(not null)
3177
3178 var args
3179 if self.n_args.n_exprs.is_empty then
3180 args = v.frame.arguments
3181 else
3182 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3183 end
3184
3185 # Standard call-next-method
3186 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3187 end
3188 end
3189
3190 redef class ANewExpr
3191 redef fun expr(v)
3192 do
3193 var mtype = self.recvtype
3194 assert mtype != null
3195
3196 if mtype.mclass.name == "NativeArray" then
3197 assert self.n_args.n_exprs.length == 1
3198 var l = v.expr(self.n_args.n_exprs.first, null)
3199 assert mtype isa MGenericType
3200 var elttype = mtype.arguments.first
3201 return v.native_array_instance(elttype, l)
3202 end
3203
3204 var recv = v.init_instance_or_extern(mtype)
3205
3206 var callsite = self.callsite
3207 if callsite == null then return recv
3208
3209 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3210 var res2 = v.compile_callsite(callsite, args)
3211 if res2 != null then
3212 #self.debug("got {res2} from {mproperty}. drop {recv}")
3213 return res2
3214 end
3215 return recv
3216 end
3217 end
3218
3219 redef class AAttrExpr
3220 redef fun expr(v)
3221 do
3222 var recv = v.expr(self.n_expr, null)
3223 var mproperty = self.mproperty.as(not null)
3224 return v.read_attribute(mproperty, recv)
3225 end
3226 end
3227
3228 redef class AAttrAssignExpr
3229 redef fun expr(v)
3230 do
3231 var recv = v.expr(self.n_expr, null)
3232 var i = v.expr(self.n_value, null)
3233 var mproperty = self.mproperty.as(not null)
3234 v.write_attribute(mproperty, recv, i)
3235 return i
3236 end
3237 end
3238
3239 redef class AAttrReassignExpr
3240 redef fun stmt(v)
3241 do
3242 var recv = v.expr(self.n_expr, null)
3243 var value = v.expr(self.n_value, null)
3244 var mproperty = self.mproperty.as(not null)
3245 var attr = v.read_attribute(mproperty, recv)
3246 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3247 assert res != null
3248 v.write_attribute(mproperty, recv, res)
3249 end
3250 end
3251
3252 redef class AIssetAttrExpr
3253 redef fun expr(v)
3254 do
3255 var recv = v.expr(self.n_expr, null)
3256 var mproperty = self.mproperty.as(not null)
3257 return v.isset_attribute(mproperty, recv)
3258 end
3259 end
3260
3261 redef class AVarargExpr
3262 redef fun expr(v)
3263 do
3264 return v.expr(self.n_expr, null)
3265 end
3266 end
3267
3268 redef class ANamedargExpr
3269 redef fun expr(v)
3270 do
3271 return v.expr(self.n_expr, null)
3272 end
3273 end
3274
3275 redef class ADebugTypeExpr
3276 redef fun stmt(v)
3277 do
3278 # do nothing
3279 end
3280 end
3281
3282 # Utils
3283
3284 redef class Array[E]
3285 # Return a new `Array` with the elements only contened in self and not in `o`
3286 fun -(o: Array[E]): Array[E] do
3287 var res = new Array[E]
3288 for e in self do if not o.has(e) then res.add(e)
3289 return res
3290 end
3291 end
3292
3293 redef class MModule
3294 # All `MProperty` associated to all `MClassDef` of `mclass`
3295 fun properties(mclass: MClass): Set[MProperty] do
3296 if not self.properties_cache.has_key(mclass) then
3297 var properties = new HashSet[MProperty]
3298 var parents = new Array[MClass]
3299 if self.flatten_mclass_hierarchy.has(mclass) then
3300 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3301 end
3302 for parent in parents do
3303 properties.add_all(self.properties(parent))
3304 end
3305 for mclassdef in mclass.mclassdefs do
3306 if not self.in_importation <= mclassdef.mmodule then continue
3307 for mprop in mclassdef.intro_mproperties do
3308 properties.add(mprop)
3309 end
3310 end
3311 self.properties_cache[mclass] = properties
3312 end
3313 return properties_cache[mclass]
3314 end
3315 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3316
3317 # Write FFI and nitni results to file
3318 fun finalize_ffi(c: AbstractCompiler) do end
3319
3320 # Give requided addinional system libraries (as given to LD_LIBS)
3321 # Note: can return null instead of an empty set
3322 fun collect_linker_libs: nullable Array[String] do return null
3323 end
3324
3325 # Create a tool context to handle options and paths
3326 var toolcontext = new ToolContext
3327
3328 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3329
3330 # We do not add other options, so process them now!
3331 toolcontext.process_options(args)
3332
3333 # We need a model to collect stufs
3334 var model = new Model
3335 # An a model builder to parse files
3336 var modelbuilder = new ModelBuilder(model, toolcontext)
3337
3338 var arguments = toolcontext.option_context.rest
3339 if arguments.length > 1 and toolcontext.opt_output.value != null then
3340 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3341 exit 1
3342 end
3343
3344 # Here we load an process all modules passed on the command line
3345 var mmodules = modelbuilder.parse(arguments)
3346
3347 if mmodules.is_empty then return
3348 modelbuilder.run_phases
3349
3350 for mmodule in mmodules do
3351 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3352 var ms = [mmodule]
3353 toolcontext.run_global_phases(ms)
3354 end