typing: move vararg_length on each argument, instead of the whole signature
[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("Filename of the generated executable", "-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 the 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 the `make` command", "--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 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)", "-g", "--debug")
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 <inttypes.h>\n")
658 self.header.add_decl("#include \"gc_chooser.h\"")
659 self.header.add_decl("#ifdef ANDROID")
660 self.header.add_decl(" #include <android/log.h>")
661 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
662 self.header.add_decl("#else")
663 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
664 self.header.add_decl("#endif")
665
666 compile_header_structs
667 compile_nitni_structs
668
669 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
670 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
671 # Signal handler function prototype
672 self.header.add_decl("void fatal_exit(int);")
673 else
674 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
675 end
676
677 if gccd_disable.has("likely") or gccd_disable.has("all") then
678 self.header.add_decl("#define likely(x) (x)")
679 self.header.add_decl("#define unlikely(x) (x)")
680 else if gccd_disable.has("correct-likely") then
681 # invert the `likely` definition
682 # Used by masochists to bench the worst case
683 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
684 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
685 else
686 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
687 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
688 end
689
690 # Global variable used by intern methods
691 self.header.add_decl("extern int glob_argc;")
692 self.header.add_decl("extern char **glob_argv;")
693 self.header.add_decl("extern val *glob_sys;")
694 end
695
696 # Declaration of structures for live Nit types
697 protected fun compile_header_structs is abstract
698
699 # Declaration of structures for nitni undelying the FFI
700 protected fun compile_nitni_structs
701 do
702 self.header.add_decl """
703 /* Native reference to Nit objects */
704 /* This structure is used to represent every Nit type in extern methods and custom C code. */
705 struct nitni_ref {
706 struct nitni_ref *next,
707 *prev; /* adjacent global references in global list */
708 int count; /* number of time this global reference has been marked */
709 };
710
711 /* List of global references from C code to Nit objects */
712 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
713 struct nitni_global_ref_list_t {
714 struct nitni_ref *head, *tail;
715 };
716 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
717
718 /* Initializer of global reference list */
719 extern void nitni_global_ref_list_init();
720
721 /* Intern function to add a global reference to the list */
722 extern void nitni_global_ref_add( struct nitni_ref *ref );
723
724 /* Intern function to remove a global reference from the list */
725 extern void nitni_global_ref_remove( struct nitni_ref *ref );
726
727 /* Increase count on an existing global reference */
728 extern void nitni_global_ref_incr( struct nitni_ref *ref );
729
730 /* Decrease count on an existing global reference */
731 extern void nitni_global_ref_decr( struct nitni_ref *ref );
732 """
733 end
734
735 fun compile_finalizer_function
736 do
737 var finalizable_type = mainmodule.finalizable_type
738 if finalizable_type == null then return
739
740 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
741
742 if finalize_meth == null then
743 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
744 return
745 end
746
747 var v = self.new_visitor
748 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
749 var recv = v.new_expr("obj", finalizable_type)
750 v.send(finalize_meth, [recv])
751 v.add "\}"
752 end
753
754 # Generate the main C function.
755 #
756 # This function:
757 #
758 # * allocate the Sys object if it exists
759 # * call init if is exists
760 # * call main if it exists
761 fun compile_main_function
762 do
763 var v = self.new_visitor
764 v.add_decl("#include <signal.h>")
765 var platform = target_platform
766
767 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
768
769 if platform.supports_libunwind then
770 v.add_decl("#ifndef NO_STACKTRACE")
771 v.add_decl("#define UNW_LOCAL_ONLY")
772 v.add_decl("#include <libunwind.h>")
773 v.add_decl("#include \"c_functions_hash.h\"")
774 v.add_decl("#endif")
775 end
776 v.add_decl("int glob_argc;")
777 v.add_decl("char **glob_argv;")
778 v.add_decl("val *glob_sys;")
779
780 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
781 for tag in count_type_test_tags do
782 v.add_decl("long count_type_test_resolved_{tag};")
783 v.add_decl("long count_type_test_unresolved_{tag};")
784 v.add_decl("long count_type_test_skipped_{tag};")
785 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
786 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
787 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
788 end
789 end
790
791 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
792 v.add_decl("long count_invoke_by_tables;")
793 v.add_decl("long count_invoke_by_direct;")
794 v.add_decl("long count_invoke_by_inline;")
795 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
796 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
797 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
798 end
799
800 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
801 v.add_decl("long count_attr_reads = 0;")
802 v.add_decl("long count_isset_checks = 0;")
803 v.compiler.header.add_decl("extern long count_attr_reads;")
804 v.compiler.header.add_decl("extern long count_isset_checks;")
805 end
806
807 v.add_decl("static void show_backtrace(void) \{")
808 if platform.supports_libunwind then
809 v.add_decl("#ifndef NO_STACKTRACE")
810 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
811 v.add_decl("unw_cursor_t cursor;")
812 v.add_decl("if(opt==NULL)\{")
813 v.add_decl("unw_context_t uc;")
814 v.add_decl("unw_word_t ip;")
815 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
816 v.add_decl("unw_getcontext(&uc);")
817 v.add_decl("unw_init_local(&cursor, &uc);")
818 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
819 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
820 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
821 v.add_decl("while (unw_step(&cursor) > 0) \{")
822 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
823 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
824 v.add_decl(" if (recv != NULL)\{")
825 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
826 v.add_decl(" \}else\{")
827 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
828 v.add_decl(" \}")
829 v.add_decl("\}")
830 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
831 v.add_decl("free(procname);")
832 v.add_decl("\}")
833 v.add_decl("#endif /* NO_STACKTRACE */")
834 end
835 v.add_decl("\}")
836
837 v.add_decl("void sig_handler(int signo)\{")
838 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
839 v.add_decl("show_backtrace();")
840 # rethrows
841 v.add_decl("signal(signo, SIG_DFL);")
842 v.add_decl("kill(getpid(), signo);")
843 v.add_decl("\}")
844
845 v.add_decl("void fatal_exit(int status) \{")
846 v.add_decl("show_backtrace();")
847 v.add_decl("exit(status);")
848 v.add_decl("\}")
849
850 if no_main then
851 v.add_decl("int nit_main(int argc, char** argv) \{")
852 else
853 v.add_decl("int main(int argc, char** argv) \{")
854 end
855
856 v.add("signal(SIGABRT, sig_handler);")
857 v.add("signal(SIGFPE, sig_handler);")
858 v.add("signal(SIGILL, sig_handler);")
859 v.add("signal(SIGINT, sig_handler);")
860 v.add("signal(SIGTERM, sig_handler);")
861 v.add("signal(SIGSEGV, sig_handler);")
862 v.add("signal(SIGPIPE, SIG_IGN);")
863
864 v.add("glob_argc = argc; glob_argv = argv;")
865 v.add("initialize_gc_option();")
866
867 v.add "initialize_nitni_global_refs();"
868
869 var main_type = mainmodule.sys_type
870 if main_type != null then
871 var mainmodule = v.compiler.mainmodule
872 var glob_sys = v.init_instance(main_type)
873 v.add("glob_sys = {glob_sys};")
874 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
875 if main_init != null then
876 v.send(main_init, [glob_sys])
877 end
878 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
879 mainmodule.try_get_primitive_method("main", main_type.mclass)
880 if main_method != null then
881 v.send(main_method, [glob_sys])
882 end
883 end
884
885 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
886 v.add_decl("long count_type_test_resolved_total = 0;")
887 v.add_decl("long count_type_test_unresolved_total = 0;")
888 v.add_decl("long count_type_test_skipped_total = 0;")
889 v.add_decl("long count_type_test_total_total = 0;")
890 for tag in count_type_test_tags do
891 v.add_decl("long count_type_test_total_{tag};")
892 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
893 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
894 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
895 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
896 v.add("count_type_test_total_total += count_type_test_total_{tag};")
897 end
898 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
899 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
900 var tags = count_type_test_tags.to_a
901 tags.add("total")
902 for tag in tags do
903 v.add("printf(\"{tag}\");")
904 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
905 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
906 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
907 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
908 end
909 end
910
911 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
912 v.add_decl("long count_invoke_total;")
913 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
914 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
915 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
916 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
917 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
918 end
919
920 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
921 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
922 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
923 end
924
925 v.add("return 0;")
926 v.add("\}")
927
928 for m in mainmodule.in_importation.greaters do
929 var f = "FILE_"+m.c_name
930 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
931 provide_declaration(f, "extern const char {f}[];")
932 end
933 end
934
935 # Copile all C functions related to the [incr|decr]_ref features of the FFI
936 fun compile_nitni_global_ref_functions
937 do
938 var v = self.new_visitor
939 v.add """
940 struct nitni_global_ref_list_t *nitni_global_ref_list;
941 void initialize_nitni_global_refs() {
942 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
943 nitni_global_ref_list->head = NULL;
944 nitni_global_ref_list->tail = NULL;
945 }
946
947 void nitni_global_ref_add( struct nitni_ref *ref ) {
948 if ( nitni_global_ref_list->head == NULL ) {
949 nitni_global_ref_list->head = ref;
950 ref->prev = NULL;
951 } else {
952 nitni_global_ref_list->tail->next = ref;
953 ref->prev = nitni_global_ref_list->tail;
954 }
955 nitni_global_ref_list->tail = ref;
956
957 ref->next = NULL;
958 }
959
960 void nitni_global_ref_remove( struct nitni_ref *ref ) {
961 if ( ref->prev == NULL ) {
962 nitni_global_ref_list->head = ref->next;
963 } else {
964 ref->prev->next = ref->next;
965 }
966
967 if ( ref->next == NULL ) {
968 nitni_global_ref_list->tail = ref->prev;
969 } else {
970 ref->next->prev = ref->prev;
971 }
972 }
973
974 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
975 if ( ref->count == 0 ) /* not registered */
976 {
977 /* add to list */
978 nitni_global_ref_add( ref );
979 }
980
981 ref->count ++;
982 }
983
984 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
985 if ( ref->count == 1 ) /* was last reference */
986 {
987 /* remove from list */
988 nitni_global_ref_remove( ref );
989 }
990
991 ref->count --;
992 }
993 """
994 end
995
996 # List of additional files required to compile (FFI)
997 var extern_bodies = new Array[ExternFile]
998
999 # List of source files to copy over to the compile dir
1000 var files_to_copy = new Array[String]
1001
1002 # This is used to avoid adding an extern file more than once
1003 private var seen_extern = new ArraySet[String]
1004
1005 # Generate code that initialize the attributes on a new instance
1006 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1007 do
1008 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1009 self.mainmodule.linearize_mclassdefs(cds)
1010 for cd in cds do
1011 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1012 npropdef.init_expr(v, recv)
1013 end
1014 end
1015 end
1016
1017 # Generate code that check if an attribute is correctly initialized
1018 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1019 do
1020 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1021 self.mainmodule.linearize_mclassdefs(cds)
1022 for cd in cds do
1023 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1024 npropdef.check_expr(v, recv)
1025 end
1026 end
1027 end
1028
1029 # stats
1030
1031 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1032 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1033 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1034 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1035
1036 protected fun init_count_type_test_tags: HashMap[String, Int]
1037 do
1038 var res = new HashMap[String, Int]
1039 for tag in count_type_test_tags do
1040 res[tag] = 0
1041 end
1042 return res
1043 end
1044
1045 # Display stats about compilation process
1046 #
1047 # Metrics used:
1048 #
1049 # * type tests against resolved types (`x isa Collection[Animal]`)
1050 # * type tests against unresolved types (`x isa Collection[E]`)
1051 # * type tests skipped
1052 # * type tests total
1053 fun display_stats
1054 do
1055 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1056 print "# static count_type_test"
1057 print "\tresolved:\tunresolved\tskipped\ttotal"
1058 var count_type_test_total = init_count_type_test_tags
1059 count_type_test_resolved["total"] = 0
1060 count_type_test_unresolved["total"] = 0
1061 count_type_test_skipped["total"] = 0
1062 count_type_test_total["total"] = 0
1063 for tag in count_type_test_tags do
1064 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1065 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1066 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1067 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1068 count_type_test_total["total"] += count_type_test_total[tag]
1069 end
1070 var count_type_test = count_type_test_total["total"]
1071 var tags = count_type_test_tags.to_a
1072 tags.add("total")
1073 for tag in tags do
1074 printn tag
1075 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1076 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1077 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1078 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1079 print ""
1080 end
1081 end
1082 end
1083
1084 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1085 end
1086
1087 # A file unit (may be more than one file if
1088 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1089 class CodeFile
1090 var name: String
1091 var writers = new Array[CodeWriter]
1092 var required_declarations = new HashSet[String]
1093 end
1094
1095 # Where to store generated lines
1096 class CodeWriter
1097 var file: CodeFile
1098 var lines: List[String] = new List[String]
1099 var decl_lines: List[String] = new List[String]
1100
1101 # Add a line in the main part of the generated C
1102 fun add(s: String) do self.lines.add(s)
1103
1104 # Add a line in the
1105 # (used for local or global declaration)
1106 fun add_decl(s: String) do self.decl_lines.add(s)
1107
1108 init
1109 do
1110 file.writers.add(self)
1111 end
1112 end
1113
1114 # A visitor on the AST of property definition that generate the C code.
1115 abstract class AbstractCompilerVisitor
1116
1117 type COMPILER: AbstractCompiler
1118
1119 # The associated compiler
1120 var compiler: COMPILER
1121
1122 # The current visited AST node
1123 var current_node: nullable ANode = null is writable
1124
1125 # The current `StaticFrame`
1126 var frame: nullable StaticFrame = null is writable
1127
1128 # Alias for self.compiler.mainmodule.object_type
1129 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1130
1131 # Alias for self.compiler.mainmodule.bool_type
1132 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1133
1134 var writer: CodeWriter is noinit
1135
1136 init
1137 do
1138 self.writer = new CodeWriter(compiler.files.last)
1139 end
1140
1141 # Force to get the primitive property named `name` in the instance `recv` or abort
1142 fun get_property(name: String, recv: MType): MMethod
1143 do
1144 assert recv isa MClassType
1145 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1146 end
1147
1148 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1149 do
1150 if callsite.is_broken then return null
1151 var initializers = callsite.mpropdef.initializers
1152 if not initializers.is_empty then
1153 var recv = arguments.first
1154
1155 var i = 1
1156 for p in initializers do
1157 if p isa MMethod then
1158 var args = [recv]
1159 for x in p.intro.msignature.mparameters do
1160 args.add arguments[i]
1161 i += 1
1162 end
1163 self.send(p, args)
1164 else if p isa MAttribute then
1165 self.write_attribute(p, recv, arguments[i])
1166 i += 1
1167 else abort
1168 end
1169 assert i == arguments.length
1170
1171 return self.send(callsite.mproperty, [recv])
1172 end
1173
1174 return self.send(callsite.mproperty, arguments)
1175 end
1176
1177 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1178
1179 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1180
1181 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1182
1183 # Return an element of a native array.
1184 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1185 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1186
1187 # Store an element in a native array.
1188 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1189 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1190
1191 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1192 # This method is used to manage varargs in signatures and returns the real array
1193 # of runtime variables to use in the call.
1194 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1195 do
1196 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1197 var res = new Array[RuntimeVariable]
1198 res.add(recv)
1199
1200 if msignature.arity == 0 then return res
1201
1202 if map == null then
1203 assert args.length == msignature.arity
1204 for ne in args do
1205 res.add self.expr(ne, null)
1206 end
1207 return res
1208 end
1209
1210 # Eval in order of arguments, not parameters
1211 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1212 for ne in args do
1213 exprs.add self.expr(ne, null)
1214 end
1215
1216 # Fill `res` with the result of the evaluation according to the mapping
1217 for i in [0..msignature.arity[ do
1218 var param = msignature.mparameters[i]
1219 var j = map.map.get_or_null(i)
1220 if j == null then
1221 # default value
1222 res.add(null_instance)
1223 continue
1224 end
1225 if param.is_vararg and args[i].vararg_decl > 0 then
1226 var vararg = exprs.sub(j, args[i].vararg_decl)
1227 var elttype = param.mtype
1228 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1229 res.add(arg)
1230 continue
1231 end
1232 res.add exprs[j]
1233 end
1234 return res
1235 end
1236
1237 # Type handling
1238
1239 # Anchor a type to the main module and the current receiver
1240 fun anchor(mtype: MType): MType
1241 do
1242 if not mtype.need_anchor then return mtype
1243 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1244 end
1245
1246 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1247 do
1248 if not mtype.need_anchor then return mtype
1249 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1250 end
1251
1252 # Unsafely cast a value to a new type
1253 # ie the result share the same C variable but my have a different mcasttype
1254 # NOTE: if the adaptation is useless then `value` is returned as it.
1255 # ENSURE: `result.name == value.name`
1256 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1257 do
1258 mtype = self.anchor(mtype)
1259 var valmtype = value.mcasttype
1260 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1261 return value
1262 end
1263
1264 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1265 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1266 return res
1267 else
1268 var res = new RuntimeVariable(value.name, valmtype, mtype)
1269 return res
1270 end
1271 end
1272
1273 # Generate a super call from a method definition
1274 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1275
1276 # Adapt the arguments of a method according to targetted `MMethodDef`
1277 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1278
1279 # Unbox all the arguments of a method when implemented `extern` or `intern`
1280 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1281
1282 # Box or unbox a value to another type iff a C type conversion is needed
1283 # ENSURE: `result.mtype.ctype == mtype.ctype`
1284 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1285
1286 # Box extern classes to be used in the generated code
1287 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1288
1289 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1290 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1291
1292 # Generate a polymorphic subtype test
1293 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1294
1295 # Generate the code required to dynamically check if 2 objects share the same runtime type
1296 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1297
1298 # Generate a Nit "is" for two runtime_variables
1299 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1300
1301 # Sends
1302
1303 # Generate a static call on a method definition
1304 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1305
1306 # Generate a polymorphic send for the method `m` and the arguments `args`
1307 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1308
1309 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1310 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1311 do
1312 assert t isa MClassType
1313 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1314 return self.call(propdef, t, args)
1315 end
1316
1317 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1318 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1319 do
1320 assert t isa MClassType
1321 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1322 return self.call(m, t, args)
1323 end
1324
1325 # Attributes handling
1326
1327 # Generate a polymorphic attribute is_set test
1328 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1329
1330 # Generate a polymorphic attribute read
1331 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1332
1333 # Generate a polymorphic attribute write
1334 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1335
1336 # Checks
1337
1338 # Add a check and an abort for a null receiver if needed
1339 fun check_recv_notnull(recv: RuntimeVariable)
1340 do
1341 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1342
1343 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1344 if maybenull then
1345 self.add("if (unlikely({recv} == NULL)) \{")
1346 self.add_abort("Receiver is null")
1347 self.add("\}")
1348 end
1349 end
1350
1351 # Names handling
1352
1353 private var names = new HashSet[String]
1354 private var last: Int = 0
1355
1356 # Return a new name based on `s` and unique in the visitor
1357 fun get_name(s: String): String
1358 do
1359 if not self.names.has(s) then
1360 self.names.add(s)
1361 return s
1362 end
1363 var i = self.last + 1
1364 loop
1365 var s2 = s + i.to_s
1366 if not self.names.has(s2) then
1367 self.last = i
1368 self.names.add(s2)
1369 return s2
1370 end
1371 i = i + 1
1372 end
1373 end
1374
1375 # Return an unique and stable identifier associated with an escapemark
1376 fun escapemark_name(e: nullable EscapeMark): String
1377 do
1378 assert e != null
1379 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1380 var name = e.name
1381 if name == null then name = "label"
1382 name = get_name(name)
1383 frame.escapemark_names[e] = name
1384 return name
1385 end
1386
1387 # Insert a C label for associated with an escapemark
1388 fun add_escape_label(e: nullable EscapeMark)
1389 do
1390 if e == null then return
1391 if e.escapes.is_empty then return
1392 add("BREAK_{escapemark_name(e)}: (void)0;")
1393 end
1394
1395 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1396 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1397 fun class_name_string(value: RuntimeVariable): String is abstract
1398
1399 # Variables handling
1400
1401 protected var variables = new HashMap[Variable, RuntimeVariable]
1402
1403 # Return the local runtime_variable associated to a Nit local variable
1404 fun variable(variable: Variable): RuntimeVariable
1405 do
1406 if self.variables.has_key(variable) then
1407 return self.variables[variable]
1408 else
1409 var name = self.get_name("var_{variable.name}")
1410 var mtype = variable.declared_type.as(not null)
1411 mtype = self.anchor(mtype)
1412 var res = new RuntimeVariable(name, mtype, mtype)
1413 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1414 self.variables[variable] = res
1415 return res
1416 end
1417 end
1418
1419 # Return a new uninitialized local runtime_variable
1420 fun new_var(mtype: MType): RuntimeVariable
1421 do
1422 mtype = self.anchor(mtype)
1423 var name = self.get_name("var")
1424 var res = new RuntimeVariable(name, mtype, mtype)
1425 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1426 return res
1427 end
1428
1429 # The difference with `new_var` is the C static type of the local variable
1430 fun new_var_extern(mtype: MType): RuntimeVariable
1431 do
1432 mtype = self.anchor(mtype)
1433 var name = self.get_name("var")
1434 var res = new RuntimeVariable(name, mtype, mtype)
1435 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1436 return res
1437 end
1438
1439 # Return a new uninitialized named runtime_variable
1440 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1441 do
1442 mtype = self.anchor(mtype)
1443 var res = new RuntimeVariable(name, mtype, mtype)
1444 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1445 return res
1446 end
1447
1448 # Correctly assign a left and a right value
1449 # Boxing and unboxing is performed if required
1450 fun assign(left, right: RuntimeVariable)
1451 do
1452 right = self.autobox(right, left.mtype)
1453 self.add("{left} = {right};")
1454 end
1455
1456 # Generate instances
1457
1458 # Generate a alloc-instance + init-attributes
1459 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1460
1461 # Allocate and init attributes of an instance of a standard or extern class
1462 #
1463 # Does not support universals and the pseudo-internal `NativeArray` class.
1464 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1465 do
1466 var recv
1467 var ctype = mtype.ctype
1468 assert mtype.mclass.name != "NativeArray"
1469 if not mtype.is_c_primitive then
1470 recv = init_instance(mtype)
1471 else if ctype == "char*" then
1472 recv = new_expr("NULL/*special!*/", mtype)
1473 else
1474 recv = new_expr("({ctype})0/*special!*/", mtype)
1475 end
1476 return recv
1477 end
1478
1479 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1480 fun set_finalizer(recv: RuntimeVariable)
1481 do
1482 var mtype = recv.mtype
1483 var finalizable_type = compiler.mainmodule.finalizable_type
1484 if finalizable_type != null and not mtype.need_anchor and
1485 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1486 add "gc_register_finalizer({recv});"
1487 end
1488 end
1489
1490 # The currently processed module
1491 #
1492 # alias for `compiler.mainmodule`
1493 fun mmodule: MModule do return compiler.mainmodule
1494
1495 # Generate an integer value
1496 fun int_instance(value: Int): RuntimeVariable
1497 do
1498 var t = mmodule.int_type
1499 var res = new RuntimeVariable("{value.to_s}l", t, t)
1500 return res
1501 end
1502
1503 # Generate a byte value
1504 fun byte_instance(value: Byte): RuntimeVariable
1505 do
1506 var t = mmodule.byte_type
1507 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1508 return res
1509 end
1510
1511 # Generate an int8 value
1512 fun int8_instance(value: Int8): RuntimeVariable
1513 do
1514 var t = mmodule.int8_type
1515 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1516 return res
1517 end
1518
1519 # Generate an int16 value
1520 fun int16_instance(value: Int16): RuntimeVariable
1521 do
1522 var t = mmodule.int16_type
1523 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1524 return res
1525 end
1526
1527 # Generate a uint16 value
1528 fun uint16_instance(value: UInt16): RuntimeVariable
1529 do
1530 var t = mmodule.uint16_type
1531 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1532 return res
1533 end
1534
1535 # Generate an int32 value
1536 fun int32_instance(value: Int32): RuntimeVariable
1537 do
1538 var t = mmodule.int32_type
1539 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1540 return res
1541 end
1542
1543 # Generate a uint32 value
1544 fun uint32_instance(value: UInt32): RuntimeVariable
1545 do
1546 var t = mmodule.uint32_type
1547 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1548 return res
1549 end
1550
1551 # Generate a char value
1552 fun char_instance(value: Char): RuntimeVariable
1553 do
1554 var t = mmodule.char_type
1555
1556 if value.code_point < 128 then
1557 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1558 else
1559 return new RuntimeVariable("{value.code_point}", t, t)
1560 end
1561 end
1562
1563 # Generate a float value
1564 #
1565 # FIXME pass a Float, not a string
1566 fun float_instance(value: String): RuntimeVariable
1567 do
1568 var t = mmodule.float_type
1569 var res = new RuntimeVariable("{value}", t, t)
1570 return res
1571 end
1572
1573 # Generate an integer value
1574 fun bool_instance(value: Bool): RuntimeVariable
1575 do
1576 var s = if value then "1" else "0"
1577 var res = new RuntimeVariable(s, bool_type, bool_type)
1578 return res
1579 end
1580
1581 # Generate the `null` value
1582 fun null_instance: RuntimeVariable
1583 do
1584 var t = compiler.mainmodule.model.null_type
1585 var res = new RuntimeVariable("((val*)NULL)", t, t)
1586 return res
1587 end
1588
1589 # Generate a string value
1590 fun string_instance(string: String): RuntimeVariable
1591 do
1592 var mtype = mmodule.string_type
1593 var name = self.get_name("varonce")
1594 self.add_decl("static {mtype.ctype} {name};")
1595 var res = self.new_var(mtype)
1596 self.add("if (likely({name}!=NULL)) \{")
1597 self.add("{res} = {name};")
1598 self.add("\} else \{")
1599 var native_mtype = mmodule.native_string_type
1600 var nat = self.new_var(native_mtype)
1601 self.add("{nat} = \"{string.escape_to_c}\";")
1602 var bytelen = self.int_instance(string.bytelen)
1603 var unilen = self.int_instance(string.length)
1604 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1605 self.add("{name} = {res};")
1606 self.add("\}")
1607 return res
1608 end
1609
1610 fun value_instance(object: Object): RuntimeVariable
1611 do
1612 if object isa Int then
1613 return int_instance(object)
1614 else if object isa Bool then
1615 return bool_instance(object)
1616 else if object isa String then
1617 return string_instance(object)
1618 else
1619 abort
1620 end
1621 end
1622
1623 # Generate an array value
1624 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1625
1626 # Get an instance of a array for a vararg
1627 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1628
1629 # Code generation
1630
1631 # Add a line in the main part of the generated C
1632 fun add(s: String) do self.writer.lines.add(s)
1633
1634 # Add a line in the
1635 # (used for local or global declaration)
1636 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1637
1638 # Request the presence of a global declaration
1639 fun require_declaration(key: String)
1640 do
1641 var reqs = self.writer.file.required_declarations
1642 if reqs.has(key) then return
1643 reqs.add(key)
1644 var node = current_node
1645 if node != null then compiler.requirers_of_declarations[key] = node
1646 end
1647
1648 # Add a declaration in the local-header
1649 # The declaration is ensured to be present once
1650 fun declare_once(s: String)
1651 do
1652 self.compiler.provide_declaration(s, s)
1653 self.require_declaration(s)
1654 end
1655
1656 # Look for a needed .h and .c file for a given module
1657 # This is used for the legacy FFI
1658 fun add_extern(mmodule: MModule)
1659 do
1660 var file = mmodule.location.file.filename
1661 file = file.strip_extension(".nit")
1662 var tryfile = file + ".nit.h"
1663 if tryfile.file_exists then
1664 self.declare_once("#include \"{tryfile.basename}\"")
1665 self.compiler.files_to_copy.add(tryfile)
1666 end
1667 tryfile = file + "_nit.h"
1668 if tryfile.file_exists then
1669 self.declare_once("#include \"{tryfile.basename}\"")
1670 self.compiler.files_to_copy.add(tryfile)
1671 end
1672
1673 if self.compiler.seen_extern.has(file) then return
1674 self.compiler.seen_extern.add(file)
1675 tryfile = file + ".nit.c"
1676 if not tryfile.file_exists then
1677 tryfile = file + "_nit.c"
1678 if not tryfile.file_exists then return
1679 end
1680 var f = new ExternCFile(tryfile.basename, "")
1681 self.compiler.extern_bodies.add(f)
1682 self.compiler.files_to_copy.add(tryfile)
1683 end
1684
1685 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1686 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1687 do
1688 var res = new_var(mtype)
1689 self.add("{res} = {cexpr};")
1690 return res
1691 end
1692
1693 # Generate generic abort
1694 # used by aborts, asserts, casts, etc.
1695 fun add_abort(message: String)
1696 do
1697 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1698 add_raw_abort
1699 end
1700
1701 fun add_raw_abort
1702 do
1703 if self.current_node != null and self.current_node.location.file != null and
1704 self.current_node.location.file.mmodule != null then
1705 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1706 self.require_declaration(f)
1707 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1708 else
1709 self.add("PRINT_ERROR(\"\\n\");")
1710 end
1711 self.add("fatal_exit(1);")
1712 end
1713
1714 # Add a dynamic cast
1715 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1716 do
1717 var res = self.type_test(value, mtype, tag)
1718 self.add("if (unlikely(!{res})) \{")
1719 var cn = self.class_name_string(value)
1720 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1721 self.add_raw_abort
1722 self.add("\}")
1723 end
1724
1725 # Generate a return with the value `s`
1726 fun ret(s: RuntimeVariable)
1727 do
1728 self.assign(self.frame.returnvar.as(not null), s)
1729 self.add("goto {self.frame.returnlabel.as(not null)};")
1730 end
1731
1732 # Compile a statement (if any)
1733 fun stmt(nexpr: nullable AExpr)
1734 do
1735 if nexpr == null then return
1736 if nexpr.is_broken then
1737 # Untyped expression.
1738 # Might mean dead code or invalid code
1739 # so aborts
1740 add_abort("FATAL: bad statement executed.")
1741 return
1742 end
1743
1744 var narray = nexpr.comprehension
1745 if narray != null then
1746 var recv = frame.comprehension.as(not null)
1747 var val = expr(nexpr, narray.element_mtype)
1748 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1749 return
1750 end
1751
1752 var old = self.current_node
1753 self.current_node = nexpr
1754 nexpr.stmt(self)
1755 self.current_node = old
1756 end
1757
1758 # Compile an expression an return its result
1759 # `mtype` is the expected return type, pass null if no specific type is expected.
1760 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1761 do
1762 var old = self.current_node
1763 self.current_node = nexpr
1764
1765 var res = null
1766 if nexpr.mtype != null then
1767 res = nexpr.expr(self)
1768 end
1769
1770 if res == null then
1771 # Untyped expression.
1772 # Might mean dead code or invalid code.
1773 # so aborts
1774 add_abort("FATAL: bad expression executed.")
1775 # and return a placebo result to please the C compiler
1776 if mtype == null then mtype = compiler.mainmodule.object_type
1777 res = new_var(mtype)
1778
1779 self.current_node = old
1780 return res
1781 end
1782
1783 if mtype != null then
1784 mtype = self.anchor(mtype)
1785 res = self.autobox(res, mtype)
1786 end
1787 res = autoadapt(res, nexpr.mtype.as(not null))
1788 var implicit_cast_to = nexpr.implicit_cast_to
1789 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1790 add_cast(res, implicit_cast_to, "auto")
1791 res = autoadapt(res, implicit_cast_to)
1792 end
1793 self.current_node = old
1794 return res
1795 end
1796
1797 # Alias for `self.expr(nexpr, self.bool_type)`
1798 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1799
1800 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1801 fun debug(message: String)
1802 do
1803 var node = self.current_node
1804 if node == null then
1805 print "?: {message}"
1806 else
1807 node.debug(message)
1808 end
1809 self.add("/* DEBUG: {message} */")
1810 end
1811 end
1812
1813 # A C function associated to a Nit method
1814 # Because of customization, a given Nit method can be compiler more that once
1815 abstract class AbstractRuntimeFunction
1816
1817 type COMPILER: AbstractCompiler
1818 type VISITOR: AbstractCompilerVisitor
1819
1820 # The associated Nit method
1821 var mmethoddef: MMethodDef
1822
1823 # The mangled c name of the runtime_function
1824 # Subclasses should redefine `build_c_name` instead
1825 fun c_name: String
1826 do
1827 var res = self.c_name_cache
1828 if res != null then return res
1829 res = self.build_c_name
1830 self.c_name_cache = res
1831 return res
1832 end
1833
1834 # Non cached version of `c_name`
1835 protected fun build_c_name: String is abstract
1836
1837 protected var c_name_cache: nullable String = null is writable
1838
1839 # Implements a call of the runtime_function
1840 # May inline the body or generate a C function call
1841 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1842
1843 # Generate the code for the `AbstractRuntimeFunction`
1844 # Warning: compile more than once compilation makes CC unhappy
1845 fun compile_to_c(compiler: COMPILER) is abstract
1846 end
1847
1848 # A runtime variable hold a runtime value in C.
1849 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1850 #
1851 # 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.
1852 class RuntimeVariable
1853 # The name of the variable in the C code
1854 var name: String
1855
1856 # The static type of the variable (as declard in C)
1857 var mtype: MType
1858
1859 # The current casted type of the variable (as known in Nit)
1860 var mcasttype: MType is writable
1861
1862 # If the variable exaclty a mcasttype?
1863 # false (usual value) means that the variable is a mcasttype or a subtype.
1864 var is_exact: Bool = false is writable
1865
1866 init
1867 do
1868 assert not mtype.need_anchor
1869 assert not mcasttype.need_anchor
1870 end
1871
1872 redef fun to_s do return name
1873
1874 redef fun inspect
1875 do
1876 var exact_str
1877 if self.is_exact then
1878 exact_str = " exact"
1879 else
1880 exact_str = ""
1881 end
1882 var type_str
1883 if self.mtype == self.mcasttype then
1884 type_str = "{mtype}{exact_str}"
1885 else
1886 type_str = "{mtype}({mcasttype}{exact_str})"
1887 end
1888 return "<{name}:{type_str}>"
1889 end
1890 end
1891
1892 # The static context of a visited property in a `AbstractCompilerVisitor`
1893 class StaticFrame
1894
1895 type VISITOR: AbstractCompilerVisitor
1896
1897 # The associated visitor
1898 var visitor: VISITOR
1899
1900 # The executed property.
1901 # A Method in case of a call, an attribute in case of a default initialization.
1902 var mpropdef: MPropDef
1903
1904 # The static type of the receiver
1905 var receiver: MClassType
1906
1907 # Arguments of the method (the first is the receiver)
1908 var arguments: Array[RuntimeVariable]
1909
1910 # The runtime_variable associated to the return (in a function)
1911 var returnvar: nullable RuntimeVariable = null is writable
1912
1913 # The label at the end of the property
1914 var returnlabel: nullable String = null is writable
1915
1916 # Labels associated to a each escapemarks.
1917 # Because of inlinings, escape-marks must be associated to their context (the frame)
1918 private var escapemark_names = new HashMap[EscapeMark, String]
1919
1920 # The array comprehension currently filled, if any
1921 private var comprehension: nullable RuntimeVariable = null
1922 end
1923
1924 redef class MType
1925 # Return the C type associated to a given Nit static type
1926 fun ctype: String do return "val*"
1927
1928 # C type outside of the compiler code and in boxes
1929 fun ctype_extern: String do return "val*"
1930
1931 # Short name of the `ctype` to use in unions
1932 fun ctypename: String do return "val"
1933
1934 # Is the associated C type a primitive one?
1935 #
1936 # ENSURE `result == (ctype != "val*")`
1937 fun is_c_primitive: Bool do return false
1938 end
1939
1940 redef class MClassType
1941
1942 redef var ctype is lazy do
1943 if mclass.name == "Int" then
1944 return "long"
1945 else if mclass.name == "Bool" then
1946 return "short int"
1947 else if mclass.name == "Char" then
1948 return "uint32_t"
1949 else if mclass.name == "Float" then
1950 return "double"
1951 else if mclass.name == "Int8" then
1952 return "int8_t"
1953 else if mclass.name == "Byte" then
1954 return "unsigned char"
1955 else if mclass.name == "Int16" then
1956 return "int16_t"
1957 else if mclass.name == "UInt16" then
1958 return "uint16_t"
1959 else if mclass.name == "Int32" then
1960 return "int32_t"
1961 else if mclass.name == "UInt32" then
1962 return "uint32_t"
1963 else if mclass.name == "NativeString" then
1964 return "char*"
1965 else if mclass.name == "NativeArray" then
1966 return "val*"
1967 else
1968 return "val*"
1969 end
1970 end
1971
1972 redef var is_c_primitive is lazy do return ctype != "val*"
1973
1974 redef fun ctype_extern: String
1975 do
1976 if mclass.kind == extern_kind then
1977 return "void*"
1978 else
1979 return ctype
1980 end
1981 end
1982
1983 redef fun ctypename: String
1984 do
1985 if mclass.name == "Int" then
1986 return "l"
1987 else if mclass.name == "Bool" then
1988 return "s"
1989 else if mclass.name == "Char" then
1990 return "c"
1991 else if mclass.name == "Float" then
1992 return "d"
1993 else if mclass.name == "Int8" then
1994 return "i8"
1995 else if mclass.name == "Byte" then
1996 return "b"
1997 else if mclass.name == "Int16" then
1998 return "i16"
1999 else if mclass.name == "UInt16" then
2000 return "u16"
2001 else if mclass.name == "Int32" then
2002 return "i32"
2003 else if mclass.name == "UInt32" then
2004 return "u32"
2005 else if mclass.name == "NativeString" then
2006 return "str"
2007 else if mclass.name == "NativeArray" then
2008 #return "{self.arguments.first.ctype}*"
2009 return "val"
2010 else
2011 return "val"
2012 end
2013 end
2014 end
2015
2016 redef class MPropDef
2017 type VISITOR: AbstractCompilerVisitor
2018 end
2019
2020 redef class MMethodDef
2021 # Can the body be inlined?
2022 fun can_inline(v: VISITOR): Bool
2023 do
2024 if is_abstract then return true
2025 if constant_value != null then return true
2026 var modelbuilder = v.compiler.modelbuilder
2027 var node = modelbuilder.mpropdef2node(self)
2028 if node isa APropdef then
2029 return node.can_inline
2030 else if node isa AClassdef then
2031 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2032 return true
2033 else
2034 abort
2035 end
2036 end
2037
2038 # Inline the body in another visitor
2039 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2040 do
2041 var modelbuilder = v.compiler.modelbuilder
2042 var val = constant_value
2043 var node = modelbuilder.mpropdef2node(self)
2044
2045 if is_abstract then
2046 var cn = v.class_name_string(arguments.first)
2047 v.current_node = node
2048 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2049 v.add_raw_abort
2050 return null
2051 end
2052
2053 if node isa APropdef then
2054 var oldnode = v.current_node
2055 v.current_node = node
2056 self.compile_parameter_check(v, arguments)
2057 node.compile_to_c(v, self, arguments)
2058 v.current_node = oldnode
2059 else if node isa AClassdef then
2060 var oldnode = v.current_node
2061 v.current_node = node
2062 self.compile_parameter_check(v, arguments)
2063 node.compile_to_c(v, self, arguments)
2064 v.current_node = oldnode
2065 else if val != null then
2066 v.ret(v.value_instance(val))
2067 else
2068 abort
2069 end
2070 return null
2071 end
2072
2073 # Generate type checks in the C code to check covariant parameters
2074 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2075 do
2076 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2077
2078 var msignature = self.msignature.as(not null)
2079
2080 for i in [0..msignature.arity[ do
2081 # skip test for vararg since the array is instantiated with the correct polymorphic type
2082 if msignature.vararg_rank == i then continue
2083
2084 # skip if the cast is not required
2085 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2086 if not origmtype.need_anchor then continue
2087
2088 # get the parameter type
2089 var mtype = msignature.mparameters[i].mtype
2090
2091 # generate the cast
2092 # note that v decides if and how to implements the cast
2093 v.add("/* Covariant cast for argument {i} ({msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
2094 v.add_cast(arguments[i+1], mtype, "covariance")
2095 end
2096 end
2097 end
2098
2099 # Node visit
2100
2101 redef class APropdef
2102 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2103 do
2104 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2105 debug("Not yet implemented")
2106 end
2107
2108 fun can_inline: Bool do return true
2109 end
2110
2111 redef class AMethPropdef
2112 redef fun compile_to_c(v, mpropdef, arguments)
2113 do
2114 # Call the implicit super-init
2115 var auto_super_inits = self.auto_super_inits
2116 if auto_super_inits != null then
2117 var args = [arguments.first]
2118 for auto_super_init in auto_super_inits do
2119 assert auto_super_init.mproperty != mpropdef.mproperty
2120 args.clear
2121 for i in [0..auto_super_init.msignature.arity+1[ do
2122 args.add(arguments[i])
2123 end
2124 assert auto_super_init.mproperty != mpropdef.mproperty
2125 v.compile_callsite(auto_super_init, args)
2126 end
2127 end
2128 if auto_super_call then
2129 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2130 end
2131
2132 # Try special compilation
2133 if mpropdef.is_intern then
2134 if compile_intern_to_c(v, mpropdef, arguments) then return
2135 else if mpropdef.is_extern then
2136 if mpropdef.mproperty.is_init then
2137 if compile_externinit_to_c(v, mpropdef, arguments) then return
2138 else
2139 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2140 end
2141 end
2142
2143 # Compile block if any
2144 var n_block = n_block
2145 if n_block != null then
2146 for i in [0..mpropdef.msignature.arity[ do
2147 var variable = self.n_signature.n_params[i].variable.as(not null)
2148 v.assign(v.variable(variable), arguments[i+1])
2149 end
2150 v.stmt(n_block)
2151 return
2152 end
2153
2154 # We have a problem
2155 var cn = v.class_name_string(arguments.first)
2156 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2157 v.add_raw_abort
2158 end
2159
2160 redef fun can_inline
2161 do
2162 if self.auto_super_inits != null then return false
2163 var nblock = self.n_block
2164 if nblock == null then return true
2165 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2166 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2167 return false
2168 end
2169
2170 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2171 do
2172 var pname = mpropdef.mproperty.name
2173 var cname = mpropdef.mclassdef.mclass.name
2174 var ret = mpropdef.msignature.return_mtype
2175 if ret != null then
2176 ret = v.resolve_for(ret, arguments.first)
2177 end
2178 if pname != "==" and pname != "!=" then
2179 v.adapt_signature(mpropdef, arguments)
2180 v.unbox_signature_extern(mpropdef, arguments)
2181 end
2182 if cname == "Int" then
2183 if pname == "output" then
2184 v.add("printf(\"%ld\\n\", {arguments.first});")
2185 return true
2186 else if pname == "object_id" then
2187 v.ret(arguments.first)
2188 return true
2189 else if pname == "+" then
2190 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2191 return true
2192 else if pname == "-" then
2193 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2194 return true
2195 else if pname == "unary -" then
2196 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2197 return true
2198 else if pname == "unary +" then
2199 v.ret(arguments[0])
2200 return true
2201 else if pname == "*" then
2202 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2203 return true
2204 else if pname == "/" then
2205 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2206 return true
2207 else if pname == "%" then
2208 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2209 return true
2210 else if pname == "==" then
2211 v.ret(v.equal_test(arguments[0], arguments[1]))
2212 return true
2213 else if pname == "!=" then
2214 var res = v.equal_test(arguments[0], arguments[1])
2215 v.ret(v.new_expr("!{res}", ret.as(not null)))
2216 return true
2217 else if pname == "<" then
2218 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2219 return true
2220 else if pname == ">" then
2221 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2222 return true
2223 else if pname == "<=" then
2224 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2225 return true
2226 else if pname == ">=" then
2227 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2228 return true
2229 else if pname == "to_i8" then
2230 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2231 return true
2232 else if pname == "to_i16" then
2233 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2234 return true
2235 else if pname == "to_u16" then
2236 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2237 return true
2238 else if pname == "to_i32" then
2239 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2240 return true
2241 else if pname == "to_u32" then
2242 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2243 return true
2244 else if pname == "to_f" then
2245 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2246 return true
2247 else if pname == "to_b" then
2248 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2249 return true
2250 end
2251 else if cname == "Char" then
2252 if pname == "object_id" then
2253 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2254 return true
2255 else if pname == "successor" then
2256 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2257 return true
2258 else if pname == "predecessor" then
2259 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2260 return true
2261 else if pname == "==" then
2262 v.ret(v.equal_test(arguments[0], arguments[1]))
2263 return true
2264 else if pname == "!=" then
2265 var res = v.equal_test(arguments[0], arguments[1])
2266 v.ret(v.new_expr("!{res}", ret.as(not null)))
2267 return true
2268 else if pname == "<" then
2269 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2270 return true
2271 else if pname == ">" then
2272 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2273 return true
2274 else if pname == "<=" then
2275 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2276 return true
2277 else if pname == ">=" then
2278 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2279 return true
2280 else if pname == "to_i" then
2281 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2282 return true
2283 end
2284 else if cname == "Byte" then
2285 if pname == "output" then
2286 v.add("printf(\"%x\\n\", {arguments.first});")
2287 return true
2288 else if pname == "object_id" then
2289 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2290 return true
2291 else if pname == "+" then
2292 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2293 return true
2294 else if pname == "-" then
2295 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2296 return true
2297 else if pname == "unary -" then
2298 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2299 return true
2300 else if pname == "unary +" then
2301 v.ret(arguments[0])
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 == "/" then
2307 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2308 return true
2309 else if pname == "%" then
2310 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2311 return true
2312 else if pname == "==" then
2313 v.ret(v.equal_test(arguments[0], arguments[1]))
2314 return true
2315 else if pname == "!=" then
2316 var res = v.equal_test(arguments[0], arguments[1])
2317 v.ret(v.new_expr("!{res}", ret.as(not null)))
2318 return true
2319 else if pname == "<" then
2320 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2321 return true
2322 else if pname == ">" then
2323 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2324 return true
2325 else if pname == "<=" then
2326 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2327 return true
2328 else if pname == ">=" then
2329 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2330 return true
2331 else if pname == "to_i" then
2332 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2333 return true
2334 else if pname == "to_f" then
2335 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2336 return true
2337 else if pname == "to_i8" then
2338 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2339 return true
2340 else if pname == "to_i16" then
2341 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2342 return true
2343 else if pname == "to_u16" then
2344 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2345 return true
2346 else if pname == "to_i32" then
2347 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2348 return true
2349 else if pname == "to_u32" then
2350 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2351 return true
2352 end
2353 else if cname == "Bool" then
2354 if pname == "output" then
2355 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2356 return true
2357 else if pname == "object_id" then
2358 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2359 return true
2360 else if pname == "==" then
2361 v.ret(v.equal_test(arguments[0], arguments[1]))
2362 return true
2363 else if pname == "!=" then
2364 var res = v.equal_test(arguments[0], arguments[1])
2365 v.ret(v.new_expr("!{res}", ret.as(not null)))
2366 return true
2367 end
2368 else if cname == "Float" then
2369 if pname == "output" then
2370 v.add("printf(\"%f\\n\", {arguments.first});")
2371 return true
2372 else if pname == "object_id" then
2373 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2374 return true
2375 else if pname == "+" then
2376 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2377 return true
2378 else if pname == "-" then
2379 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2380 return true
2381 else if pname == "unary -" then
2382 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2383 return true
2384 else if pname == "unary +" then
2385 v.ret(arguments[0])
2386 return true
2387 else if pname == "succ" then
2388 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2389 return true
2390 else if pname == "prec" then
2391 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2392 return true
2393 else if pname == "*" then
2394 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2395 return true
2396 else if pname == "/" then
2397 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2398 return true
2399 else if pname == "==" then
2400 v.ret(v.equal_test(arguments[0], arguments[1]))
2401 return true
2402 else if pname == "!=" then
2403 var res = v.equal_test(arguments[0], arguments[1])
2404 v.ret(v.new_expr("!{res}", ret.as(not null)))
2405 return true
2406 else if pname == "<" then
2407 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2408 return true
2409 else if pname == ">" then
2410 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2411 return true
2412 else if pname == "<=" then
2413 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2414 return true
2415 else if pname == ">=" then
2416 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2417 return true
2418 else if pname == "to_i" then
2419 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2420 return true
2421 else if pname == "to_b" then
2422 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2423 return true
2424 else if pname == "to_i8" then
2425 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2426 return true
2427 else if pname == "to_i16" then
2428 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2429 return true
2430 else if pname == "to_u16" then
2431 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2432 return true
2433 else if pname == "to_i32" then
2434 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2435 return true
2436 else if pname == "to_u32" then
2437 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2438 return true
2439 end
2440 else if cname == "NativeString" then
2441 if pname == "[]" then
2442 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2443 return true
2444 else if pname == "[]=" then
2445 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2446 return true
2447 else if pname == "copy_to" then
2448 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2449 return true
2450 else if pname == "atoi" then
2451 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2452 return true
2453 else if pname == "fast_cstring" then
2454 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2455 return true
2456 else if pname == "new" then
2457 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2458 return true
2459 end
2460 else if cname == "NativeArray" then
2461 v.native_array_def(pname, ret, arguments)
2462 return true
2463 else if cname == "Int8" then
2464 if pname == "output" then
2465 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2466 return true
2467 else if pname == "object_id" then
2468 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2469 return true
2470 else if pname == "+" then
2471 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2472 return true
2473 else if pname == "-" then
2474 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2475 return true
2476 else if pname == "unary -" then
2477 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2478 return true
2479 else if pname == "unary +" then
2480 v.ret(arguments[0])
2481 return true
2482 else if pname == "*" then
2483 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2484 return true
2485 else if pname == "/" then
2486 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2487 return true
2488 else if pname == "%" then
2489 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2490 return true
2491 else if pname == "<<" then
2492 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2493 return true
2494 else if pname == ">>" then
2495 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2496 return true
2497 else if pname == "==" then
2498 v.ret(v.equal_test(arguments[0], arguments[1]))
2499 return true
2500 else if pname == "!=" then
2501 var res = v.equal_test(arguments[0], arguments[1])
2502 v.ret(v.new_expr("!{res}", ret.as(not null)))
2503 return true
2504 else if pname == "<" then
2505 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2506 return true
2507 else if pname == ">" then
2508 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2509 return true
2510 else if pname == "<=" then
2511 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2512 return true
2513 else if pname == ">=" then
2514 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2515 return true
2516 else if pname == "to_i" then
2517 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2518 return true
2519 else if pname == "to_b" then
2520 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2521 return true
2522 else if pname == "to_i16" then
2523 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2524 return true
2525 else if pname == "to_u16" then
2526 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2527 return true
2528 else if pname == "to_i32" then
2529 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2530 return true
2531 else if pname == "to_u32" then
2532 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2533 return true
2534 else if pname == "to_f" then
2535 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2536 return true
2537 else if pname == "&" then
2538 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2539 return true
2540 else if pname == "|" then
2541 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2542 return true
2543 else if pname == "^" then
2544 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2545 return true
2546 else if pname == "unary ~" then
2547 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2548 return true
2549 end
2550 else if cname == "Int16" then
2551 if pname == "output" then
2552 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2553 return true
2554 else if pname == "object_id" then
2555 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2556 return true
2557 else if pname == "+" then
2558 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2559 return true
2560 else if pname == "-" then
2561 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2562 return true
2563 else if pname == "unary -" then
2564 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2565 return true
2566 else if pname == "unary +" then
2567 v.ret(arguments[0])
2568 return true
2569 else if pname == "*" then
2570 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2571 return true
2572 else if pname == "/" then
2573 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2574 return true
2575 else if pname == "%" then
2576 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2577 return true
2578 else if pname == "<<" then
2579 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2580 return true
2581 else if pname == ">>" then
2582 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2583 return true
2584 else if pname == "==" then
2585 v.ret(v.equal_test(arguments[0], arguments[1]))
2586 return true
2587 else if pname == "!=" then
2588 var res = v.equal_test(arguments[0], arguments[1])
2589 v.ret(v.new_expr("!{res}", ret.as(not null)))
2590 return true
2591 else if pname == "<" then
2592 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2593 return true
2594 else if pname == ">" then
2595 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2596 return true
2597 else if pname == "<=" then
2598 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2599 return true
2600 else if pname == ">=" then
2601 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2602 return true
2603 else if pname == "to_i" then
2604 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2605 return true
2606 else if pname == "to_b" then
2607 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2608 return true
2609 else if pname == "to_i8" then
2610 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2611 return true
2612 else if pname == "to_u16" then
2613 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2614 return true
2615 else if pname == "to_i32" then
2616 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2617 return true
2618 else if pname == "to_u32" then
2619 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2620 return true
2621 else if pname == "to_f" then
2622 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2623 return true
2624 else if pname == "&" then
2625 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2626 return true
2627 else if pname == "|" then
2628 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2629 return true
2630 else if pname == "^" then
2631 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2632 return true
2633 else if pname == "unary ~" then
2634 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2635 return true
2636 end
2637 else if cname == "UInt16" then
2638 if pname == "output" then
2639 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2640 return true
2641 else if pname == "object_id" then
2642 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2643 return true
2644 else if pname == "+" then
2645 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2646 return true
2647 else if pname == "-" then
2648 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2649 return true
2650 else if pname == "unary -" then
2651 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2652 return true
2653 else if pname == "unary +" then
2654 v.ret(arguments[0])
2655 return true
2656 else if pname == "*" then
2657 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2658 return true
2659 else if pname == "/" then
2660 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2661 return true
2662 else if pname == "%" then
2663 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2664 return true
2665 else if pname == "<<" then
2666 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2667 return true
2668 else if pname == ">>" then
2669 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2670 return true
2671 else if pname == "==" then
2672 v.ret(v.equal_test(arguments[0], arguments[1]))
2673 return true
2674 else if pname == "!=" then
2675 var res = v.equal_test(arguments[0], arguments[1])
2676 v.ret(v.new_expr("!{res}", ret.as(not null)))
2677 return true
2678 else if pname == "<" then
2679 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2680 return true
2681 else if pname == ">" then
2682 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2683 return true
2684 else if pname == "<=" then
2685 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2686 return true
2687 else if pname == ">=" then
2688 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2689 return true
2690 else if pname == "to_i" then
2691 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2692 return true
2693 else if pname == "to_b" then
2694 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2695 return true
2696 else if pname == "to_i8" then
2697 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2698 return true
2699 else if pname == "to_i16" then
2700 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2701 return true
2702 else if pname == "to_i32" then
2703 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2704 return true
2705 else if pname == "to_u32" then
2706 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2707 return true
2708 else if pname == "to_f" then
2709 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2710 return true
2711 else if pname == "&" then
2712 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2713 return true
2714 else if pname == "|" then
2715 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2716 return true
2717 else if pname == "^" then
2718 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2719 return true
2720 else if pname == "unary ~" then
2721 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2722 return true
2723 end
2724 else if cname == "Int32" then
2725 if pname == "output" then
2726 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2727 return true
2728 else if pname == "object_id" then
2729 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2730 return true
2731 else if pname == "+" then
2732 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2733 return true
2734 else if pname == "-" then
2735 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2736 return true
2737 else if pname == "unary -" then
2738 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2739 return true
2740 else if pname == "unary +" then
2741 v.ret(arguments[0])
2742 return true
2743 else if pname == "*" then
2744 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2745 return true
2746 else if pname == "/" then
2747 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2748 return true
2749 else if pname == "%" then
2750 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2751 return true
2752 else if pname == "<<" then
2753 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2754 return true
2755 else if pname == ">>" then
2756 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2757 return true
2758 else if pname == "==" then
2759 v.ret(v.equal_test(arguments[0], arguments[1]))
2760 return true
2761 else if pname == "!=" then
2762 var res = v.equal_test(arguments[0], arguments[1])
2763 v.ret(v.new_expr("!{res}", ret.as(not null)))
2764 return true
2765 else if pname == "<" then
2766 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2767 return true
2768 else if pname == ">" then
2769 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2770 return true
2771 else if pname == "<=" then
2772 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2773 return true
2774 else if pname == ">=" then
2775 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2776 return true
2777 else if pname == "to_i" then
2778 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2779 return true
2780 else if pname == "to_b" then
2781 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2782 return true
2783 else if pname == "to_i8" then
2784 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2785 return true
2786 else if pname == "to_i16" then
2787 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2788 return true
2789 else if pname == "to_u16" then
2790 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2791 return true
2792 else if pname == "to_u32" then
2793 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2794 return true
2795 else if pname == "to_f" then
2796 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2797 return true
2798 else if pname == "&" then
2799 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2800 return true
2801 else if pname == "|" then
2802 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2803 return true
2804 else if pname == "^" then
2805 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2806 return true
2807 else if pname == "unary ~" then
2808 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2809 return true
2810 end
2811 else if cname == "UInt32" then
2812 if pname == "output" then
2813 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2814 return true
2815 else if pname == "object_id" then
2816 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2817 return true
2818 else if pname == "+" then
2819 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2820 return true
2821 else if pname == "-" then
2822 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2823 return true
2824 else if pname == "unary -" then
2825 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2826 return true
2827 else if pname == "unary +" then
2828 v.ret(arguments[0])
2829 return true
2830 else if pname == "*" then
2831 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2832 return true
2833 else if pname == "/" then
2834 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2835 return true
2836 else if pname == "%" then
2837 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2838 return true
2839 else if pname == "<<" then
2840 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2841 return true
2842 else if pname == ">>" then
2843 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2844 return true
2845 else if pname == "==" then
2846 v.ret(v.equal_test(arguments[0], arguments[1]))
2847 return true
2848 else if pname == "!=" then
2849 var res = v.equal_test(arguments[0], arguments[1])
2850 v.ret(v.new_expr("!{res}", ret.as(not null)))
2851 return true
2852 else if pname == "<" then
2853 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2854 return true
2855 else if pname == ">" then
2856 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2857 return true
2858 else if pname == "<=" then
2859 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2860 return true
2861 else if pname == ">=" then
2862 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2863 return true
2864 else if pname == "to_i" then
2865 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2866 return true
2867 else if pname == "to_b" then
2868 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2869 return true
2870 else if pname == "to_i8" then
2871 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2872 return true
2873 else if pname == "to_i16" then
2874 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2875 return true
2876 else if pname == "to_u16" then
2877 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2878 return true
2879 else if pname == "to_i32" then
2880 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2881 return true
2882 else if pname == "to_f" then
2883 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2884 return true
2885 else if pname == "&" then
2886 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2887 return true
2888 else if pname == "|" then
2889 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2890 return true
2891 else if pname == "^" then
2892 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2893 return true
2894 else if pname == "unary ~" then
2895 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2896 return true
2897 end
2898 end
2899 if pname == "exit" then
2900 v.add("exit({arguments[1]});")
2901 return true
2902 else if pname == "sys" then
2903 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2904 return true
2905 else if pname == "calloc_string" then
2906 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2907 return true
2908 else if pname == "calloc_array" then
2909 v.calloc_array(ret.as(not null), arguments)
2910 return true
2911 else if pname == "object_id" then
2912 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2913 return true
2914 else if pname == "is_same_type" then
2915 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2916 return true
2917 else if pname == "is_same_instance" then
2918 v.ret(v.equal_test(arguments[0], arguments[1]))
2919 return true
2920 else if pname == "output_class_name" then
2921 var nat = v.class_name_string(arguments.first)
2922 v.add("printf(\"%s\\n\", {nat});")
2923 return true
2924 else if pname == "native_class_name" then
2925 var nat = v.class_name_string(arguments.first)
2926 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2927 return true
2928 else if pname == "force_garbage_collection" then
2929 v.add("nit_gcollect();")
2930 return true
2931 else if pname == "native_argc" then
2932 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2933 return true
2934 else if pname == "native_argv" then
2935 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2936 return true
2937 end
2938 return false
2939 end
2940
2941 # Compile an extern method
2942 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2943 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2944 do
2945 var externname
2946 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2947 if at != null and at.n_args.length == 1 then
2948 externname = at.arg_as_string(v.compiler.modelbuilder)
2949 if externname == null then return false
2950 else
2951 return false
2952 end
2953 v.add_extern(mpropdef.mclassdef.mmodule)
2954 var res: nullable RuntimeVariable = null
2955 var ret = mpropdef.msignature.return_mtype
2956 if ret != null then
2957 ret = v.resolve_for(ret, arguments.first)
2958 res = v.new_var_extern(ret)
2959 end
2960 v.adapt_signature(mpropdef, arguments)
2961 v.unbox_signature_extern(mpropdef, arguments)
2962
2963 if res == null then
2964 v.add("{externname}({arguments.join(", ")});")
2965 else
2966 v.add("{res} = {externname}({arguments.join(", ")});")
2967 res = v.box_extern(res, ret.as(not null))
2968 v.ret(res)
2969 end
2970 return true
2971 end
2972
2973 # Compile an extern factory
2974 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2975 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2976 do
2977 var externname
2978 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2979 if at != null then
2980 externname = at.arg_as_string(v.compiler.modelbuilder)
2981 if externname == null then return false
2982 else
2983 return false
2984 end
2985 v.add_extern(mpropdef.mclassdef.mmodule)
2986 v.adapt_signature(mpropdef, arguments)
2987 v.unbox_signature_extern(mpropdef, arguments)
2988 var ret = arguments.first.mtype
2989 var res = v.new_var_extern(ret)
2990
2991 arguments.shift
2992
2993 v.add("{res} = {externname}({arguments.join(", ")});")
2994 res = v.box_extern(res, ret)
2995 v.ret(res)
2996 return true
2997 end
2998 end
2999
3000 redef class AAttrPropdef
3001 redef fun can_inline: Bool do return not is_lazy
3002
3003 redef fun compile_to_c(v, mpropdef, arguments)
3004 do
3005 if mpropdef == mreadpropdef then
3006 assert arguments.length == 1
3007 var recv = arguments.first
3008 var res
3009 if is_lazy then
3010 var set
3011 var ret = self.mtype
3012 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3013 var guard = self.mlazypropdef.mproperty
3014 if useiset then
3015 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3016 else
3017 set = v.read_attribute(guard, recv)
3018 end
3019 v.add("if(likely({set})) \{")
3020 res = v.read_attribute(self.mpropdef.mproperty, recv)
3021 v.add("\} else \{")
3022
3023 var value = evaluate_expr(v, recv)
3024
3025 v.assign(res, value)
3026 if not useiset then
3027 var true_v = v.bool_instance(true)
3028 v.write_attribute(guard, arguments.first, true_v)
3029 end
3030 v.add("\}")
3031 else
3032 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3033 end
3034 v.assign(v.frame.returnvar.as(not null), res)
3035 else if mpropdef == mwritepropdef then
3036 assert arguments.length == 2
3037 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
3038 if is_lazy then
3039 var ret = self.mtype
3040 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3041 if not useiset then
3042 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3043 end
3044 end
3045 else
3046 abort
3047 end
3048 end
3049
3050 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3051 do
3052 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3053 end
3054
3055 # Evaluate, store and return the default value of the attribute
3056 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3057 do
3058 var oldnode = v.current_node
3059 v.current_node = self
3060 var old_frame = v.frame
3061 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3062 v.frame = frame
3063
3064 var value
3065 var mtype = self.mtype
3066 assert mtype != null
3067
3068 var nexpr = self.n_expr
3069 var nblock = self.n_block
3070 if nexpr != null then
3071 value = v.expr(nexpr, mtype)
3072 else if nblock != null then
3073 value = v.new_var(mtype)
3074 frame.returnvar = value
3075 frame.returnlabel = v.get_name("RET_LABEL")
3076 v.add("\{")
3077 v.stmt(nblock)
3078 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3079 v.add("\}")
3080 else
3081 abort
3082 end
3083
3084 v.write_attribute(self.mpropdef.mproperty, recv, value)
3085
3086 v.frame = old_frame
3087 v.current_node = oldnode
3088
3089 return value
3090 end
3091
3092 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3093 do
3094 var nexpr = self.n_expr
3095 if nexpr != null then return
3096
3097 var oldnode = v.current_node
3098 v.current_node = self
3099 var old_frame = v.frame
3100 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3101 v.frame = frame
3102 # Force read to check the initialization
3103 v.read_attribute(self.mpropdef.mproperty, recv)
3104 v.frame = old_frame
3105 v.current_node = oldnode
3106 end
3107 end
3108
3109 redef class AClassdef
3110 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3111 do
3112 if mpropdef == self.mfree_init then
3113 assert mpropdef.mproperty.is_root_init
3114 assert arguments.length == 1
3115 if not mpropdef.is_intro then
3116 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3117 end
3118 return
3119 else
3120 abort
3121 end
3122 end
3123 end
3124
3125 redef class AExpr
3126 # Try to compile self as an expression
3127 # Do not call this method directly, use `v.expr` instead
3128 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3129 do
3130 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3131 var mtype = self.mtype
3132 if mtype == null then
3133 return null
3134 else
3135 var res = v.new_var(mtype)
3136 v.add("/* {res} = NOT YET {class_name} */")
3137 return res
3138 end
3139 end
3140
3141 # Try to compile self as a statement
3142 # Do not call this method directly, use `v.stmt` instead
3143 private fun stmt(v: AbstractCompilerVisitor)
3144 do
3145 expr(v)
3146 end
3147 end
3148
3149 redef class ABlockExpr
3150 redef fun stmt(v)
3151 do
3152 for e in self.n_expr do v.stmt(e)
3153 end
3154 redef fun expr(v)
3155 do
3156 var last = self.n_expr.last
3157 for e in self.n_expr do
3158 if e == last then break
3159 v.stmt(e)
3160 end
3161 return v.expr(last, null)
3162 end
3163 end
3164
3165 redef class AVardeclExpr
3166 redef fun stmt(v)
3167 do
3168 var variable = self.variable.as(not null)
3169 var ne = self.n_expr
3170 if ne != null then
3171 var i = v.expr(ne, variable.declared_type)
3172 v.assign(v.variable(variable), i)
3173 end
3174 end
3175 end
3176
3177 redef class AVarExpr
3178 redef fun expr(v)
3179 do
3180 var res = v.variable(self.variable.as(not null))
3181 var mtype = self.mtype.as(not null)
3182 return v.autoadapt(res, mtype)
3183 end
3184 end
3185
3186 redef class AVarAssignExpr
3187 redef fun expr(v)
3188 do
3189 var variable = self.variable.as(not null)
3190 var i = v.expr(self.n_value, variable.declared_type)
3191 v.assign(v.variable(variable), i)
3192 return i
3193 end
3194 end
3195
3196 redef class AVarReassignExpr
3197 redef fun stmt(v)
3198 do
3199 var variable = self.variable.as(not null)
3200 var vari = v.variable(variable)
3201 var value = v.expr(self.n_value, variable.declared_type)
3202 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3203 assert res != null
3204 v.assign(v.variable(variable), res)
3205 end
3206 end
3207
3208 redef class ASelfExpr
3209 redef fun expr(v) do return v.frame.arguments.first
3210 end
3211
3212 redef class AImplicitSelfExpr
3213 redef fun expr(v) do
3214 if not is_sys then return super
3215 return v.new_expr("glob_sys", mtype.as(not null))
3216 end
3217 end
3218
3219 redef class AEscapeExpr
3220 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3221 end
3222
3223 redef class AReturnExpr
3224 redef fun stmt(v)
3225 do
3226 var nexpr = self.n_expr
3227 if nexpr != null then
3228 var returnvar = v.frame.returnvar.as(not null)
3229 var i = v.expr(nexpr, returnvar.mtype)
3230 v.assign(returnvar, i)
3231 end
3232 v.add("goto {v.frame.returnlabel.as(not null)};")
3233 end
3234 end
3235
3236 redef class AAbortExpr
3237 redef fun stmt(v) do v.add_abort("Aborted")
3238 end
3239
3240 redef class AIfExpr
3241 redef fun stmt(v)
3242 do
3243 var cond = v.expr_bool(self.n_expr)
3244 v.add("if ({cond})\{")
3245 v.stmt(self.n_then)
3246 v.add("\} else \{")
3247 v.stmt(self.n_else)
3248 v.add("\}")
3249 end
3250
3251 redef fun expr(v)
3252 do
3253 var res = v.new_var(self.mtype.as(not null))
3254 var cond = v.expr_bool(self.n_expr)
3255 v.add("if ({cond})\{")
3256 v.assign(res, v.expr(self.n_then.as(not null), null))
3257 v.add("\} else \{")
3258 v.assign(res, v.expr(self.n_else.as(not null), null))
3259 v.add("\}")
3260 return res
3261 end
3262 end
3263
3264 redef class AIfexprExpr
3265 redef fun expr(v)
3266 do
3267 var res = v.new_var(self.mtype.as(not null))
3268 var cond = v.expr_bool(self.n_expr)
3269 v.add("if ({cond})\{")
3270 v.assign(res, v.expr(self.n_then, null))
3271 v.add("\} else \{")
3272 v.assign(res, v.expr(self.n_else, null))
3273 v.add("\}")
3274 return res
3275 end
3276 end
3277
3278 redef class ADoExpr
3279 redef fun stmt(v)
3280 do
3281 v.stmt(self.n_block)
3282 v.add_escape_label(break_mark)
3283 end
3284 end
3285
3286 redef class AWhileExpr
3287 redef fun stmt(v)
3288 do
3289 v.add("for(;;) \{")
3290 var cond = v.expr_bool(self.n_expr)
3291 v.add("if (!{cond}) break;")
3292 v.stmt(self.n_block)
3293 v.add_escape_label(continue_mark)
3294 v.add("\}")
3295 v.add_escape_label(break_mark)
3296 end
3297 end
3298
3299 redef class ALoopExpr
3300 redef fun stmt(v)
3301 do
3302 v.add("for(;;) \{")
3303 v.stmt(self.n_block)
3304 v.add_escape_label(continue_mark)
3305 v.add("\}")
3306 v.add_escape_label(break_mark)
3307 end
3308 end
3309
3310 redef class AForExpr
3311 redef fun stmt(v)
3312 do
3313 for g in n_groups do
3314 var cl = v.expr(g.n_expr, null)
3315 var it_meth = g.method_iterator
3316 assert it_meth != null
3317 var it = v.compile_callsite(it_meth, [cl])
3318 assert it != null
3319 g.it = it
3320 end
3321 v.add("for(;;) \{")
3322 for g in n_groups do
3323 var it = g.it
3324 var isok_meth = g.method_is_ok
3325 assert isok_meth != null
3326 var ok = v.compile_callsite(isok_meth, [it])
3327 assert ok != null
3328 v.add("if(!{ok}) break;")
3329 if g.variables.length == 1 then
3330 var item_meth = g.method_item
3331 assert item_meth != null
3332 var i = v.compile_callsite(item_meth, [it])
3333 assert i != null
3334 v.assign(v.variable(g.variables.first), i)
3335 else if g.variables.length == 2 then
3336 var key_meth = g.method_key
3337 assert key_meth != null
3338 var i = v.compile_callsite(key_meth, [it])
3339 assert i != null
3340 v.assign(v.variable(g.variables[0]), i)
3341 var item_meth = g.method_item
3342 assert item_meth != null
3343 i = v.compile_callsite(item_meth, [it])
3344 assert i != null
3345 v.assign(v.variable(g.variables[1]), i)
3346 else
3347 abort
3348 end
3349 end
3350 v.stmt(self.n_block)
3351 v.add_escape_label(continue_mark)
3352 for g in n_groups do
3353 var next_meth = g.method_next
3354 assert next_meth != null
3355 v.compile_callsite(next_meth, [g.it])
3356 end
3357 v.add("\}")
3358 v.add_escape_label(break_mark)
3359
3360 for g in n_groups do
3361 var method_finish = g.method_finish
3362 if method_finish != null then
3363 # TODO: Find a way to call this also in long escape (e.g. return)
3364 v.compile_callsite(method_finish, [g.it])
3365 end
3366 end
3367 end
3368 end
3369
3370 redef class AForGroup
3371 # C variable representing the iterator
3372 private var it: RuntimeVariable is noinit
3373 end
3374
3375 redef class AAssertExpr
3376 redef fun stmt(v)
3377 do
3378 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3379
3380 var cond = v.expr_bool(self.n_expr)
3381 v.add("if (unlikely(!{cond})) \{")
3382 v.stmt(self.n_else)
3383 var nid = self.n_id
3384 if nid != null then
3385 v.add_abort("Assert '{nid.text}' failed")
3386 else
3387 v.add_abort("Assert failed")
3388 end
3389 v.add("\}")
3390 end
3391 end
3392
3393 redef class AOrExpr
3394 redef fun expr(v)
3395 do
3396 var res = v.new_var(self.mtype.as(not null))
3397 var i1 = v.expr_bool(self.n_expr)
3398 v.add("if ({i1}) \{")
3399 v.add("{res} = 1;")
3400 v.add("\} else \{")
3401 var i2 = v.expr_bool(self.n_expr2)
3402 v.add("{res} = {i2};")
3403 v.add("\}")
3404 return res
3405 end
3406 end
3407
3408 redef class AImpliesExpr
3409 redef fun expr(v)
3410 do
3411 var res = v.new_var(self.mtype.as(not null))
3412 var i1 = v.expr_bool(self.n_expr)
3413 v.add("if (!{i1}) \{")
3414 v.add("{res} = 1;")
3415 v.add("\} else \{")
3416 var i2 = v.expr_bool(self.n_expr2)
3417 v.add("{res} = {i2};")
3418 v.add("\}")
3419 return res
3420 end
3421 end
3422
3423 redef class AAndExpr
3424 redef fun expr(v)
3425 do
3426 var res = v.new_var(self.mtype.as(not null))
3427 var i1 = v.expr_bool(self.n_expr)
3428 v.add("if (!{i1}) \{")
3429 v.add("{res} = 0;")
3430 v.add("\} else \{")
3431 var i2 = v.expr_bool(self.n_expr2)
3432 v.add("{res} = {i2};")
3433 v.add("\}")
3434 return res
3435 end
3436 end
3437
3438 redef class ANotExpr
3439 redef fun expr(v)
3440 do
3441 var cond = v.expr_bool(self.n_expr)
3442 return v.new_expr("!{cond}", self.mtype.as(not null))
3443 end
3444 end
3445
3446 redef class AOrElseExpr
3447 redef fun expr(v)
3448 do
3449 var res = v.new_var(self.mtype.as(not null))
3450 var i1 = v.expr(self.n_expr, null)
3451 v.add("if ({i1}!=NULL) \{")
3452 v.assign(res, i1)
3453 v.add("\} else \{")
3454 var i2 = v.expr(self.n_expr2, null)
3455 v.assign(res, i2)
3456 v.add("\}")
3457 return res
3458 end
3459 end
3460
3461 redef class AIntegerExpr
3462 redef fun expr(v) do
3463 if value isa Int then return v.int_instance(value.as(Int))
3464 if value isa Byte then return v.byte_instance(value.as(Byte))
3465 if value isa Int8 then return v.int8_instance(value.as(Int8))
3466 if value isa Int16 then return v.int16_instance(value.as(Int16))
3467 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3468 if value isa Int32 then return v.int32_instance(value.as(Int32))
3469 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3470 # Should never happen
3471 abort
3472 end
3473 end
3474
3475 redef class AFloatExpr
3476 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3477 end
3478
3479 redef class ACharExpr
3480 redef fun expr(v) do return v.char_instance(self.value.as(not null))
3481 end
3482
3483 redef class AArrayExpr
3484 redef fun expr(v)
3485 do
3486 var mtype = self.element_mtype.as(not null)
3487 var array = new Array[RuntimeVariable]
3488 var res = v.array_instance(array, mtype)
3489
3490 var old_comprehension = v.frame.comprehension
3491 v.frame.comprehension = res
3492 for nexpr in self.n_exprs do
3493 v.stmt(nexpr)
3494 end
3495 v.frame.comprehension = old_comprehension
3496
3497 return res
3498 end
3499 end
3500
3501 redef class AStringFormExpr
3502 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3503 end
3504
3505 redef class ASuperstringExpr
3506 redef fun expr(v)
3507 do
3508 var type_string = mtype.as(not null)
3509
3510 # Collect elements of the superstring
3511 var array = new Array[AExpr]
3512 for ne in self.n_exprs do
3513 # Drop literal empty string.
3514 # They appears in things like "{a}" that is ["", a, ""]
3515 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3516 array.add(ne)
3517 end
3518
3519 # Store the allocated native array in a static variable
3520 # For reusing later
3521 var varonce = v.get_name("varonce")
3522 v.add("if (unlikely({varonce}==NULL)) \{")
3523
3524 # The native array that will contains the elements to_s-ized.
3525 # For fast concatenation.
3526 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3527
3528 v.add_decl("static {a.mtype.ctype} {varonce};")
3529
3530 # Pre-fill the array with the literal string parts.
3531 # So they do not need to be filled again when reused
3532 for i in [0..array.length[ do
3533 var ne = array[i]
3534 if not ne isa AStringFormExpr then continue
3535 var e = v.expr(ne, null)
3536 v.native_array_set(a, i, e)
3537 end
3538
3539 v.add("\} else \{")
3540 # Take the native-array from the store.
3541 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3542 # WARNING: not thread safe! (FIXME?)
3543 v.add("{a} = {varonce};")
3544 v.add("{varonce} = NULL;")
3545 v.add("\}")
3546
3547 # Stringify the elements and put them in the native array
3548 var to_s_method = v.get_property("to_s", v.object_type)
3549 for i in [0..array.length[ do
3550 var ne = array[i]
3551 if ne isa AStringFormExpr then continue
3552 var e = v.expr(ne, null)
3553 # Skip the `to_s` if the element is already a String
3554 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3555 e = v.send(to_s_method, [e]).as(not null)
3556 end
3557 v.native_array_set(a, i, e)
3558 end
3559
3560 # Fast join the native string to get the result
3561 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3562
3563 # We finish to work with the native array,
3564 # so store it so that it can be reused
3565 v.add("{varonce} = {a};")
3566 return res
3567 end
3568 end
3569
3570 redef class ACrangeExpr
3571 redef fun expr(v)
3572 do
3573 var i1 = v.expr(self.n_expr, null)
3574 var i2 = v.expr(self.n_expr2, null)
3575 var mtype = self.mtype.as(MClassType)
3576 var res = v.init_instance(mtype)
3577 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3578 return res
3579 end
3580 end
3581
3582 redef class AOrangeExpr
3583 redef fun expr(v)
3584 do
3585 var i1 = v.expr(self.n_expr, null)
3586 var i2 = v.expr(self.n_expr2, null)
3587 var mtype = self.mtype.as(MClassType)
3588 var res = v.init_instance(mtype)
3589 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3590 return res
3591 end
3592 end
3593
3594 redef class ATrueExpr
3595 redef fun expr(v) do return v.bool_instance(true)
3596 end
3597
3598 redef class AFalseExpr
3599 redef fun expr(v) do return v.bool_instance(false)
3600 end
3601
3602 redef class ANullExpr
3603 redef fun expr(v) do return v.null_instance
3604 end
3605
3606 redef class AIsaExpr
3607 redef fun expr(v)
3608 do
3609 var i = v.expr(self.n_expr, null)
3610 var cast_type = self.cast_type
3611 if cast_type == null then return null # no-no on broken node
3612 return v.type_test(i, cast_type, "isa")
3613 end
3614 end
3615
3616 redef class AAsCastExpr
3617 redef fun expr(v)
3618 do
3619 var i = v.expr(self.n_expr, null)
3620 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3621
3622 v.add_cast(i, self.mtype.as(not null), "as")
3623 return i
3624 end
3625 end
3626
3627 redef class AAsNotnullExpr
3628 redef fun expr(v)
3629 do
3630 var i = v.expr(self.n_expr, null)
3631 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3632
3633 if i.mtype.is_c_primitive then return i
3634
3635 v.add("if (unlikely({i} == NULL)) \{")
3636 v.add_abort("Cast failed")
3637 v.add("\}")
3638 return i
3639 end
3640 end
3641
3642 redef class AParExpr
3643 redef fun expr(v) do return v.expr(self.n_expr, null)
3644 end
3645
3646 redef class AOnceExpr
3647 redef fun expr(v)
3648 do
3649 var mtype = self.mtype.as(not null)
3650 var name = v.get_name("varonce")
3651 var guard = v.get_name(name + "_guard")
3652 v.add_decl("static {mtype.ctype} {name};")
3653 v.add_decl("static int {guard};")
3654 var res = v.new_var(mtype)
3655 v.add("if (likely({guard})) \{")
3656 v.add("{res} = {name};")
3657 v.add("\} else \{")
3658 var i = v.expr(self.n_expr, mtype)
3659 v.add("{res} = {i};")
3660 v.add("{name} = {res};")
3661 v.add("{guard} = 1;")
3662 v.add("\}")
3663 return res
3664 end
3665 end
3666
3667 redef class ASendExpr
3668 redef fun expr(v)
3669 do
3670 var recv = v.expr(self.n_expr, null)
3671 var callsite = self.callsite.as(not null)
3672 if callsite.is_broken then return null
3673 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3674 return v.compile_callsite(callsite, args)
3675 end
3676 end
3677
3678 redef class ASendReassignFormExpr
3679 redef fun stmt(v)
3680 do
3681 var recv = v.expr(self.n_expr, null)
3682 var callsite = self.callsite.as(not null)
3683 if callsite.is_broken then return
3684 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3685
3686 var value = v.expr(self.n_value, null)
3687
3688 var left = v.compile_callsite(callsite, args)
3689 assert left != null
3690
3691 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3692 assert res != null
3693
3694 args.add(res)
3695 v.compile_callsite(self.write_callsite.as(not null), args)
3696 end
3697 end
3698
3699 redef class ASuperExpr
3700 redef fun expr(v)
3701 do
3702 var frame = v.frame.as(not null)
3703 var recv = frame.arguments.first
3704
3705 var callsite = self.callsite
3706 if callsite != null then
3707 if callsite.is_broken then return null
3708 var args
3709
3710 if self.n_args.n_exprs.is_empty then
3711 # Add automatic arguments for the super init call
3712 args = [recv]
3713 for i in [0..callsite.msignature.arity[ do
3714 args.add(frame.arguments[i+1])
3715 end
3716 else
3717 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3718 end
3719
3720 # Super init call
3721 var res = v.compile_callsite(callsite, args)
3722 return res
3723 end
3724
3725 var mpropdef = self.mpropdef.as(not null)
3726
3727 var args
3728 if self.n_args.n_exprs.is_empty then
3729 args = frame.arguments
3730 else
3731 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3732 end
3733
3734 # Standard call-next-method
3735 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3736 end
3737 end
3738
3739 redef class ANewExpr
3740 redef fun expr(v)
3741 do
3742 var mtype = self.recvtype
3743 assert mtype != null
3744
3745 if mtype.mclass.name == "NativeArray" then
3746 assert self.n_args.n_exprs.length == 1
3747 var l = v.expr(self.n_args.n_exprs.first, null)
3748 assert mtype isa MGenericType
3749 var elttype = mtype.arguments.first
3750 return v.native_array_instance(elttype, l)
3751 end
3752
3753 var recv = v.init_instance_or_extern(mtype)
3754
3755 var callsite = self.callsite
3756 if callsite == null then return recv
3757 if callsite.is_broken then return null
3758
3759 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3760 var res2 = v.compile_callsite(callsite, args)
3761 if res2 != null then
3762 #self.debug("got {res2} from {mproperty}. drop {recv}")
3763 return res2
3764 end
3765 return recv
3766 end
3767 end
3768
3769 redef class AAttrExpr
3770 redef fun expr(v)
3771 do
3772 var recv = v.expr(self.n_expr, null)
3773 var mproperty = self.mproperty.as(not null)
3774 return v.read_attribute(mproperty, recv)
3775 end
3776 end
3777
3778 redef class AAttrAssignExpr
3779 redef fun expr(v)
3780 do
3781 var recv = v.expr(self.n_expr, null)
3782 var i = v.expr(self.n_value, null)
3783 var mproperty = self.mproperty.as(not null)
3784 v.write_attribute(mproperty, recv, i)
3785 return i
3786 end
3787 end
3788
3789 redef class AAttrReassignExpr
3790 redef fun stmt(v)
3791 do
3792 var recv = v.expr(self.n_expr, null)
3793 var value = v.expr(self.n_value, null)
3794 var mproperty = self.mproperty.as(not null)
3795 var attr = v.read_attribute(mproperty, recv)
3796 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3797 assert res != null
3798 v.write_attribute(mproperty, recv, res)
3799 end
3800 end
3801
3802 redef class AIssetAttrExpr
3803 redef fun expr(v)
3804 do
3805 var recv = v.expr(self.n_expr, null)
3806 var mproperty = self.mproperty.as(not null)
3807 return v.isset_attribute(mproperty, recv)
3808 end
3809 end
3810
3811 redef class AVarargExpr
3812 redef fun expr(v)
3813 do
3814 return v.expr(self.n_expr, null)
3815 end
3816 end
3817
3818 redef class ANamedargExpr
3819 redef fun expr(v)
3820 do
3821 return v.expr(self.n_expr, null)
3822 end
3823 end
3824
3825 redef class ADebugTypeExpr
3826 redef fun stmt(v)
3827 do
3828 # do nothing
3829 end
3830 end
3831
3832 # Utils
3833
3834 redef class Array[E]
3835 # Return a new `Array` with the elements only contened in self and not in `o`
3836 fun -(o: Array[E]): Array[E] do
3837 var res = new Array[E]
3838 for e in self do if not o.has(e) then res.add(e)
3839 return res
3840 end
3841 end
3842
3843 redef class MModule
3844 # All `MProperty` associated to all `MClassDef` of `mclass`
3845 fun properties(mclass: MClass): Set[MProperty] do
3846 if not self.properties_cache.has_key(mclass) then
3847 var properties = new HashSet[MProperty]
3848 var parents = new Array[MClass]
3849 if self.flatten_mclass_hierarchy.has(mclass) then
3850 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3851 end
3852 for parent in parents do
3853 properties.add_all(self.properties(parent))
3854 end
3855 for mclassdef in mclass.mclassdefs do
3856 if not self.in_importation <= mclassdef.mmodule then continue
3857 for mprop in mclassdef.intro_mproperties do
3858 properties.add(mprop)
3859 end
3860 end
3861 self.properties_cache[mclass] = properties
3862 end
3863 return properties_cache[mclass]
3864 end
3865 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3866
3867 # Write FFI and nitni results to file
3868 fun finalize_ffi(c: AbstractCompiler) do end
3869
3870 # Give requided addinional system libraries (as given to LD_LIBS)
3871 # Note: can return null instead of an empty set
3872 fun collect_linker_libs: nullable Array[String] do return null
3873 end
3874
3875 # Create a tool context to handle options and paths
3876 var toolcontext = new ToolContext
3877
3878 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3879
3880 # We do not add other options, so process them now!
3881 toolcontext.process_options(args)
3882
3883 # We need a model to collect stufs
3884 var model = new Model
3885 # An a model builder to parse files
3886 var modelbuilder = new ModelBuilder(model, toolcontext)
3887
3888 var arguments = toolcontext.option_context.rest
3889 if arguments.length > 1 and toolcontext.opt_output.value != null then
3890 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3891 exit 1
3892 end
3893
3894 # Here we load an process all modules passed on the command line
3895 var mmodules = modelbuilder.parse(arguments)
3896
3897 if mmodules.is_empty then toolcontext.quit
3898
3899 modelbuilder.run_phases
3900
3901 for mmodule in mmodules do
3902 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3903 var ms = [mmodule]
3904 toolcontext.run_global_phases(ms)
3905 end