src: remove some `call on nullable` warning with a non-null local variable
[nit.git] / src / compiler / abstract_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract compiler
18 module abstract_compiler
19
20 import literal
21 import semantize
22 import platform
23 import c_tools
24 private import annotation
25 import mixin
26 import counter
27
28 # Add compiling options
29 redef class ToolContext
30 # --output
31 var opt_output = new OptionString("Output file", "-o", "--output")
32 # --dir
33 var opt_dir = new OptionString("Output directory", "--dir")
34 # --no-cc
35 var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
36 # --no-main
37 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
38 # --make-flags
39 var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
40 # --max-c-lines
41 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
42 # --group-c-files
43 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
44 # --compile-dir
45 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
46 # --hardening
47 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
48 # --no-check-covariance
49 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
50 # --no-check-attr-isset
51 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
52 # --no-check-assert
53 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
54 # --no-check-autocast
55 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
56 # --no-check-null
57 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
58 # --no-check-all
59 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
60 # --typing-test-metrics
61 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
62 # --invocation-metrics
63 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
64 # --isset-checks-metrics
65 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
66 # --no-stacktrace
67 var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
68 # --no-gcc-directives
69 var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
70 # --release
71 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
72 # -g
73 var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "--debug", "-g")
74
75 redef init
76 do
77 super
78 self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
79 self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
80 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
81 self.option_context.add_option(self.opt_no_stacktrace)
82 self.option_context.add_option(self.opt_no_gcc_directive)
83 self.option_context.add_option(self.opt_release)
84 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
85 self.option_context.add_option(self.opt_debug)
86
87 opt_no_main.hidden = true
88 end
89
90 redef fun process_options(args)
91 do
92 super
93
94 if opt_output.value != null and opt_dir.value != null then
95 print "Option Error: cannot use both --dir and --output"
96 exit(1)
97 end
98
99 if opt_no_check_all.value then
100 opt_no_check_covariance.value = true
101 opt_no_check_attr_isset.value = true
102 opt_no_check_assert.value = true
103 opt_no_check_autocast.value = true
104 opt_no_check_null.value = true
105 end
106 end
107 end
108
109 redef class ModelBuilder
110 # Simple indirection to `Toolchain::write_and_make`
111 protected fun write_and_make(compiler: AbstractCompiler)
112 do
113 var platform = compiler.target_platform
114 var toolchain = platform.toolchain(toolcontext, compiler)
115 compiler.toolchain = toolchain
116 toolchain.write_and_make
117 end
118 end
119
120 redef class Platform
121 # The specific tool-chain associated to the platform
122 fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
123 do
124 return new MakefileToolchain(toolcontext, compiler)
125 end
126 end
127
128 # Build toolchain for a specific target program, varies per `Platform`
129 class Toolchain
130
131 # Toolcontext
132 var toolcontext: ToolContext
133
134 # Compiler of the target program
135 var compiler: AbstractCompiler
136
137 # Directory where to generate all files
138 #
139 # The option `--compile_dir` change this directory.
140 fun root_compile_dir: String
141 do
142 var compile_dir = toolcontext.opt_compile_dir.value
143 if compile_dir == null then compile_dir = "nit_compile"
144 return compile_dir
145 end
146
147 # Directory where to generate all C files
148 #
149 # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
150 fun compile_dir: String do return root_compile_dir
151
152 # Write all C files and compile them
153 fun write_and_make is abstract
154 end
155
156 # Default toolchain using a Makefile
157 class MakefileToolchain
158 super Toolchain
159
160 redef fun write_and_make
161 do
162 var debug = toolcontext.opt_debug.value
163 var compile_dir = compile_dir
164
165 # Remove the compilation directory unless explicitly set
166 var auto_remove = toolcontext.opt_compile_dir.value == null
167 # If debug flag is set, do not remove sources
168 if debug then auto_remove = false
169
170 # Generate the .h and .c files
171 # A single C file regroups many compiled rumtime functions
172 # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
173 var time0 = get_time
174 self.toolcontext.info("*** WRITING C ***", 1)
175
176 root_compile_dir.mkdir
177 compile_dir.mkdir
178
179 var cfiles = new Array[String]
180 write_files(compile_dir, cfiles)
181
182 # Generate the Makefile
183
184 write_makefile(compile_dir, cfiles)
185
186 var time1 = get_time
187 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
188
189 # Execute the Makefile
190
191 if self.toolcontext.opt_no_cc.value then return
192
193 time0 = time1
194 self.toolcontext.info("*** COMPILING C ***", 1)
195
196 compile_c_code(compile_dir)
197
198 if auto_remove then
199 sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
200 end
201
202 time1 = get_time
203 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
204 end
205
206 # Write all source files to the `compile_dir`
207 fun write_files(compile_dir: String, cfiles: Array[String])
208 do
209 var platform = compiler.target_platform
210 if platform.supports_libunwind then compiler.build_c_to_nit_bindings
211 var cc_opt_with_libgc = "-DWITH_LIBGC"
212 if not platform.supports_libgc then cc_opt_with_libgc = ""
213
214 # Add gc_choser.h to aditionnal bodies
215 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
216 if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
217 compiler.extern_bodies.add(gc_chooser)
218 var clib = toolcontext.nit_dir / "clib"
219 compiler.files_to_copy.add "{clib}/gc_chooser.c"
220 compiler.files_to_copy.add "{clib}/gc_chooser.h"
221
222 # FFI
223 for m in compiler.mainmodule.in_importation.greaters do
224 compiler.finalize_ffi_for_module(m)
225 end
226
227 # Copy original .[ch] files to compile_dir
228 for src in compiler.files_to_copy do
229 var basename = src.basename
230 var dst = "{compile_dir}/{basename}"
231 src.file_copy_to dst
232 end
233
234 var hfilename = compiler.header.file.name + ".h"
235 var hfilepath = "{compile_dir}/{hfilename}"
236 var h = new FileWriter.open(hfilepath)
237 for l in compiler.header.decl_lines do
238 h.write l
239 h.write "\n"
240 end
241 for l in compiler.header.lines do
242 h.write l
243 h.write "\n"
244 end
245 h.close
246
247 var max_c_lines = toolcontext.opt_max_c_lines.value
248 for f in compiler.files do
249 var i = 0
250 var count = 0
251 var file: nullable FileWriter = null
252 for vis in f.writers do
253 if vis == compiler.header then continue
254 var total_lines = vis.lines.length + vis.decl_lines.length
255 if total_lines == 0 then continue
256 count += total_lines
257 if file == null or (count > max_c_lines and max_c_lines > 0) then
258 i += 1
259 if file != null then file.close
260 var cfilename = "{f.name}.{i}.c"
261 var cfilepath = "{compile_dir}/{cfilename}"
262 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
263 cfiles.add(cfilename)
264 file = new FileWriter.open(cfilepath)
265 file.write "#include \"{f.name}.0.h\"\n"
266 count = total_lines
267 end
268 for l in vis.decl_lines do
269 file.write l
270 file.write "\n"
271 end
272 for l in vis.lines do
273 file.write l
274 file.write "\n"
275 end
276 end
277 if file == null then continue
278 file.close
279
280 var cfilename = "{f.name}.0.h"
281 var cfilepath = "{compile_dir}/{cfilename}"
282 var hfile: nullable FileWriter = null
283 hfile = new FileWriter.open(cfilepath)
284 hfile.write "#include \"{hfilename}\"\n"
285 for key in f.required_declarations do
286 if not compiler.provided_declarations.has_key(key) then
287 var node = compiler.requirers_of_declarations.get_or_null(key)
288 if node != null then
289 node.debug "No provided declaration for {key}"
290 else
291 print "No provided declaration for {key}"
292 end
293 abort
294 end
295 hfile.write compiler.provided_declarations[key]
296 hfile.write "\n"
297 end
298 hfile.close
299 end
300
301 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
302 end
303
304 # Get the name of the Makefile to use
305 fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
306
307 # Get the default name of the executable to produce
308 fun default_outname: String
309 do
310 var mainmodule = compiler.mainmodule.first_real_mmodule
311 return mainmodule.name
312 end
313
314 # Combine options and platform informations to get the final path of the outfile
315 fun outfile(mainmodule: MModule): String
316 do
317 var res = self.toolcontext.opt_output.value
318 if res != null then return res
319 res = default_outname
320 var dir = self.toolcontext.opt_dir.value
321 if dir != null then return dir.join_path(res)
322 return res
323 end
324
325 # Write the Makefile
326 fun write_makefile(compile_dir: String, cfiles: Array[String])
327 do
328 var mainmodule = compiler.mainmodule
329 var platform = compiler.target_platform
330
331 var outname = outfile(mainmodule)
332
333 var real_outpath = compile_dir.relpath(outname)
334 var outpath = real_outpath.escape_to_mk
335 if outpath != real_outpath then
336 # If the name is crazy and need escaping, we will do an indirection
337 # 1. generate the binary in the nit_compile dir under an escaped name
338 # 2. copy the binary at the right place in the `all` goal.
339 outpath = mainmodule.c_name
340 end
341 var makename = makefile_name
342 var makepath = "{compile_dir}/{makename}"
343 var makefile = new FileWriter.open(makepath)
344
345 var linker_options = new HashSet[String]
346 for m in mainmodule.in_importation.greaters do
347 var libs = m.collect_linker_libs
348 if libs != null then linker_options.add_all(libs)
349 end
350 var debug = toolcontext.opt_debug.value
351
352 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
353
354 makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
355 if platform.supports_libunwind then
356 if toolcontext.opt_no_stacktrace.value then
357 makefile.write "NO_STACKTRACE=True"
358 else
359 makefile.write "NO_STACKTRACE= # Set to `True` to enable"
360 end
361 end
362
363 # Dynamic adaptations
364 # While `platform` enable complex toolchains, they are statically applied
365 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
366 makefile.write "\n\n"
367
368 # Check and adapt the targeted system
369 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
370
371 # Check and adapt for the compiler used
372 # clang need an additionnal `-Qunused-arguments`
373 makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
374
375 if platform.supports_libunwind then
376 makefile.write """
377 ifneq ($(NO_STACKTRACE), True)
378 # Check and include lib-unwind in a portable way
379 ifneq ($(uname_S),Darwin)
380 # already included on macosx, but need to get the correct flags in other supported platforms.
381 ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
382 LDLIBS += `pkg-config --libs libunwind`
383 CFLAGS += `pkg-config --cflags libunwind`
384 else
385 $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
386 CFLAGS += -D NO_STACKTRACE
387 endif
388 endif
389 else
390 # Stacktraces disabled
391 CFLAGS += -D NO_STACKTRACE
392 endif
393
394 """
395 else
396 makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
397 end
398
399 makefile.write """
400 # Special configuration for Darwin
401 ifeq ($(uname_S),Darwin)
402 # Remove POSIX flag -lrt
403 LDLIBS := $(filter-out -lrt,$(LDLIBS))
404 endif
405
406 """
407
408 makefile.write("all: {outpath}\n")
409 if outpath != real_outpath then
410 makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
411 end
412 makefile.write("\n")
413
414 var ofiles = new Array[String]
415 var dep_rules = new Array[String]
416 # Compile each generated file
417 for f in cfiles do
418 var o = f.strip_extension(".c") + ".o"
419 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
420 ofiles.add(o)
421 dep_rules.add(o)
422 end
423
424 # Generate linker script, if any
425 if not compiler.linker_script.is_empty then
426 var linker_script_path = "{compile_dir}/linker_script"
427 ofiles.add "linker_script"
428 var f = new FileWriter.open(linker_script_path)
429 for l in compiler.linker_script do
430 f.write l
431 f.write "\n"
432 end
433 f.close
434 end
435
436 var java_files = new Array[ExternFile]
437
438 var pkgconfigs = new Array[String]
439 for f in compiler.extern_bodies do
440 pkgconfigs.add_all f.pkgconfigs
441 end
442 # Protect pkg-config
443 if not pkgconfigs.is_empty then
444 makefile.write """
445 # does pkg-config exists?
446 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
447 $(error "Command `pkg-config` not found. Please install it")
448 endif
449 """
450 for p in pkgconfigs do
451 makefile.write """
452 # Check for library {{{p}}}
453 ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
454 $(error "pkg-config: package {{{p}}} is not found.")
455 endif
456 """
457 end
458 end
459
460 # Compile each required extern body into a specific .o
461 for f in compiler.extern_bodies do
462 var o = f.makefile_rule_name
463 var ff = f.filename.basename
464 makefile.write("{o}: {ff}\n")
465 makefile.write("\t{f.makefile_rule_content}\n\n")
466 dep_rules.add(f.makefile_rule_name)
467
468 if f.compiles_to_o_file then ofiles.add(o)
469 if f.add_to_jar then java_files.add(f)
470 end
471
472 if not java_files.is_empty then
473 var jar_file = "{outpath}.jar"
474
475 var class_files_array = new Array[String]
476 for f in java_files do class_files_array.add(f.makefile_rule_name)
477 var class_files = class_files_array.join(" ")
478
479 makefile.write("{jar_file}: {class_files}\n")
480 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
481 dep_rules.add jar_file
482 end
483
484 # Link edition
485 var pkg = ""
486 if not pkgconfigs.is_empty then
487 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
488 end
489 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
490 # Clean
491 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
492 if outpath != real_outpath then
493 makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
494 end
495 makefile.close
496 self.toolcontext.info("Generated makefile: {makepath}", 2)
497
498 makepath.file_copy_to "{compile_dir}/Makefile"
499 end
500
501 # The C code is generated, compile it to an executable
502 fun compile_c_code(compile_dir: String)
503 do
504 var makename = makefile_name
505
506 var makeflags = self.toolcontext.opt_make_flags.value
507 if makeflags == null then makeflags = ""
508
509 var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
510 self.toolcontext.info(command, 2)
511
512 var res
513 if self.toolcontext.verbose_level >= 3 then
514 res = sys.system("{command} 2>&1")
515 else
516 res = sys.system("{command} 2>&1 >/dev/null")
517 end
518 if res != 0 then
519 toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
520 end
521 end
522 end
523
524 # Singleton that store the knowledge about the compilation process
525 abstract class AbstractCompiler
526 type VISITOR: AbstractCompilerVisitor
527
528 # Table corresponding c_names to nit names (methods)
529 var names = new HashMap[String, String]
530
531 # The main module of the program currently compiled
532 # Is assigned during the separate compilation
533 var mainmodule: MModule is writable
534
535 # The real main module of the program
536 var realmainmodule: MModule is noinit
537
538 # The modelbuilder used to know the model and the AST
539 var modelbuilder: ModelBuilder is protected writable
540
541 # The associated toolchain
542 #
543 # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
544 var toolchain: Toolchain is noinit
545
546 # Is hardening asked? (see --hardening)
547 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
548
549 # The targeted specific platform
550 var target_platform: Platform is noinit
551
552 init
553 do
554 self.realmainmodule = mainmodule
555 target_platform = mainmodule.target_platform or else new Platform
556 end
557
558 # Do the full code generation of the program `mainmodule`
559 # It is the main method usually called after the instantiation
560 fun do_compilation is abstract
561
562 # Force the creation of a new file
563 # The point is to avoid contamination between must-be-compiled-separately files
564 fun new_file(name: String): CodeFile
565 do
566 if modelbuilder.toolcontext.opt_group_c_files.value then
567 if self.files.is_empty then
568 var f = new CodeFile(mainmodule.c_name)
569 self.files.add(f)
570 end
571 return self.files.first
572 end
573 var f = new CodeFile(name)
574 self.files.add(f)
575 return f
576 end
577
578 # The list of all associated files
579 # Used to generate .c files
580 var files = new List[CodeFile]
581
582 # Initialize a visitor specific for a compiler engine
583 fun new_visitor: VISITOR is abstract
584
585 # Where global declaration are stored (the main .h)
586 var header: CodeWriter is writable, noinit
587
588 # Additionnal linker script for `ld`.
589 # Mainly used to do specific link-time symbol resolution
590 var linker_script = new Array[String]
591
592 # Provide a declaration that can be requested (before or latter) by a visitor
593 fun provide_declaration(key: String, s: String)
594 do
595 if self.provided_declarations.has_key(key) then
596 assert self.provided_declarations[key] == s
597 end
598 self.provided_declarations[key] = s
599 end
600
601 private var provided_declarations = new HashMap[String, String]
602
603 private var requirers_of_declarations = new HashMap[String, ANode]
604
605 # Builds the .c and .h files to be used when generating a Stack Trace
606 # Binds the generated C function names to Nit function names
607 fun build_c_to_nit_bindings
608 do
609 var compile_dir = toolchain.compile_dir
610
611 var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
612 stream.write("#include <string.h>\n")
613 stream.write("#include <stdlib.h>\n")
614 stream.write("#include \"c_functions_hash.h\"\n")
615 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
616 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
617 stream.write("char* procname = malloc(len+1);")
618 stream.write("memcpy(procname, procproc, len);")
619 stream.write("procname[len] = '\\0';")
620 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
621 for i in names.keys do
622 stream.write("\{\"")
623 stream.write(i.escape_to_c)
624 stream.write("\",\"")
625 stream.write(names[i].escape_to_c)
626 stream.write("\"\},\n")
627 end
628 stream.write("\};\n")
629 stream.write("int i;")
630 stream.write("for(i = 0; i < {names.length}; i++)\{")
631 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
632 stream.write("free(procname);")
633 stream.write("return map[i].nit_name;")
634 stream.write("\}")
635 stream.write("\}")
636 stream.write("free(procname);")
637 stream.write("return NULL;")
638 stream.write("\}\n")
639 stream.close
640
641 stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
642 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
643 stream.close
644
645 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
646 end
647
648 # Compile C headers
649 # This method call compile_header_strucs method that has to be refined
650 fun compile_header do
651 self.header.add_decl("#include <stdlib.h>")
652 self.header.add_decl("#include <stdio.h>")
653 self.header.add_decl("#include <string.h>")
654 self.header.add_decl("#include <sys/types.h>\n")
655 self.header.add_decl("#include <unistd.h>\n")
656 self.header.add_decl("#include <stdint.h>\n")
657 self.header.add_decl("#include <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 map.vararg_decl > 0 then
1226 var vararg = exprs.sub(j, map.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 var modelbuilder = v.compiler.modelbuilder
2026 var node = modelbuilder.mpropdef2node(self)
2027 if node isa APropdef then
2028 return node.can_inline
2029 else if node isa AClassdef then
2030 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2031 return true
2032 else
2033 abort
2034 end
2035 end
2036
2037 # Inline the body in another visitor
2038 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2039 do
2040 var modelbuilder = v.compiler.modelbuilder
2041 var val = constant_value
2042 var node = modelbuilder.mpropdef2node(self)
2043
2044 if is_abstract then
2045 var cn = v.class_name_string(arguments.first)
2046 v.current_node = node
2047 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2048 v.add_raw_abort
2049 return null
2050 end
2051
2052 if node isa APropdef then
2053 var oldnode = v.current_node
2054 v.current_node = node
2055 self.compile_parameter_check(v, arguments)
2056 node.compile_to_c(v, self, arguments)
2057 v.current_node = oldnode
2058 else if node isa AClassdef then
2059 var oldnode = v.current_node
2060 v.current_node = node
2061 self.compile_parameter_check(v, arguments)
2062 node.compile_to_c(v, self, arguments)
2063 v.current_node = oldnode
2064 else if val != null then
2065 v.ret(v.value_instance(val))
2066 else
2067 abort
2068 end
2069 return null
2070 end
2071
2072 # Generate type checks in the C code to check covariant parameters
2073 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2074 do
2075 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2076
2077 var msignature = self.msignature.as(not null)
2078
2079 for i in [0..msignature.arity[ do
2080 # skip test for vararg since the array is instantiated with the correct polymorphic type
2081 if msignature.vararg_rank == i then continue
2082
2083 # skip if the cast is not required
2084 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2085 if not origmtype.need_anchor then continue
2086
2087 # get the parameter type
2088 var mtype = msignature.mparameters[i].mtype
2089
2090 # generate the cast
2091 # note that v decides if and how to implements the cast
2092 v.add("/* Covariant cast for argument {i} ({msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
2093 v.add_cast(arguments[i+1], mtype, "covariance")
2094 end
2095 end
2096 end
2097
2098 # Node visit
2099
2100 redef class APropdef
2101 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2102 do
2103 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2104 debug("Not yet implemented")
2105 end
2106
2107 fun can_inline: Bool do return true
2108 end
2109
2110 redef class AMethPropdef
2111 redef fun compile_to_c(v, mpropdef, arguments)
2112 do
2113 # Call the implicit super-init
2114 var auto_super_inits = self.auto_super_inits
2115 if auto_super_inits != null then
2116 var args = [arguments.first]
2117 for auto_super_init in auto_super_inits do
2118 assert auto_super_init.mproperty != mpropdef.mproperty
2119 args.clear
2120 for i in [0..auto_super_init.msignature.arity+1[ do
2121 args.add(arguments[i])
2122 end
2123 assert auto_super_init.mproperty != mpropdef.mproperty
2124 v.compile_callsite(auto_super_init, args)
2125 end
2126 end
2127 if auto_super_call then
2128 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2129 end
2130
2131 # Try special compilation
2132 if mpropdef.is_intern then
2133 if compile_intern_to_c(v, mpropdef, arguments) then return
2134 else if mpropdef.is_extern then
2135 if mpropdef.mproperty.is_init then
2136 if compile_externinit_to_c(v, mpropdef, arguments) then return
2137 else
2138 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2139 end
2140 end
2141
2142 # Compile block if any
2143 var n_block = n_block
2144 if n_block != null then
2145 for i in [0..mpropdef.msignature.arity[ do
2146 var variable = self.n_signature.n_params[i].variable.as(not null)
2147 v.assign(v.variable(variable), arguments[i+1])
2148 end
2149 v.stmt(n_block)
2150 return
2151 end
2152
2153 # We have a problem
2154 var cn = v.class_name_string(arguments.first)
2155 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2156 v.add_raw_abort
2157 end
2158
2159 redef fun can_inline
2160 do
2161 if self.auto_super_inits != null then return false
2162 var nblock = self.n_block
2163 if nblock == null then return true
2164 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2165 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2166 return false
2167 end
2168
2169 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2170 do
2171 var pname = mpropdef.mproperty.name
2172 var cname = mpropdef.mclassdef.mclass.name
2173 var ret = mpropdef.msignature.return_mtype
2174 if ret != null then
2175 ret = v.resolve_for(ret, arguments.first)
2176 end
2177 if pname != "==" and pname != "!=" then
2178 v.adapt_signature(mpropdef, arguments)
2179 v.unbox_signature_extern(mpropdef, arguments)
2180 end
2181 if cname == "Int" then
2182 if pname == "output" then
2183 v.add("printf(\"%ld\\n\", {arguments.first});")
2184 return true
2185 else if pname == "object_id" then
2186 v.ret(arguments.first)
2187 return true
2188 else if pname == "+" then
2189 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2190 return true
2191 else if pname == "-" then
2192 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2193 return true
2194 else if pname == "unary -" then
2195 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2196 return true
2197 else if pname == "unary +" then
2198 v.ret(arguments[0])
2199 return true
2200 else if pname == "*" then
2201 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2202 return true
2203 else if pname == "/" then
2204 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2205 return true
2206 else if pname == "%" then
2207 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2208 return true
2209 else if pname == "==" then
2210 v.ret(v.equal_test(arguments[0], arguments[1]))
2211 return true
2212 else if pname == "!=" then
2213 var res = v.equal_test(arguments[0], arguments[1])
2214 v.ret(v.new_expr("!{res}", ret.as(not null)))
2215 return true
2216 else if pname == "<" then
2217 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2218 return true
2219 else if pname == ">" then
2220 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2221 return true
2222 else if pname == "<=" then
2223 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2224 return true
2225 else if pname == ">=" then
2226 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2227 return true
2228 else if pname == "to_i8" then
2229 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2230 return true
2231 else if pname == "to_i16" then
2232 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2233 return true
2234 else if pname == "to_u16" then
2235 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2236 return true
2237 else if pname == "to_i32" then
2238 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2239 return true
2240 else if pname == "to_u32" then
2241 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2242 return true
2243 else if pname == "to_f" then
2244 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2245 return true
2246 else if pname == "to_b" then
2247 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2248 return true
2249 end
2250 else if cname == "Char" then
2251 if pname == "object_id" then
2252 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2253 return true
2254 else if pname == "successor" then
2255 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2256 return true
2257 else if pname == "predecessor" then
2258 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2259 return true
2260 else if pname == "==" then
2261 v.ret(v.equal_test(arguments[0], arguments[1]))
2262 return true
2263 else if pname == "!=" then
2264 var res = v.equal_test(arguments[0], arguments[1])
2265 v.ret(v.new_expr("!{res}", ret.as(not null)))
2266 return true
2267 else if pname == "<" then
2268 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2269 return true
2270 else if pname == ">" then
2271 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2272 return true
2273 else if pname == "<=" then
2274 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2275 return true
2276 else if pname == ">=" then
2277 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2278 return true
2279 else if pname == "to_i" then
2280 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2281 return true
2282 end
2283 else if cname == "Byte" then
2284 if pname == "output" then
2285 v.add("printf(\"%x\\n\", {arguments.first});")
2286 return true
2287 else if pname == "object_id" then
2288 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2289 return true
2290 else if pname == "+" then
2291 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2292 return true
2293 else if pname == "-" then
2294 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2295 return true
2296 else if pname == "unary -" then
2297 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2298 return true
2299 else if pname == "unary +" then
2300 v.ret(arguments[0])
2301 return true
2302 else if pname == "*" then
2303 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2304 return true
2305 else if pname == "/" then
2306 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2307 return true
2308 else if pname == "%" then
2309 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2310 return true
2311 else if pname == "==" then
2312 v.ret(v.equal_test(arguments[0], arguments[1]))
2313 return true
2314 else if pname == "!=" then
2315 var res = v.equal_test(arguments[0], arguments[1])
2316 v.ret(v.new_expr("!{res}", ret.as(not null)))
2317 return true
2318 else if pname == "<" then
2319 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2320 return true
2321 else if pname == ">" then
2322 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2323 return true
2324 else if pname == "<=" then
2325 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2326 return true
2327 else if pname == ">=" then
2328 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2329 return true
2330 else if pname == "to_i" then
2331 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2332 return true
2333 else if pname == "to_f" then
2334 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2335 return true
2336 else if pname == "to_i8" then
2337 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2338 return true
2339 else if pname == "to_i16" then
2340 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2341 return true
2342 else if pname == "to_u16" then
2343 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2344 return true
2345 else if pname == "to_i32" then
2346 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2347 return true
2348 else if pname == "to_u32" then
2349 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2350 return true
2351 end
2352 else if cname == "Bool" then
2353 if pname == "output" then
2354 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2355 return true
2356 else if pname == "object_id" then
2357 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2358 return true
2359 else if pname == "==" then
2360 v.ret(v.equal_test(arguments[0], arguments[1]))
2361 return true
2362 else if pname == "!=" then
2363 var res = v.equal_test(arguments[0], arguments[1])
2364 v.ret(v.new_expr("!{res}", ret.as(not null)))
2365 return true
2366 end
2367 else if cname == "Float" then
2368 if pname == "output" then
2369 v.add("printf(\"%f\\n\", {arguments.first});")
2370 return true
2371 else if pname == "object_id" then
2372 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2373 return true
2374 else if pname == "+" then
2375 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2376 return true
2377 else if pname == "-" then
2378 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2379 return true
2380 else if pname == "unary -" then
2381 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2382 return true
2383 else if pname == "unary +" then
2384 v.ret(arguments[0])
2385 return true
2386 else if pname == "succ" then
2387 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2388 return true
2389 else if pname == "prec" then
2390 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2391 return true
2392 else if pname == "*" then
2393 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2394 return true
2395 else if pname == "/" then
2396 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2397 return true
2398 else if pname == "==" then
2399 v.ret(v.equal_test(arguments[0], arguments[1]))
2400 return true
2401 else if pname == "!=" then
2402 var res = v.equal_test(arguments[0], arguments[1])
2403 v.ret(v.new_expr("!{res}", ret.as(not null)))
2404 return true
2405 else if pname == "<" then
2406 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2407 return true
2408 else if pname == ">" then
2409 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2410 return true
2411 else if pname == "<=" then
2412 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2413 return true
2414 else if pname == ">=" then
2415 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2416 return true
2417 else if pname == "to_i" then
2418 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2419 return true
2420 else if pname == "to_b" then
2421 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2422 return true
2423 else if pname == "to_i8" then
2424 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2425 return true
2426 else if pname == "to_i16" then
2427 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2428 return true
2429 else if pname == "to_u16" then
2430 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2431 return true
2432 else if pname == "to_i32" then
2433 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2434 return true
2435 else if pname == "to_u32" then
2436 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2437 return true
2438 end
2439 else if cname == "NativeString" then
2440 if pname == "[]" then
2441 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2442 return true
2443 else if pname == "[]=" then
2444 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2445 return true
2446 else if pname == "copy_to" then
2447 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2448 return true
2449 else if pname == "atoi" then
2450 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2451 return true
2452 else if pname == "fast_cstring" then
2453 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2454 return true
2455 else if pname == "new" then
2456 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2457 return true
2458 end
2459 else if cname == "NativeArray" then
2460 v.native_array_def(pname, ret, arguments)
2461 return true
2462 else if cname == "Int8" then
2463 if pname == "output" then
2464 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2465 return true
2466 else if pname == "object_id" then
2467 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2468 return true
2469 else if pname == "+" then
2470 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2471 return true
2472 else if pname == "-" then
2473 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2474 return true
2475 else if pname == "unary -" then
2476 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2477 return true
2478 else if pname == "unary +" then
2479 v.ret(arguments[0])
2480 return true
2481 else if pname == "*" then
2482 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2483 return true
2484 else if pname == "/" then
2485 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2486 return true
2487 else if pname == "%" then
2488 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2489 return true
2490 else if pname == "<<" then
2491 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2492 return true
2493 else if pname == ">>" then
2494 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2495 return true
2496 else if pname == "==" then
2497 v.ret(v.equal_test(arguments[0], arguments[1]))
2498 return true
2499 else if pname == "!=" then
2500 var res = v.equal_test(arguments[0], arguments[1])
2501 v.ret(v.new_expr("!{res}", ret.as(not null)))
2502 return true
2503 else if pname == "<" then
2504 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2505 return true
2506 else if pname == ">" then
2507 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2508 return true
2509 else if pname == "<=" then
2510 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2511 return true
2512 else if pname == ">=" then
2513 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2514 return true
2515 else if pname == "to_i" then
2516 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2517 return true
2518 else if pname == "to_b" then
2519 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2520 return true
2521 else if pname == "to_i16" then
2522 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2523 return true
2524 else if pname == "to_u16" then
2525 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2526 return true
2527 else if pname == "to_i32" then
2528 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2529 return true
2530 else if pname == "to_u32" then
2531 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2532 return true
2533 else if pname == "to_f" then
2534 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2535 return true
2536 else if pname == "&" then
2537 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2538 return true
2539 else if pname == "|" then
2540 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2541 return true
2542 else if pname == "^" then
2543 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2544 return true
2545 else if pname == "unary ~" then
2546 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2547 return true
2548 end
2549 else if cname == "Int16" then
2550 if pname == "output" then
2551 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2552 return true
2553 else if pname == "object_id" then
2554 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2555 return true
2556 else if pname == "+" then
2557 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2558 return true
2559 else if pname == "-" then
2560 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2561 return true
2562 else if pname == "unary -" then
2563 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2564 return true
2565 else if pname == "unary +" then
2566 v.ret(arguments[0])
2567 return true
2568 else if pname == "*" then
2569 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2570 return true
2571 else if pname == "/" then
2572 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2573 return true
2574 else if pname == "%" then
2575 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2576 return true
2577 else if pname == "<<" then
2578 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2579 return true
2580 else if pname == ">>" then
2581 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2582 return true
2583 else if pname == "==" then
2584 v.ret(v.equal_test(arguments[0], arguments[1]))
2585 return true
2586 else if pname == "!=" then
2587 var res = v.equal_test(arguments[0], arguments[1])
2588 v.ret(v.new_expr("!{res}", ret.as(not null)))
2589 return true
2590 else if pname == "<" then
2591 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2592 return true
2593 else if pname == ">" then
2594 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2595 return true
2596 else if pname == "<=" then
2597 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2598 return true
2599 else if pname == ">=" then
2600 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2601 return true
2602 else if pname == "to_i" then
2603 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2604 return true
2605 else if pname == "to_b" then
2606 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2607 return true
2608 else if pname == "to_i8" then
2609 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2610 return true
2611 else if pname == "to_u16" then
2612 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2613 return true
2614 else if pname == "to_i32" then
2615 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2616 return true
2617 else if pname == "to_u32" then
2618 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2619 return true
2620 else if pname == "to_f" then
2621 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2622 return true
2623 else if pname == "&" then
2624 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2625 return true
2626 else if pname == "|" then
2627 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2628 return true
2629 else if pname == "^" then
2630 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2631 return true
2632 else if pname == "unary ~" then
2633 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2634 return true
2635 end
2636 else if cname == "UInt16" then
2637 if pname == "output" then
2638 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2639 return true
2640 else if pname == "object_id" then
2641 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2642 return true
2643 else if pname == "+" then
2644 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2645 return true
2646 else if pname == "-" then
2647 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2648 return true
2649 else if pname == "unary -" then
2650 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2651 return true
2652 else if pname == "unary +" then
2653 v.ret(arguments[0])
2654 return true
2655 else if pname == "*" then
2656 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2657 return true
2658 else if pname == "/" then
2659 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2660 return true
2661 else if pname == "%" then
2662 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2663 return true
2664 else if pname == "<<" then
2665 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2666 return true
2667 else if pname == ">>" then
2668 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2669 return true
2670 else if pname == "==" then
2671 v.ret(v.equal_test(arguments[0], arguments[1]))
2672 return true
2673 else if pname == "!=" then
2674 var res = v.equal_test(arguments[0], arguments[1])
2675 v.ret(v.new_expr("!{res}", ret.as(not null)))
2676 return true
2677 else if pname == "<" then
2678 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2679 return true
2680 else if pname == ">" then
2681 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2682 return true
2683 else if pname == "<=" then
2684 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2685 return true
2686 else if pname == ">=" then
2687 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2688 return true
2689 else if pname == "to_i" then
2690 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2691 return true
2692 else if pname == "to_b" then
2693 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2694 return true
2695 else if pname == "to_i8" then
2696 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2697 return true
2698 else if pname == "to_i16" then
2699 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2700 return true
2701 else if pname == "to_i32" then
2702 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2703 return true
2704 else if pname == "to_u32" then
2705 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2706 return true
2707 else if pname == "to_f" then
2708 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2709 return true
2710 else if pname == "&" then
2711 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2712 return true
2713 else if pname == "|" then
2714 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2715 return true
2716 else if pname == "^" then
2717 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2718 return true
2719 else if pname == "unary ~" then
2720 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2721 return true
2722 end
2723 else if cname == "Int32" then
2724 if pname == "output" then
2725 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2726 return true
2727 else if pname == "object_id" then
2728 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2729 return true
2730 else if pname == "+" then
2731 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2732 return true
2733 else if pname == "-" then
2734 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2735 return true
2736 else if pname == "unary -" then
2737 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2738 return true
2739 else if pname == "unary +" then
2740 v.ret(arguments[0])
2741 return true
2742 else if pname == "*" then
2743 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2744 return true
2745 else if pname == "/" then
2746 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2747 return true
2748 else if pname == "%" then
2749 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2750 return true
2751 else if pname == "<<" then
2752 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2753 return true
2754 else if pname == ">>" then
2755 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2756 return true
2757 else if pname == "==" then
2758 v.ret(v.equal_test(arguments[0], arguments[1]))
2759 return true
2760 else if pname == "!=" then
2761 var res = v.equal_test(arguments[0], arguments[1])
2762 v.ret(v.new_expr("!{res}", ret.as(not null)))
2763 return true
2764 else if pname == "<" then
2765 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2766 return true
2767 else if pname == ">" then
2768 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2769 return true
2770 else if pname == "<=" then
2771 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2772 return true
2773 else if pname == ">=" then
2774 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2775 return true
2776 else if pname == "to_i" then
2777 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2778 return true
2779 else if pname == "to_b" then
2780 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2781 return true
2782 else if pname == "to_i8" then
2783 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2784 return true
2785 else if pname == "to_i16" then
2786 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2787 return true
2788 else if pname == "to_u16" then
2789 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2790 return true
2791 else if pname == "to_u32" then
2792 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2793 return true
2794 else if pname == "to_f" then
2795 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2796 return true
2797 else if pname == "&" then
2798 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2799 return true
2800 else if pname == "|" then
2801 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2802 return true
2803 else if pname == "^" then
2804 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2805 return true
2806 else if pname == "unary ~" then
2807 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2808 return true
2809 end
2810 else if cname == "UInt32" then
2811 if pname == "output" then
2812 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2813 return true
2814 else if pname == "object_id" then
2815 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2816 return true
2817 else if pname == "+" then
2818 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2819 return true
2820 else if pname == "-" then
2821 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2822 return true
2823 else if pname == "unary -" then
2824 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2825 return true
2826 else if pname == "unary +" then
2827 v.ret(arguments[0])
2828 return true
2829 else if pname == "*" then
2830 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2831 return true
2832 else if pname == "/" then
2833 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2834 return true
2835 else if pname == "%" then
2836 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2837 return true
2838 else if pname == "<<" then
2839 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2840 return true
2841 else if pname == ">>" then
2842 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2843 return true
2844 else if pname == "==" then
2845 v.ret(v.equal_test(arguments[0], arguments[1]))
2846 return true
2847 else if pname == "!=" then
2848 var res = v.equal_test(arguments[0], arguments[1])
2849 v.ret(v.new_expr("!{res}", ret.as(not null)))
2850 return true
2851 else if pname == "<" then
2852 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2853 return true
2854 else if pname == ">" then
2855 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2856 return true
2857 else if pname == "<=" then
2858 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2859 return true
2860 else if pname == ">=" then
2861 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2862 return true
2863 else if pname == "to_i" then
2864 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2865 return true
2866 else if pname == "to_b" then
2867 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2868 return true
2869 else if pname == "to_i8" then
2870 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2871 return true
2872 else if pname == "to_i16" then
2873 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2874 return true
2875 else if pname == "to_u16" then
2876 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2877 return true
2878 else if pname == "to_i32" then
2879 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2880 return true
2881 else if pname == "to_f" then
2882 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2883 return true
2884 else if pname == "&" then
2885 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2886 return true
2887 else if pname == "|" then
2888 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2889 return true
2890 else if pname == "^" then
2891 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2892 return true
2893 else if pname == "unary ~" then
2894 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2895 return true
2896 end
2897 end
2898 if pname == "exit" then
2899 v.add("exit({arguments[1]});")
2900 return true
2901 else if pname == "sys" then
2902 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2903 return true
2904 else if pname == "calloc_string" then
2905 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2906 return true
2907 else if pname == "calloc_array" then
2908 v.calloc_array(ret.as(not null), arguments)
2909 return true
2910 else if pname == "object_id" then
2911 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2912 return true
2913 else if pname == "is_same_type" then
2914 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2915 return true
2916 else if pname == "is_same_instance" then
2917 v.ret(v.equal_test(arguments[0], arguments[1]))
2918 return true
2919 else if pname == "output_class_name" then
2920 var nat = v.class_name_string(arguments.first)
2921 v.add("printf(\"%s\\n\", {nat});")
2922 return true
2923 else if pname == "native_class_name" then
2924 var nat = v.class_name_string(arguments.first)
2925 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2926 return true
2927 else if pname == "force_garbage_collection" then
2928 v.add("nit_gcollect();")
2929 return true
2930 else if pname == "native_argc" then
2931 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2932 return true
2933 else if pname == "native_argv" then
2934 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2935 return true
2936 end
2937 return false
2938 end
2939
2940 # Compile an extern method
2941 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2942 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2943 do
2944 var externname
2945 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2946 if at != null and at.n_args.length == 1 then
2947 externname = at.arg_as_string(v.compiler.modelbuilder)
2948 if externname == null then return false
2949 else
2950 return false
2951 end
2952 v.add_extern(mpropdef.mclassdef.mmodule)
2953 var res: nullable RuntimeVariable = null
2954 var ret = mpropdef.msignature.return_mtype
2955 if ret != null then
2956 ret = v.resolve_for(ret, arguments.first)
2957 res = v.new_var_extern(ret)
2958 end
2959 v.adapt_signature(mpropdef, arguments)
2960 v.unbox_signature_extern(mpropdef, arguments)
2961
2962 if res == null then
2963 v.add("{externname}({arguments.join(", ")});")
2964 else
2965 v.add("{res} = {externname}({arguments.join(", ")});")
2966 res = v.box_extern(res, ret.as(not null))
2967 v.ret(res)
2968 end
2969 return true
2970 end
2971
2972 # Compile an extern factory
2973 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2974 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2975 do
2976 var externname
2977 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2978 if at != null then
2979 externname = at.arg_as_string(v.compiler.modelbuilder)
2980 if externname == null then return false
2981 else
2982 return false
2983 end
2984 v.add_extern(mpropdef.mclassdef.mmodule)
2985 v.adapt_signature(mpropdef, arguments)
2986 v.unbox_signature_extern(mpropdef, arguments)
2987 var ret = arguments.first.mtype
2988 var res = v.new_var_extern(ret)
2989
2990 arguments.shift
2991
2992 v.add("{res} = {externname}({arguments.join(", ")});")
2993 res = v.box_extern(res, ret)
2994 v.ret(res)
2995 return true
2996 end
2997 end
2998
2999 redef class AAttrPropdef
3000 redef fun can_inline: Bool do return not is_lazy
3001
3002 redef fun compile_to_c(v, mpropdef, arguments)
3003 do
3004 if mpropdef == mreadpropdef then
3005 assert arguments.length == 1
3006 var recv = arguments.first
3007 var res
3008 if is_lazy then
3009 var set
3010 var ret = self.mtype
3011 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3012 var guard = self.mlazypropdef.mproperty
3013 if useiset then
3014 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3015 else
3016 set = v.read_attribute(guard, recv)
3017 end
3018 v.add("if(likely({set})) \{")
3019 res = v.read_attribute(self.mpropdef.mproperty, recv)
3020 v.add("\} else \{")
3021
3022 var value = evaluate_expr(v, recv)
3023
3024 v.assign(res, value)
3025 if not useiset then
3026 var true_v = v.bool_instance(true)
3027 v.write_attribute(guard, arguments.first, true_v)
3028 end
3029 v.add("\}")
3030 else
3031 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3032 end
3033 v.assign(v.frame.returnvar.as(not null), res)
3034 else if mpropdef == mwritepropdef then
3035 assert arguments.length == 2
3036 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
3037 if is_lazy then
3038 var ret = self.mtype
3039 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3040 if not useiset then
3041 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3042 end
3043 end
3044 else
3045 abort
3046 end
3047 end
3048
3049 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3050 do
3051 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3052 end
3053
3054 # Evaluate, store and return the default value of the attribute
3055 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3056 do
3057 var oldnode = v.current_node
3058 v.current_node = self
3059 var old_frame = v.frame
3060 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3061 v.frame = frame
3062
3063 var value
3064 var mtype = self.mtype
3065 assert mtype != null
3066
3067 var nexpr = self.n_expr
3068 var nblock = self.n_block
3069 if nexpr != null then
3070 value = v.expr(nexpr, mtype)
3071 else if nblock != null then
3072 value = v.new_var(mtype)
3073 frame.returnvar = value
3074 frame.returnlabel = v.get_name("RET_LABEL")
3075 v.add("\{")
3076 v.stmt(nblock)
3077 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3078 v.add("\}")
3079 else
3080 abort
3081 end
3082
3083 v.write_attribute(self.mpropdef.mproperty, recv, value)
3084
3085 v.frame = old_frame
3086 v.current_node = oldnode
3087
3088 return value
3089 end
3090
3091 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3092 do
3093 var nexpr = self.n_expr
3094 if nexpr != null then return
3095
3096 var oldnode = v.current_node
3097 v.current_node = self
3098 var old_frame = v.frame
3099 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3100 v.frame = frame
3101 # Force read to check the initialization
3102 v.read_attribute(self.mpropdef.mproperty, recv)
3103 v.frame = old_frame
3104 v.current_node = oldnode
3105 end
3106 end
3107
3108 redef class AClassdef
3109 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3110 do
3111 if mpropdef == self.mfree_init then
3112 assert mpropdef.mproperty.is_root_init
3113 assert arguments.length == 1
3114 if not mpropdef.is_intro then
3115 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3116 end
3117 return
3118 else
3119 abort
3120 end
3121 end
3122 end
3123
3124 redef class AExpr
3125 # Try to compile self as an expression
3126 # Do not call this method directly, use `v.expr` instead
3127 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3128 do
3129 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3130 var mtype = self.mtype
3131 if mtype == null then
3132 return null
3133 else
3134 var res = v.new_var(mtype)
3135 v.add("/* {res} = NOT YET {class_name} */")
3136 return res
3137 end
3138 end
3139
3140 # Try to compile self as a statement
3141 # Do not call this method directly, use `v.stmt` instead
3142 private fun stmt(v: AbstractCompilerVisitor)
3143 do
3144 expr(v)
3145 end
3146 end
3147
3148 redef class ABlockExpr
3149 redef fun stmt(v)
3150 do
3151 for e in self.n_expr do v.stmt(e)
3152 end
3153 redef fun expr(v)
3154 do
3155 var last = self.n_expr.last
3156 for e in self.n_expr do
3157 if e == last then break
3158 v.stmt(e)
3159 end
3160 return v.expr(last, null)
3161 end
3162 end
3163
3164 redef class AVardeclExpr
3165 redef fun stmt(v)
3166 do
3167 var variable = self.variable.as(not null)
3168 var ne = self.n_expr
3169 if ne != null then
3170 var i = v.expr(ne, variable.declared_type)
3171 v.assign(v.variable(variable), i)
3172 end
3173 end
3174 end
3175
3176 redef class AVarExpr
3177 redef fun expr(v)
3178 do
3179 var res = v.variable(self.variable.as(not null))
3180 var mtype = self.mtype.as(not null)
3181 return v.autoadapt(res, mtype)
3182 end
3183 end
3184
3185 redef class AVarAssignExpr
3186 redef fun expr(v)
3187 do
3188 var variable = self.variable.as(not null)
3189 var i = v.expr(self.n_value, variable.declared_type)
3190 v.assign(v.variable(variable), i)
3191 return i
3192 end
3193 end
3194
3195 redef class AVarReassignExpr
3196 redef fun stmt(v)
3197 do
3198 var variable = self.variable.as(not null)
3199 var vari = v.variable(variable)
3200 var value = v.expr(self.n_value, variable.declared_type)
3201 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3202 assert res != null
3203 v.assign(v.variable(variable), res)
3204 end
3205 end
3206
3207 redef class ASelfExpr
3208 redef fun expr(v) do return v.frame.arguments.first
3209 end
3210
3211 redef class AImplicitSelfExpr
3212 redef fun expr(v) do
3213 if not is_sys then return super
3214 return v.new_expr("glob_sys", mtype.as(not null))
3215 end
3216 end
3217
3218 redef class AEscapeExpr
3219 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3220 end
3221
3222 redef class AReturnExpr
3223 redef fun stmt(v)
3224 do
3225 var nexpr = self.n_expr
3226 if nexpr != null then
3227 var returnvar = v.frame.returnvar.as(not null)
3228 var i = v.expr(nexpr, returnvar.mtype)
3229 v.assign(returnvar, i)
3230 end
3231 v.add("goto {v.frame.returnlabel.as(not null)};")
3232 end
3233 end
3234
3235 redef class AAbortExpr
3236 redef fun stmt(v) do v.add_abort("Aborted")
3237 end
3238
3239 redef class AIfExpr
3240 redef fun stmt(v)
3241 do
3242 var cond = v.expr_bool(self.n_expr)
3243 v.add("if ({cond})\{")
3244 v.stmt(self.n_then)
3245 v.add("\} else \{")
3246 v.stmt(self.n_else)
3247 v.add("\}")
3248 end
3249
3250 redef fun expr(v)
3251 do
3252 var res = v.new_var(self.mtype.as(not null))
3253 var cond = v.expr_bool(self.n_expr)
3254 v.add("if ({cond})\{")
3255 v.assign(res, v.expr(self.n_then.as(not null), null))
3256 v.add("\} else \{")
3257 v.assign(res, v.expr(self.n_else.as(not null), null))
3258 v.add("\}")
3259 return res
3260 end
3261 end
3262
3263 redef class AIfexprExpr
3264 redef fun expr(v)
3265 do
3266 var res = v.new_var(self.mtype.as(not null))
3267 var cond = v.expr_bool(self.n_expr)
3268 v.add("if ({cond})\{")
3269 v.assign(res, v.expr(self.n_then, null))
3270 v.add("\} else \{")
3271 v.assign(res, v.expr(self.n_else, null))
3272 v.add("\}")
3273 return res
3274 end
3275 end
3276
3277 redef class ADoExpr
3278 redef fun stmt(v)
3279 do
3280 v.stmt(self.n_block)
3281 v.add_escape_label(break_mark)
3282 end
3283 end
3284
3285 redef class AWhileExpr
3286 redef fun stmt(v)
3287 do
3288 v.add("for(;;) \{")
3289 var cond = v.expr_bool(self.n_expr)
3290 v.add("if (!{cond}) break;")
3291 v.stmt(self.n_block)
3292 v.add_escape_label(continue_mark)
3293 v.add("\}")
3294 v.add_escape_label(break_mark)
3295 end
3296 end
3297
3298 redef class ALoopExpr
3299 redef fun stmt(v)
3300 do
3301 v.add("for(;;) \{")
3302 v.stmt(self.n_block)
3303 v.add_escape_label(continue_mark)
3304 v.add("\}")
3305 v.add_escape_label(break_mark)
3306 end
3307 end
3308
3309 redef class AForExpr
3310 redef fun stmt(v)
3311 do
3312 for g in n_groups do
3313 var cl = v.expr(g.n_expr, null)
3314 var it_meth = g.method_iterator
3315 assert it_meth != null
3316 var it = v.compile_callsite(it_meth, [cl])
3317 assert it != null
3318 g.it = it
3319 end
3320 v.add("for(;;) \{")
3321 for g in n_groups do
3322 var it = g.it
3323 var isok_meth = g.method_is_ok
3324 assert isok_meth != null
3325 var ok = v.compile_callsite(isok_meth, [it])
3326 assert ok != null
3327 v.add("if(!{ok}) break;")
3328 if g.variables.length == 1 then
3329 var item_meth = g.method_item
3330 assert item_meth != null
3331 var i = v.compile_callsite(item_meth, [it])
3332 assert i != null
3333 v.assign(v.variable(g.variables.first), i)
3334 else if g.variables.length == 2 then
3335 var key_meth = g.method_key
3336 assert key_meth != null
3337 var i = v.compile_callsite(key_meth, [it])
3338 assert i != null
3339 v.assign(v.variable(g.variables[0]), i)
3340 var item_meth = g.method_item
3341 assert item_meth != null
3342 i = v.compile_callsite(item_meth, [it])
3343 assert i != null
3344 v.assign(v.variable(g.variables[1]), i)
3345 else
3346 abort
3347 end
3348 end
3349 v.stmt(self.n_block)
3350 v.add_escape_label(continue_mark)
3351 for g in n_groups do
3352 var next_meth = g.method_next
3353 assert next_meth != null
3354 v.compile_callsite(next_meth, [g.it])
3355 end
3356 v.add("\}")
3357 v.add_escape_label(break_mark)
3358
3359 for g in n_groups do
3360 var method_finish = g.method_finish
3361 if method_finish != null then
3362 # TODO: Find a way to call this also in long escape (e.g. return)
3363 v.compile_callsite(method_finish, [g.it])
3364 end
3365 end
3366 end
3367 end
3368
3369 redef class AForGroup
3370 # C variable representing the iterator
3371 private var it: RuntimeVariable is noinit
3372 end
3373
3374 redef class AAssertExpr
3375 redef fun stmt(v)
3376 do
3377 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3378
3379 var cond = v.expr_bool(self.n_expr)
3380 v.add("if (unlikely(!{cond})) \{")
3381 v.stmt(self.n_else)
3382 var nid = self.n_id
3383 if nid != null then
3384 v.add_abort("Assert '{nid.text}' failed")
3385 else
3386 v.add_abort("Assert failed")
3387 end
3388 v.add("\}")
3389 end
3390 end
3391
3392 redef class AOrExpr
3393 redef fun expr(v)
3394 do
3395 var res = v.new_var(self.mtype.as(not null))
3396 var i1 = v.expr_bool(self.n_expr)
3397 v.add("if ({i1}) \{")
3398 v.add("{res} = 1;")
3399 v.add("\} else \{")
3400 var i2 = v.expr_bool(self.n_expr2)
3401 v.add("{res} = {i2};")
3402 v.add("\}")
3403 return res
3404 end
3405 end
3406
3407 redef class AImpliesExpr
3408 redef fun expr(v)
3409 do
3410 var res = v.new_var(self.mtype.as(not null))
3411 var i1 = v.expr_bool(self.n_expr)
3412 v.add("if (!{i1}) \{")
3413 v.add("{res} = 1;")
3414 v.add("\} else \{")
3415 var i2 = v.expr_bool(self.n_expr2)
3416 v.add("{res} = {i2};")
3417 v.add("\}")
3418 return res
3419 end
3420 end
3421
3422 redef class AAndExpr
3423 redef fun expr(v)
3424 do
3425 var res = v.new_var(self.mtype.as(not null))
3426 var i1 = v.expr_bool(self.n_expr)
3427 v.add("if (!{i1}) \{")
3428 v.add("{res} = 0;")
3429 v.add("\} else \{")
3430 var i2 = v.expr_bool(self.n_expr2)
3431 v.add("{res} = {i2};")
3432 v.add("\}")
3433 return res
3434 end
3435 end
3436
3437 redef class ANotExpr
3438 redef fun expr(v)
3439 do
3440 var cond = v.expr_bool(self.n_expr)
3441 return v.new_expr("!{cond}", self.mtype.as(not null))
3442 end
3443 end
3444
3445 redef class AOrElseExpr
3446 redef fun expr(v)
3447 do
3448 var res = v.new_var(self.mtype.as(not null))
3449 var i1 = v.expr(self.n_expr, null)
3450 v.add("if ({i1}!=NULL) \{")
3451 v.assign(res, i1)
3452 v.add("\} else \{")
3453 var i2 = v.expr(self.n_expr2, null)
3454 v.assign(res, i2)
3455 v.add("\}")
3456 return res
3457 end
3458 end
3459
3460 redef class AIntegerExpr
3461 redef fun expr(v) do
3462 if value isa Int then return v.int_instance(value.as(Int))
3463 if value isa Byte then return v.byte_instance(value.as(Byte))
3464 if value isa Int8 then return v.int8_instance(value.as(Int8))
3465 if value isa Int16 then return v.int16_instance(value.as(Int16))
3466 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3467 if value isa Int32 then return v.int32_instance(value.as(Int32))
3468 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3469 # Should never happen
3470 abort
3471 end
3472 end
3473
3474 redef class AFloatExpr
3475 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3476 end
3477
3478 redef class ACharExpr
3479 redef fun expr(v) do return v.char_instance(self.value.as(not null))
3480 end
3481
3482 redef class AArrayExpr
3483 redef fun expr(v)
3484 do
3485 var mtype = self.element_mtype.as(not null)
3486 var array = new Array[RuntimeVariable]
3487 var res = v.array_instance(array, mtype)
3488
3489 var old_comprehension = v.frame.comprehension
3490 v.frame.comprehension = res
3491 for nexpr in self.n_exprs do
3492 v.stmt(nexpr)
3493 end
3494 v.frame.comprehension = old_comprehension
3495
3496 return res
3497 end
3498 end
3499
3500 redef class AStringFormExpr
3501 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3502 end
3503
3504 redef class ASuperstringExpr
3505 redef fun expr(v)
3506 do
3507 var type_string = mtype.as(not null)
3508
3509 # Collect elements of the superstring
3510 var array = new Array[AExpr]
3511 for ne in self.n_exprs do
3512 # Drop literal empty string.
3513 # They appears in things like "{a}" that is ["", a, ""]
3514 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3515 array.add(ne)
3516 end
3517
3518 # Store the allocated native array in a static variable
3519 # For reusing later
3520 var varonce = v.get_name("varonce")
3521 v.add("if (unlikely({varonce}==NULL)) \{")
3522
3523 # The native array that will contains the elements to_s-ized.
3524 # For fast concatenation.
3525 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3526
3527 v.add_decl("static {a.mtype.ctype} {varonce};")
3528
3529 # Pre-fill the array with the literal string parts.
3530 # So they do not need to be filled again when reused
3531 for i in [0..array.length[ do
3532 var ne = array[i]
3533 if not ne isa AStringFormExpr then continue
3534 var e = v.expr(ne, null)
3535 v.native_array_set(a, i, e)
3536 end
3537
3538 v.add("\} else \{")
3539 # Take the native-array from the store.
3540 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3541 # WARNING: not thread safe! (FIXME?)
3542 v.add("{a} = {varonce};")
3543 v.add("{varonce} = NULL;")
3544 v.add("\}")
3545
3546 # Stringify the elements and put them in the native array
3547 var to_s_method = v.get_property("to_s", v.object_type)
3548 for i in [0..array.length[ do
3549 var ne = array[i]
3550 if ne isa AStringFormExpr then continue
3551 var e = v.expr(ne, null)
3552 # Skip the `to_s` if the element is already a String
3553 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3554 e = v.send(to_s_method, [e]).as(not null)
3555 end
3556 v.native_array_set(a, i, e)
3557 end
3558
3559 # Fast join the native string to get the result
3560 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3561
3562 # We finish to work with the native array,
3563 # so store it so that it can be reused
3564 v.add("{varonce} = {a};")
3565 return res
3566 end
3567 end
3568
3569 redef class ACrangeExpr
3570 redef fun expr(v)
3571 do
3572 var i1 = v.expr(self.n_expr, null)
3573 var i2 = v.expr(self.n_expr2, null)
3574 var mtype = self.mtype.as(MClassType)
3575 var res = v.init_instance(mtype)
3576 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3577 return res
3578 end
3579 end
3580
3581 redef class AOrangeExpr
3582 redef fun expr(v)
3583 do
3584 var i1 = v.expr(self.n_expr, null)
3585 var i2 = v.expr(self.n_expr2, null)
3586 var mtype = self.mtype.as(MClassType)
3587 var res = v.init_instance(mtype)
3588 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3589 return res
3590 end
3591 end
3592
3593 redef class ATrueExpr
3594 redef fun expr(v) do return v.bool_instance(true)
3595 end
3596
3597 redef class AFalseExpr
3598 redef fun expr(v) do return v.bool_instance(false)
3599 end
3600
3601 redef class ANullExpr
3602 redef fun expr(v) do return v.null_instance
3603 end
3604
3605 redef class AIsaExpr
3606 redef fun expr(v)
3607 do
3608 var i = v.expr(self.n_expr, null)
3609 var cast_type = self.cast_type
3610 if cast_type == null then return null # no-no on broken node
3611 return v.type_test(i, cast_type, "isa")
3612 end
3613 end
3614
3615 redef class AAsCastExpr
3616 redef fun expr(v)
3617 do
3618 var i = v.expr(self.n_expr, null)
3619 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3620
3621 v.add_cast(i, self.mtype.as(not null), "as")
3622 return i
3623 end
3624 end
3625
3626 redef class AAsNotnullExpr
3627 redef fun expr(v)
3628 do
3629 var i = v.expr(self.n_expr, null)
3630 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3631
3632 if i.mtype.is_c_primitive then return i
3633
3634 v.add("if (unlikely({i} == NULL)) \{")
3635 v.add_abort("Cast failed")
3636 v.add("\}")
3637 return i
3638 end
3639 end
3640
3641 redef class AParExpr
3642 redef fun expr(v) do return v.expr(self.n_expr, null)
3643 end
3644
3645 redef class AOnceExpr
3646 redef fun expr(v)
3647 do
3648 var mtype = self.mtype.as(not null)
3649 var name = v.get_name("varonce")
3650 var guard = v.get_name(name + "_guard")
3651 v.add_decl("static {mtype.ctype} {name};")
3652 v.add_decl("static int {guard};")
3653 var res = v.new_var(mtype)
3654 v.add("if (likely({guard})) \{")
3655 v.add("{res} = {name};")
3656 v.add("\} else \{")
3657 var i = v.expr(self.n_expr, mtype)
3658 v.add("{res} = {i};")
3659 v.add("{name} = {res};")
3660 v.add("{guard} = 1;")
3661 v.add("\}")
3662 return res
3663 end
3664 end
3665
3666 redef class ASendExpr
3667 redef fun expr(v)
3668 do
3669 var recv = v.expr(self.n_expr, null)
3670 var callsite = self.callsite.as(not null)
3671 if callsite.is_broken then return null
3672 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3673 return v.compile_callsite(callsite, args)
3674 end
3675 end
3676
3677 redef class ASendReassignFormExpr
3678 redef fun stmt(v)
3679 do
3680 var recv = v.expr(self.n_expr, null)
3681 var callsite = self.callsite.as(not null)
3682 if callsite.is_broken then return
3683 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3684
3685 var value = v.expr(self.n_value, null)
3686
3687 var left = v.compile_callsite(callsite, args)
3688 assert left != null
3689
3690 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3691 assert res != null
3692
3693 args.add(res)
3694 v.compile_callsite(self.write_callsite.as(not null), args)
3695 end
3696 end
3697
3698 redef class ASuperExpr
3699 redef fun expr(v)
3700 do
3701 var frame = v.frame.as(not null)
3702 var recv = frame.arguments.first
3703
3704 var callsite = self.callsite
3705 if callsite != null then
3706 if callsite.is_broken then return null
3707 var args
3708
3709 if self.n_args.n_exprs.is_empty then
3710 # Add automatic arguments for the super init call
3711 args = [recv]
3712 for i in [0..callsite.msignature.arity[ do
3713 args.add(frame.arguments[i+1])
3714 end
3715 else
3716 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3717 end
3718
3719 # Super init call
3720 var res = v.compile_callsite(callsite, args)
3721 return res
3722 end
3723
3724 var mpropdef = self.mpropdef.as(not null)
3725
3726 var args
3727 if self.n_args.n_exprs.is_empty then
3728 args = frame.arguments
3729 else
3730 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3731 end
3732
3733 # Standard call-next-method
3734 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3735 end
3736 end
3737
3738 redef class ANewExpr
3739 redef fun expr(v)
3740 do
3741 var mtype = self.recvtype
3742 assert mtype != null
3743
3744 if mtype.mclass.name == "NativeArray" then
3745 assert self.n_args.n_exprs.length == 1
3746 var l = v.expr(self.n_args.n_exprs.first, null)
3747 assert mtype isa MGenericType
3748 var elttype = mtype.arguments.first
3749 return v.native_array_instance(elttype, l)
3750 end
3751
3752 var recv = v.init_instance_or_extern(mtype)
3753
3754 var callsite = self.callsite
3755 if callsite == null then return recv
3756 if callsite.is_broken then return null
3757
3758 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3759 var res2 = v.compile_callsite(callsite, args)
3760 if res2 != null then
3761 #self.debug("got {res2} from {mproperty}. drop {recv}")
3762 return res2
3763 end
3764 return recv
3765 end
3766 end
3767
3768 redef class AAttrExpr
3769 redef fun expr(v)
3770 do
3771 var recv = v.expr(self.n_expr, null)
3772 var mproperty = self.mproperty.as(not null)
3773 return v.read_attribute(mproperty, recv)
3774 end
3775 end
3776
3777 redef class AAttrAssignExpr
3778 redef fun expr(v)
3779 do
3780 var recv = v.expr(self.n_expr, null)
3781 var i = v.expr(self.n_value, null)
3782 var mproperty = self.mproperty.as(not null)
3783 v.write_attribute(mproperty, recv, i)
3784 return i
3785 end
3786 end
3787
3788 redef class AAttrReassignExpr
3789 redef fun stmt(v)
3790 do
3791 var recv = v.expr(self.n_expr, null)
3792 var value = v.expr(self.n_value, null)
3793 var mproperty = self.mproperty.as(not null)
3794 var attr = v.read_attribute(mproperty, recv)
3795 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3796 assert res != null
3797 v.write_attribute(mproperty, recv, res)
3798 end
3799 end
3800
3801 redef class AIssetAttrExpr
3802 redef fun expr(v)
3803 do
3804 var recv = v.expr(self.n_expr, null)
3805 var mproperty = self.mproperty.as(not null)
3806 return v.isset_attribute(mproperty, recv)
3807 end
3808 end
3809
3810 redef class AVarargExpr
3811 redef fun expr(v)
3812 do
3813 return v.expr(self.n_expr, null)
3814 end
3815 end
3816
3817 redef class ANamedargExpr
3818 redef fun expr(v)
3819 do
3820 return v.expr(self.n_expr, null)
3821 end
3822 end
3823
3824 redef class ADebugTypeExpr
3825 redef fun stmt(v)
3826 do
3827 # do nothing
3828 end
3829 end
3830
3831 # Utils
3832
3833 redef class Array[E]
3834 # Return a new `Array` with the elements only contened in self and not in `o`
3835 fun -(o: Array[E]): Array[E] do
3836 var res = new Array[E]
3837 for e in self do if not o.has(e) then res.add(e)
3838 return res
3839 end
3840 end
3841
3842 redef class MModule
3843 # All `MProperty` associated to all `MClassDef` of `mclass`
3844 fun properties(mclass: MClass): Set[MProperty] do
3845 if not self.properties_cache.has_key(mclass) then
3846 var properties = new HashSet[MProperty]
3847 var parents = new Array[MClass]
3848 if self.flatten_mclass_hierarchy.has(mclass) then
3849 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3850 end
3851 for parent in parents do
3852 properties.add_all(self.properties(parent))
3853 end
3854 for mclassdef in mclass.mclassdefs do
3855 if not self.in_importation <= mclassdef.mmodule then continue
3856 for mprop in mclassdef.intro_mproperties do
3857 properties.add(mprop)
3858 end
3859 end
3860 self.properties_cache[mclass] = properties
3861 end
3862 return properties_cache[mclass]
3863 end
3864 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3865
3866 # Write FFI and nitni results to file
3867 fun finalize_ffi(c: AbstractCompiler) do end
3868
3869 # Give requided addinional system libraries (as given to LD_LIBS)
3870 # Note: can return null instead of an empty set
3871 fun collect_linker_libs: nullable Array[String] do return null
3872 end
3873
3874 # Create a tool context to handle options and paths
3875 var toolcontext = new ToolContext
3876
3877 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3878
3879 # We do not add other options, so process them now!
3880 toolcontext.process_options(args)
3881
3882 # We need a model to collect stufs
3883 var model = new Model
3884 # An a model builder to parse files
3885 var modelbuilder = new ModelBuilder(model, toolcontext)
3886
3887 var arguments = toolcontext.option_context.rest
3888 if arguments.length > 1 and toolcontext.opt_output.value != null then
3889 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3890 exit 1
3891 end
3892
3893 # Here we load an process all modules passed on the command line
3894 var mmodules = modelbuilder.parse(arguments)
3895
3896 if mmodules.is_empty then
3897 toolcontext.check_errors
3898 toolcontext.errors_info
3899 if toolcontext.error_count > 0 then exit(1) else exit(0)
3900 end
3901
3902 modelbuilder.run_phases
3903
3904 for mmodule in mmodules do
3905 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3906 var ms = [mmodule]
3907 toolcontext.run_global_phases(ms)
3908 end