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