src: Added complete FlatString generation from compiler
[nit.git] / src / compiler / abstract_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract compiler
18 module abstract_compiler
19
20 import literal
21 import semantize
22 import platform
23 import c_tools
24 private import annotation
25 import mixin
26 import counter
27
28 # Add compiling options
29 redef class ToolContext
30 # --output
31 var opt_output = new OptionString("Output file", "-o", "--output")
32 # --dir
33 var opt_dir = new OptionString("Output directory", "--dir")
34 # --no-cc
35 var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
36 # --no-main
37 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
38 # --make-flags
39 var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
40 # --max-c-lines
41 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
42 # --group-c-files
43 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
44 # --compile-dir
45 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
46 # --hardening
47 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
48 # --no-check-covariance
49 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
50 # --no-check-attr-isset
51 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
52 # --no-check-assert
53 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
54 # --no-check-autocast
55 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
56 # --no-check-null
57 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
58 # --no-check-all
59 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
60 # --typing-test-metrics
61 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
62 # --invocation-metrics
63 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
64 # --isset-checks-metrics
65 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
66 # --no-stacktrace
67 var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
68 # --no-gcc-directives
69 var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
70 # --release
71 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
72 # -g
73 var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "--debug", "-g")
74
75 redef init
76 do
77 super
78 self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
79 self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
80 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
81 self.option_context.add_option(self.opt_no_stacktrace)
82 self.option_context.add_option(self.opt_no_gcc_directive)
83 self.option_context.add_option(self.opt_release)
84 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
85 self.option_context.add_option(self.opt_debug)
86
87 opt_no_main.hidden = true
88 end
89
90 redef fun process_options(args)
91 do
92 super
93
94 if opt_output.value != null and opt_dir.value != null then
95 print "Option Error: cannot use both --dir and --output"
96 exit(1)
97 end
98
99 if opt_no_check_all.value then
100 opt_no_check_covariance.value = true
101 opt_no_check_attr_isset.value = true
102 opt_no_check_assert.value = true
103 opt_no_check_autocast.value = true
104 opt_no_check_null.value = true
105 end
106 end
107 end
108
109 redef class ModelBuilder
110 # Simple indirection to `Toolchain::write_and_make`
111 protected fun write_and_make(compiler: AbstractCompiler)
112 do
113 var platform = compiler.target_platform
114 var toolchain = platform.toolchain(toolcontext, compiler)
115 compiler.toolchain = toolchain
116 toolchain.write_and_make
117 end
118 end
119
120 redef class Platform
121 # The specific tool-chain associated to the platform
122 fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
123 do
124 return new MakefileToolchain(toolcontext, compiler)
125 end
126 end
127
128 # Build toolchain for a specific target program, varies per `Platform`
129 class Toolchain
130
131 # Toolcontext
132 var toolcontext: ToolContext
133
134 # Compiler of the target program
135 var compiler: AbstractCompiler
136
137 # Directory where to generate all files
138 #
139 # The option `--compile_dir` change this directory.
140 fun root_compile_dir: String
141 do
142 var compile_dir = toolcontext.opt_compile_dir.value
143 if compile_dir == null then compile_dir = "nit_compile"
144 return compile_dir
145 end
146
147 # Directory where to generate all C files
148 #
149 # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
150 fun compile_dir: String do return root_compile_dir
151
152 # Write all C files and compile them
153 fun write_and_make is abstract
154 end
155
156 # Default toolchain using a Makefile
157 class MakefileToolchain
158 super Toolchain
159
160 redef fun write_and_make
161 do
162 var debug = toolcontext.opt_debug.value
163 var compile_dir = compile_dir
164
165 # Remove the compilation directory unless explicitly set
166 var auto_remove = toolcontext.opt_compile_dir.value == null
167 # If debug flag is set, do not remove sources
168 if debug then auto_remove = false
169
170 # Generate the .h and .c files
171 # A single C file regroups many compiled rumtime functions
172 # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
173 var time0 = get_time
174 self.toolcontext.info("*** WRITING C ***", 1)
175
176 root_compile_dir.mkdir
177 compile_dir.mkdir
178
179 var cfiles = new Array[String]
180 write_files(compile_dir, cfiles)
181
182 # Generate the Makefile
183
184 write_makefile(compile_dir, cfiles)
185
186 var time1 = get_time
187 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
188
189 # Execute the Makefile
190
191 if self.toolcontext.opt_no_cc.value then return
192
193 time0 = time1
194 self.toolcontext.info("*** COMPILING C ***", 1)
195
196 compile_c_code(compile_dir)
197
198 if auto_remove then
199 sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
200 end
201
202 time1 = get_time
203 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
204 end
205
206 # Write all source files to the `compile_dir`
207 fun write_files(compile_dir: String, cfiles: Array[String])
208 do
209 var platform = compiler.target_platform
210 if platform.supports_libunwind then compiler.build_c_to_nit_bindings
211 var cc_opt_with_libgc = "-DWITH_LIBGC"
212 if not platform.supports_libgc then cc_opt_with_libgc = ""
213
214 # Add gc_choser.h to aditionnal bodies
215 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
216 if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
217 compiler.extern_bodies.add(gc_chooser)
218 var clib = toolcontext.nit_dir / "clib"
219 compiler.files_to_copy.add "{clib}/gc_chooser.c"
220 compiler.files_to_copy.add "{clib}/gc_chooser.h"
221
222 # FFI
223 for m in compiler.mainmodule.in_importation.greaters do
224 compiler.finalize_ffi_for_module(m)
225 end
226
227 # Copy original .[ch] files to compile_dir
228 for src in compiler.files_to_copy do
229 var basename = src.basename
230 var dst = "{compile_dir}/{basename}"
231 src.file_copy_to dst
232 end
233
234 var hfilename = compiler.header.file.name + ".h"
235 var hfilepath = "{compile_dir}/{hfilename}"
236 var h = new FileWriter.open(hfilepath)
237 for l in compiler.header.decl_lines do
238 h.write l
239 h.write "\n"
240 end
241 for l in compiler.header.lines do
242 h.write l
243 h.write "\n"
244 end
245 h.close
246
247 var max_c_lines = toolcontext.opt_max_c_lines.value
248 for f in compiler.files do
249 var i = 0
250 var count = 0
251 var file: nullable FileWriter = null
252 for vis in f.writers do
253 if vis == compiler.header then continue
254 var total_lines = vis.lines.length + vis.decl_lines.length
255 if total_lines == 0 then continue
256 count += total_lines
257 if file == null or (count > max_c_lines and max_c_lines > 0) then
258 i += 1
259 if file != null then file.close
260 var cfilename = "{f.name}.{i}.c"
261 var cfilepath = "{compile_dir}/{cfilename}"
262 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
263 cfiles.add(cfilename)
264 file = new FileWriter.open(cfilepath)
265 file.write "#include \"{f.name}.0.h\"\n"
266 count = total_lines
267 end
268 for l in vis.decl_lines do
269 file.write l
270 file.write "\n"
271 end
272 for l in vis.lines do
273 file.write l
274 file.write "\n"
275 end
276 end
277 if file == null then continue
278 file.close
279
280 var cfilename = "{f.name}.0.h"
281 var cfilepath = "{compile_dir}/{cfilename}"
282 var hfile: nullable FileWriter = null
283 hfile = new FileWriter.open(cfilepath)
284 hfile.write "#include \"{hfilename}\"\n"
285 for key in f.required_declarations do
286 if not compiler.provided_declarations.has_key(key) then
287 var node = compiler.requirers_of_declarations.get_or_null(key)
288 if node != null then
289 node.debug "No provided declaration for {key}"
290 else
291 print "No provided declaration for {key}"
292 end
293 abort
294 end
295 hfile.write compiler.provided_declarations[key]
296 hfile.write "\n"
297 end
298 hfile.close
299 end
300
301 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
302 end
303
304 # Get the name of the Makefile to use
305 fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
306
307 # Get the default name of the executable to produce
308 fun default_outname: String
309 do
310 var mainmodule = compiler.mainmodule.first_real_mmodule
311 return mainmodule.name
312 end
313
314 # Combine options and platform informations to get the final path of the outfile
315 fun outfile(mainmodule: MModule): String
316 do
317 var res = self.toolcontext.opt_output.value
318 if res != null then return res
319 res = default_outname
320 var dir = self.toolcontext.opt_dir.value
321 if dir != null then return dir.join_path(res)
322 return res
323 end
324
325 # Write the Makefile
326 fun write_makefile(compile_dir: String, cfiles: Array[String])
327 do
328 var mainmodule = compiler.mainmodule
329 var platform = compiler.target_platform
330
331 var outname = outfile(mainmodule)
332
333 var real_outpath = compile_dir.relpath(outname)
334 var outpath = real_outpath.escape_to_mk
335 if outpath != real_outpath then
336 # If the name is crazy and need escaping, we will do an indirection
337 # 1. generate the binary in the nit_compile dir under an escaped name
338 # 2. copy the binary at the right place in the `all` goal.
339 outpath = mainmodule.c_name
340 end
341 var makename = makefile_name
342 var makepath = "{compile_dir}/{makename}"
343 var makefile = new FileWriter.open(makepath)
344
345 var linker_options = new HashSet[String]
346 for m in mainmodule.in_importation.greaters do
347 var libs = m.collect_linker_libs
348 if libs != null then linker_options.add_all(libs)
349 end
350 var debug = toolcontext.opt_debug.value
351
352 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
353
354 makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
355 if platform.supports_libunwind then
356 if toolcontext.opt_no_stacktrace.value then
357 makefile.write "NO_STACKTRACE=True"
358 else
359 makefile.write "NO_STACKTRACE= # Set to `True` to enable"
360 end
361 end
362
363 # Dynamic adaptations
364 # While `platform` enable complex toolchains, they are statically applied
365 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
366 makefile.write "\n\n"
367
368 # Check and adapt the targeted system
369 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
370
371 # Check and adapt for the compiler used
372 # clang need an additionnal `-Qunused-arguments`
373 makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
374
375 if platform.supports_libunwind then
376 makefile.write """
377 ifneq ($(NO_STACKTRACE), True)
378 # Check and include lib-unwind in a portable way
379 ifneq ($(uname_S),Darwin)
380 # already included on macosx, but need to get the correct flags in other supported platforms.
381 ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
382 LDLIBS += `pkg-config --libs libunwind`
383 CFLAGS += `pkg-config --cflags libunwind`
384 else
385 $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
386 CFLAGS += -D NO_STACKTRACE
387 endif
388 endif
389 else
390 # Stacktraces disabled
391 CFLAGS += -D NO_STACKTRACE
392 endif
393
394 """
395 else
396 makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
397 end
398
399 makefile.write """
400 # Special configuration for Darwin
401 ifeq ($(uname_S),Darwin)
402 # Remove POSIX flag -lrt
403 LDLIBS := $(filter-out -lrt,$(LDLIBS))
404 endif
405
406 """
407
408 makefile.write("all: {outpath}\n")
409 if outpath != real_outpath then
410 makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
411 end
412 makefile.write("\n")
413
414 var ofiles = new Array[String]
415 var dep_rules = new Array[String]
416 # Compile each generated file
417 for f in cfiles do
418 var o = f.strip_extension(".c") + ".o"
419 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
420 ofiles.add(o)
421 dep_rules.add(o)
422 end
423
424 # Generate linker script, if any
425 if not compiler.linker_script.is_empty then
426 var linker_script_path = "{compile_dir}/linker_script"
427 ofiles.add "linker_script"
428 var f = new FileWriter.open(linker_script_path)
429 for l in compiler.linker_script do
430 f.write l
431 f.write "\n"
432 end
433 f.close
434 end
435
436 var java_files = new Array[ExternFile]
437
438 var pkgconfigs = new Array[String]
439 for f in compiler.extern_bodies do
440 pkgconfigs.add_all f.pkgconfigs
441 end
442 # Protect pkg-config
443 if not pkgconfigs.is_empty then
444 makefile.write """
445 # does pkg-config exists?
446 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
447 $(error "Command `pkg-config` not found. Please install it")
448 endif
449 """
450 for p in pkgconfigs do
451 makefile.write """
452 # Check for library {{{p}}}
453 ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
454 $(error "pkg-config: package {{{p}}} is not found.")
455 endif
456 """
457 end
458 end
459
460 # Compile each required extern body into a specific .o
461 for f in compiler.extern_bodies do
462 var o = f.makefile_rule_name
463 var ff = f.filename.basename
464 makefile.write("{o}: {ff}\n")
465 makefile.write("\t{f.makefile_rule_content}\n\n")
466 dep_rules.add(f.makefile_rule_name)
467
468 if f.compiles_to_o_file then ofiles.add(o)
469 if f.add_to_jar then java_files.add(f)
470 end
471
472 if not java_files.is_empty then
473 var jar_file = "{outpath}.jar"
474
475 var class_files_array = new Array[String]
476 for f in java_files do class_files_array.add(f.makefile_rule_name)
477 var class_files = class_files_array.join(" ")
478
479 makefile.write("{jar_file}: {class_files}\n")
480 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
481 dep_rules.add jar_file
482 end
483
484 # Link edition
485 var pkg = ""
486 if not pkgconfigs.is_empty then
487 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
488 end
489 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
490 # Clean
491 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
492 if outpath != real_outpath then
493 makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
494 end
495 makefile.close
496 self.toolcontext.info("Generated makefile: {makepath}", 2)
497
498 makepath.file_copy_to "{compile_dir}/Makefile"
499 end
500
501 # The C code is generated, compile it to an executable
502 fun compile_c_code(compile_dir: String)
503 do
504 var makename = makefile_name
505
506 var makeflags = self.toolcontext.opt_make_flags.value
507 if makeflags == null then makeflags = ""
508
509 var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
510 self.toolcontext.info(command, 2)
511
512 var res
513 if self.toolcontext.verbose_level >= 3 then
514 res = sys.system("{command} 2>&1")
515 else
516 res = sys.system("{command} 2>&1 >/dev/null")
517 end
518 if res != 0 then
519 toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
520 end
521 end
522 end
523
524 # Singleton that store the knowledge about the compilation process
525 abstract class AbstractCompiler
526 type VISITOR: AbstractCompilerVisitor
527
528 # Table corresponding c_names to nit names (methods)
529 var names = new HashMap[String, String]
530
531 # The main module of the program currently compiled
532 # Is assigned during the separate compilation
533 var mainmodule: MModule is writable
534
535 # The real main module of the program
536 var realmainmodule: MModule is noinit
537
538 # The modelbuilder used to know the model and the AST
539 var modelbuilder: ModelBuilder is protected writable
540
541 # The associated toolchain
542 #
543 # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
544 var toolchain: Toolchain is noinit
545
546 # Is hardening asked? (see --hardening)
547 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
548
549 # The targeted specific platform
550 var target_platform: Platform is noinit
551
552 init
553 do
554 self.realmainmodule = mainmodule
555 target_platform = mainmodule.target_platform or else new Platform
556 end
557
558 # Do the full code generation of the program `mainmodule`
559 # It is the main method usually called after the instantiation
560 fun do_compilation is abstract
561
562 # Force the creation of a new file
563 # The point is to avoid contamination between must-be-compiled-separately files
564 fun new_file(name: String): CodeFile
565 do
566 if modelbuilder.toolcontext.opt_group_c_files.value then
567 if self.files.is_empty then
568 var f = new CodeFile(mainmodule.c_name)
569 self.files.add(f)
570 end
571 return self.files.first
572 end
573 var f = new CodeFile(name)
574 self.files.add(f)
575 return f
576 end
577
578 # The list of all associated files
579 # Used to generate .c files
580 var files = new List[CodeFile]
581
582 # Initialize a visitor specific for a compiler engine
583 fun new_visitor: VISITOR is abstract
584
585 # Where global declaration are stored (the main .h)
586 var header: CodeWriter is writable, noinit
587
588 # Additionnal linker script for `ld`.
589 # Mainly used to do specific link-time symbol resolution
590 var linker_script = new Array[String]
591
592 # Provide a declaration that can be requested (before or latter) by a visitor
593 fun provide_declaration(key: String, s: String)
594 do
595 if self.provided_declarations.has_key(key) then
596 assert self.provided_declarations[key] == s
597 end
598 self.provided_declarations[key] = s
599 end
600
601 private var provided_declarations = new HashMap[String, String]
602
603 private var requirers_of_declarations = new HashMap[String, ANode]
604
605 # Builds the .c and .h files to be used when generating a Stack Trace
606 # Binds the generated C function names to Nit function names
607 fun build_c_to_nit_bindings
608 do
609 var compile_dir = toolchain.compile_dir
610
611 var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
612 stream.write("#include <string.h>\n")
613 stream.write("#include <stdlib.h>\n")
614 stream.write("#include \"c_functions_hash.h\"\n")
615 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
616 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
617 stream.write("char* procname = malloc(len+1);")
618 stream.write("memcpy(procname, procproc, len);")
619 stream.write("procname[len] = '\\0';")
620 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
621 for i in names.keys do
622 stream.write("\{\"")
623 stream.write(i.escape_to_c)
624 stream.write("\",\"")
625 stream.write(names[i].escape_to_c)
626 stream.write("\"\},\n")
627 end
628 stream.write("\};\n")
629 stream.write("int i;")
630 stream.write("for(i = 0; i < {names.length}; i++)\{")
631 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
632 stream.write("free(procname);")
633 stream.write("return map[i].nit_name;")
634 stream.write("\}")
635 stream.write("\}")
636 stream.write("free(procname);")
637 stream.write("return NULL;")
638 stream.write("\}\n")
639 stream.close
640
641 stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
642 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
643 stream.close
644
645 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
646 end
647
648 # Compile C headers
649 # This method call compile_header_strucs method that has to be refined
650 fun compile_header do
651 self.header.add_decl("#include <stdlib.h>")
652 self.header.add_decl("#include <stdio.h>")
653 self.header.add_decl("#include <string.h>")
654 self.header.add_decl("#include <sys/types.h>\n")
655 self.header.add_decl("#include <unistd.h>\n")
656 self.header.add_decl("#include <stdint.h>\n")
657 self.header.add_decl("#include <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 var initializers = callsite.mpropdef.initializers
1151 if not initializers.is_empty then
1152 var recv = arguments.first
1153
1154 var i = 1
1155 for p in initializers do
1156 if p isa MMethod then
1157 var args = [recv]
1158 for x in p.intro.msignature.mparameters do
1159 args.add arguments[i]
1160 i += 1
1161 end
1162 self.send(p, args)
1163 else if p isa MAttribute then
1164 self.write_attribute(p, recv, arguments[i])
1165 i += 1
1166 else abort
1167 end
1168 assert i == arguments.length
1169
1170 return self.send(callsite.mproperty, [recv])
1171 end
1172
1173 return self.send(callsite.mproperty, arguments)
1174 end
1175
1176 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1177
1178 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1179
1180 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1181
1182 # Return an element of a native array.
1183 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1184 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1185
1186 # Store an element in a native array.
1187 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1188 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1189
1190 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1191 # This method is used to manage varargs in signatures and returns the real array
1192 # of runtime variables to use in the call.
1193 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1194 do
1195 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1196 var res = new Array[RuntimeVariable]
1197 res.add(recv)
1198
1199 if msignature.arity == 0 then return res
1200
1201 if map == null then
1202 assert args.length == msignature.arity
1203 for ne in args do
1204 res.add self.expr(ne, null)
1205 end
1206 return res
1207 end
1208
1209 # Eval in order of arguments, not parameters
1210 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1211 for ne in args do
1212 exprs.add self.expr(ne, null)
1213 end
1214
1215 # Fill `res` with the result of the evaluation according to the mapping
1216 for i in [0..msignature.arity[ do
1217 var param = msignature.mparameters[i]
1218 var j = map.map.get_or_null(i)
1219 if j == null then
1220 # default value
1221 res.add(null_instance)
1222 continue
1223 end
1224 if param.is_vararg and map.vararg_decl > 0 then
1225 var vararg = exprs.sub(j, map.vararg_decl)
1226 var elttype = param.mtype
1227 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1228 res.add(arg)
1229 continue
1230 end
1231 res.add exprs[j]
1232 end
1233 return res
1234 end
1235
1236 # Type handling
1237
1238 # Anchor a type to the main module and the current receiver
1239 fun anchor(mtype: MType): MType
1240 do
1241 if not mtype.need_anchor then return mtype
1242 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1243 end
1244
1245 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1246 do
1247 if not mtype.need_anchor then return mtype
1248 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1249 end
1250
1251 # Unsafely cast a value to a new type
1252 # ie the result share the same C variable but my have a different mcasttype
1253 # NOTE: if the adaptation is useless then `value` is returned as it.
1254 # ENSURE: `result.name == value.name`
1255 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1256 do
1257 mtype = self.anchor(mtype)
1258 var valmtype = value.mcasttype
1259 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1260 return value
1261 end
1262
1263 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1264 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1265 return res
1266 else
1267 var res = new RuntimeVariable(value.name, valmtype, mtype)
1268 return res
1269 end
1270 end
1271
1272 # Generate a super call from a method definition
1273 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1274
1275 # Adapt the arguments of a method according to targetted `MMethodDef`
1276 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1277
1278 # Unbox all the arguments of a method when implemented `extern` or `intern`
1279 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1280
1281 # Box or unbox a value to another type iff a C type conversion is needed
1282 # ENSURE: `result.mtype.ctype == mtype.ctype`
1283 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1284
1285 # Box extern classes to be used in the generated code
1286 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1287
1288 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1289 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1290
1291 # Generate a polymorphic subtype test
1292 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1293
1294 # Generate the code required to dynamically check if 2 objects share the same runtime type
1295 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1296
1297 # Generate a Nit "is" for two runtime_variables
1298 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1299
1300 # Sends
1301
1302 # Generate a static call on a method definition
1303 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1304
1305 # Generate a polymorphic send for the method `m` and the arguments `args`
1306 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1307
1308 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1309 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1310 do
1311 assert t isa MClassType
1312 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1313 return self.call(propdef, t, args)
1314 end
1315
1316 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1317 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1318 do
1319 assert t isa MClassType
1320 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1321 return self.call(m, t, args)
1322 end
1323
1324 # Attributes handling
1325
1326 # Generate a polymorphic attribute is_set test
1327 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1328
1329 # Generate a polymorphic attribute read
1330 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1331
1332 # Generate a polymorphic attribute write
1333 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1334
1335 # Checks
1336
1337 # Add a check and an abort for a null receiver if needed
1338 fun check_recv_notnull(recv: RuntimeVariable)
1339 do
1340 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1341
1342 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1343 if maybenull then
1344 self.add("if (unlikely({recv} == NULL)) \{")
1345 self.add_abort("Receiver is null")
1346 self.add("\}")
1347 end
1348 end
1349
1350 # Names handling
1351
1352 private var names = new HashSet[String]
1353 private var last: Int = 0
1354
1355 # Return a new name based on `s` and unique in the visitor
1356 fun get_name(s: String): String
1357 do
1358 if not self.names.has(s) then
1359 self.names.add(s)
1360 return s
1361 end
1362 var i = self.last + 1
1363 loop
1364 var s2 = s + i.to_s
1365 if not self.names.has(s2) then
1366 self.last = i
1367 self.names.add(s2)
1368 return s2
1369 end
1370 i = i + 1
1371 end
1372 end
1373
1374 # Return an unique and stable identifier associated with an escapemark
1375 fun escapemark_name(e: nullable EscapeMark): String
1376 do
1377 assert e != null
1378 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1379 var name = e.name
1380 if name == null then name = "label"
1381 name = get_name(name)
1382 frame.escapemark_names[e] = name
1383 return name
1384 end
1385
1386 # Insert a C label for associated with an escapemark
1387 fun add_escape_label(e: nullable EscapeMark)
1388 do
1389 if e == null then return
1390 if e.escapes.is_empty then return
1391 add("BREAK_{escapemark_name(e)}: (void)0;")
1392 end
1393
1394 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1395 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1396 fun class_name_string(value: RuntimeVariable): String is abstract
1397
1398 # Variables handling
1399
1400 protected var variables = new HashMap[Variable, RuntimeVariable]
1401
1402 # Return the local runtime_variable associated to a Nit local variable
1403 fun variable(variable: Variable): RuntimeVariable
1404 do
1405 if self.variables.has_key(variable) then
1406 return self.variables[variable]
1407 else
1408 var name = self.get_name("var_{variable.name}")
1409 var mtype = variable.declared_type.as(not null)
1410 mtype = self.anchor(mtype)
1411 var res = new RuntimeVariable(name, mtype, mtype)
1412 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1413 self.variables[variable] = res
1414 return res
1415 end
1416 end
1417
1418 # Return a new uninitialized local runtime_variable
1419 fun new_var(mtype: MType): RuntimeVariable
1420 do
1421 mtype = self.anchor(mtype)
1422 var name = self.get_name("var")
1423 var res = new RuntimeVariable(name, mtype, mtype)
1424 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1425 return res
1426 end
1427
1428 # The difference with `new_var` is the C static type of the local variable
1429 fun new_var_extern(mtype: MType): RuntimeVariable
1430 do
1431 mtype = self.anchor(mtype)
1432 var name = self.get_name("var")
1433 var res = new RuntimeVariable(name, mtype, mtype)
1434 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1435 return res
1436 end
1437
1438 # Return a new uninitialized named runtime_variable
1439 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1440 do
1441 mtype = self.anchor(mtype)
1442 var res = new RuntimeVariable(name, mtype, mtype)
1443 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1444 return res
1445 end
1446
1447 # Correctly assign a left and a right value
1448 # Boxing and unboxing is performed if required
1449 fun assign(left, right: RuntimeVariable)
1450 do
1451 right = self.autobox(right, left.mtype)
1452 self.add("{left} = {right};")
1453 end
1454
1455 # Generate instances
1456
1457 # Generate a alloc-instance + init-attributes
1458 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1459
1460 # Allocate and init attributes of an instance of a standard or extern class
1461 #
1462 # Does not support universals and the pseudo-internal `NativeArray` class.
1463 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1464 do
1465 var recv
1466 var ctype = mtype.ctype
1467 assert mtype.mclass.name != "NativeArray"
1468 if not mtype.is_c_primitive then
1469 recv = init_instance(mtype)
1470 else if ctype == "char*" then
1471 recv = new_expr("NULL/*special!*/", mtype)
1472 else
1473 recv = new_expr("({ctype})0/*special!*/", mtype)
1474 end
1475 return recv
1476 end
1477
1478 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1479 fun set_finalizer(recv: RuntimeVariable)
1480 do
1481 var mtype = recv.mtype
1482 var finalizable_type = compiler.mainmodule.finalizable_type
1483 if finalizable_type != null and not mtype.need_anchor and
1484 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1485 add "gc_register_finalizer({recv});"
1486 end
1487 end
1488
1489 # The currently processed module
1490 #
1491 # alias for `compiler.mainmodule`
1492 fun mmodule: MModule do return compiler.mainmodule
1493
1494 # Generate an integer value
1495 fun int_instance(value: Int): RuntimeVariable
1496 do
1497 var t = mmodule.int_type
1498 var res = new RuntimeVariable("{value.to_s}l", t, t)
1499 return res
1500 end
1501
1502 # Generate a byte value
1503 fun byte_instance(value: Byte): RuntimeVariable
1504 do
1505 var t = mmodule.byte_type
1506 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1507 return res
1508 end
1509
1510 # Generate an int8 value
1511 fun int8_instance(value: Int8): RuntimeVariable
1512 do
1513 var t = mmodule.int8_type
1514 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1515 return res
1516 end
1517
1518 # Generate an int16 value
1519 fun int16_instance(value: Int16): RuntimeVariable
1520 do
1521 var t = mmodule.int16_type
1522 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1523 return res
1524 end
1525
1526 # Generate a uint16 value
1527 fun uint16_instance(value: UInt16): RuntimeVariable
1528 do
1529 var t = mmodule.uint16_type
1530 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1531 return res
1532 end
1533
1534 # Generate an int32 value
1535 fun int32_instance(value: Int32): RuntimeVariable
1536 do
1537 var t = mmodule.int32_type
1538 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1539 return res
1540 end
1541
1542 # Generate a uint32 value
1543 fun uint32_instance(value: UInt32): RuntimeVariable
1544 do
1545 var t = mmodule.uint32_type
1546 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1547 return res
1548 end
1549
1550 # Generate a char value
1551 fun char_instance(value: Char): RuntimeVariable
1552 do
1553 var t = mmodule.char_type
1554
1555 if value.ascii < 128 then
1556 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1557 else
1558 return new RuntimeVariable("{value.ascii}", t, t)
1559 end
1560 end
1561
1562 # Generate a float value
1563 #
1564 # FIXME pass a Float, not a string
1565 fun float_instance(value: String): RuntimeVariable
1566 do
1567 var t = mmodule.float_type
1568 var res = new RuntimeVariable("{value}", t, t)
1569 return res
1570 end
1571
1572 # Generate an integer value
1573 fun bool_instance(value: Bool): RuntimeVariable
1574 do
1575 var s = if value then "1" else "0"
1576 var res = new RuntimeVariable(s, bool_type, bool_type)
1577 return res
1578 end
1579
1580 # Generate the `null` value
1581 fun null_instance: RuntimeVariable
1582 do
1583 var t = compiler.mainmodule.model.null_type
1584 var res = new RuntimeVariable("((val*)NULL)", t, t)
1585 return res
1586 end
1587
1588 # Generate a string value
1589 fun string_instance(string: String): RuntimeVariable
1590 do
1591 var mtype = mmodule.string_type
1592 var name = self.get_name("varonce")
1593 self.add_decl("static {mtype.ctype} {name};")
1594 var res = self.new_var(mtype)
1595 self.add("if (likely({name}!=NULL)) \{")
1596 self.add("{res} = {name};")
1597 self.add("\} else \{")
1598 var native_mtype = mmodule.native_string_type
1599 var nat = self.new_var(native_mtype)
1600 self.add("{nat} = \"{string.escape_to_c}\";")
1601 var bytelen = self.int_instance(string.bytelen)
1602 var unilen = self.int_instance(string.length)
1603 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1604 self.add("{name} = {res};")
1605 self.add("\}")
1606 return res
1607 end
1608
1609 fun value_instance(object: Object): RuntimeVariable
1610 do
1611 if object isa Int then
1612 return int_instance(object)
1613 else if object isa Bool then
1614 return bool_instance(object)
1615 else if object isa String then
1616 return string_instance(object)
1617 else
1618 abort
1619 end
1620 end
1621
1622 # Generate an array value
1623 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1624
1625 # Get an instance of a array for a vararg
1626 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1627
1628 # Code generation
1629
1630 # Add a line in the main part of the generated C
1631 fun add(s: String) do self.writer.lines.add(s)
1632
1633 # Add a line in the
1634 # (used for local or global declaration)
1635 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1636
1637 # Request the presence of a global declaration
1638 fun require_declaration(key: String)
1639 do
1640 var reqs = self.writer.file.required_declarations
1641 if reqs.has(key) then return
1642 reqs.add(key)
1643 var node = current_node
1644 if node != null then compiler.requirers_of_declarations[key] = node
1645 end
1646
1647 # Add a declaration in the local-header
1648 # The declaration is ensured to be present once
1649 fun declare_once(s: String)
1650 do
1651 self.compiler.provide_declaration(s, s)
1652 self.require_declaration(s)
1653 end
1654
1655 # Look for a needed .h and .c file for a given module
1656 # This is used for the legacy FFI
1657 fun add_extern(mmodule: MModule)
1658 do
1659 var file = mmodule.location.file.filename
1660 file = file.strip_extension(".nit")
1661 var tryfile = file + ".nit.h"
1662 if tryfile.file_exists then
1663 self.declare_once("#include \"{tryfile.basename}\"")
1664 self.compiler.files_to_copy.add(tryfile)
1665 end
1666 tryfile = file + "_nit.h"
1667 if tryfile.file_exists then
1668 self.declare_once("#include \"{tryfile.basename}\"")
1669 self.compiler.files_to_copy.add(tryfile)
1670 end
1671
1672 if self.compiler.seen_extern.has(file) then return
1673 self.compiler.seen_extern.add(file)
1674 tryfile = file + ".nit.c"
1675 if not tryfile.file_exists then
1676 tryfile = file + "_nit.c"
1677 if not tryfile.file_exists then return
1678 end
1679 var f = new ExternCFile(tryfile.basename, "")
1680 self.compiler.extern_bodies.add(f)
1681 self.compiler.files_to_copy.add(tryfile)
1682 end
1683
1684 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1685 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1686 do
1687 var res = new_var(mtype)
1688 self.add("{res} = {cexpr};")
1689 return res
1690 end
1691
1692 # Generate generic abort
1693 # used by aborts, asserts, casts, etc.
1694 fun add_abort(message: String)
1695 do
1696 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1697 add_raw_abort
1698 end
1699
1700 fun add_raw_abort
1701 do
1702 if self.current_node != null and self.current_node.location.file != null and
1703 self.current_node.location.file.mmodule != null then
1704 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1705 self.require_declaration(f)
1706 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1707 else
1708 self.add("PRINT_ERROR(\"\\n\");")
1709 end
1710 self.add("fatal_exit(1);")
1711 end
1712
1713 # Add a dynamic cast
1714 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1715 do
1716 var res = self.type_test(value, mtype, tag)
1717 self.add("if (unlikely(!{res})) \{")
1718 var cn = self.class_name_string(value)
1719 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1720 self.add_raw_abort
1721 self.add("\}")
1722 end
1723
1724 # Generate a return with the value `s`
1725 fun ret(s: RuntimeVariable)
1726 do
1727 self.assign(self.frame.returnvar.as(not null), s)
1728 self.add("goto {self.frame.returnlabel.as(not null)};")
1729 end
1730
1731 # Compile a statement (if any)
1732 fun stmt(nexpr: nullable AExpr)
1733 do
1734 if nexpr == null then return
1735 if nexpr.mtype == null and not nexpr.is_typed then
1736 # Untyped expression.
1737 # Might mean dead code or invalid code
1738 # so aborts
1739 add_abort("FATAL: bad statement executed.")
1740 return
1741 end
1742
1743 var narray = nexpr.comprehension
1744 if narray != null then
1745 var recv = frame.comprehension.as(not null)
1746 var val = expr(nexpr, narray.element_mtype)
1747 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1748 return
1749 end
1750
1751 var old = self.current_node
1752 self.current_node = nexpr
1753 nexpr.stmt(self)
1754 self.current_node = old
1755 end
1756
1757 # Compile an expression an return its result
1758 # `mtype` is the expected return type, pass null if no specific type is expected.
1759 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1760 do
1761 var old = self.current_node
1762 self.current_node = nexpr
1763
1764 var res = null
1765 if nexpr.mtype != null then
1766 res = nexpr.expr(self)
1767 end
1768
1769 if res == null then
1770 # Untyped expression.
1771 # Might mean dead code or invalid code.
1772 # so aborts
1773 add_abort("FATAL: bad expression executed.")
1774 # and return a placebo result to please the C compiler
1775 if mtype == null then mtype = compiler.mainmodule.object_type
1776 res = new_var(mtype)
1777
1778 self.current_node = old
1779 return res
1780 end
1781
1782 if mtype != null then
1783 mtype = self.anchor(mtype)
1784 res = self.autobox(res, mtype)
1785 end
1786 res = autoadapt(res, nexpr.mtype.as(not null))
1787 var implicit_cast_to = nexpr.implicit_cast_to
1788 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1789 add_cast(res, implicit_cast_to, "auto")
1790 res = autoadapt(res, implicit_cast_to)
1791 end
1792 self.current_node = old
1793 return res
1794 end
1795
1796 # Alias for `self.expr(nexpr, self.bool_type)`
1797 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1798
1799 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1800 fun debug(message: String)
1801 do
1802 var node = self.current_node
1803 if node == null then
1804 print "?: {message}"
1805 else
1806 node.debug(message)
1807 end
1808 self.add("/* DEBUG: {message} */")
1809 end
1810 end
1811
1812 # A C function associated to a Nit method
1813 # Because of customization, a given Nit method can be compiler more that once
1814 abstract class AbstractRuntimeFunction
1815
1816 type COMPILER: AbstractCompiler
1817 type VISITOR: AbstractCompilerVisitor
1818
1819 # The associated Nit method
1820 var mmethoddef: MMethodDef
1821
1822 # The mangled c name of the runtime_function
1823 # Subclasses should redefine `build_c_name` instead
1824 fun c_name: String
1825 do
1826 var res = self.c_name_cache
1827 if res != null then return res
1828 res = self.build_c_name
1829 self.c_name_cache = res
1830 return res
1831 end
1832
1833 # Non cached version of `c_name`
1834 protected fun build_c_name: String is abstract
1835
1836 protected var c_name_cache: nullable String = null is writable
1837
1838 # Implements a call of the runtime_function
1839 # May inline the body or generate a C function call
1840 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1841
1842 # Generate the code for the `AbstractRuntimeFunction`
1843 # Warning: compile more than once compilation makes CC unhappy
1844 fun compile_to_c(compiler: COMPILER) is abstract
1845 end
1846
1847 # A runtime variable hold a runtime value in C.
1848 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1849 #
1850 # 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.
1851 class RuntimeVariable
1852 # The name of the variable in the C code
1853 var name: String
1854
1855 # The static type of the variable (as declard in C)
1856 var mtype: MType
1857
1858 # The current casted type of the variable (as known in Nit)
1859 var mcasttype: MType is writable
1860
1861 # If the variable exaclty a mcasttype?
1862 # false (usual value) means that the variable is a mcasttype or a subtype.
1863 var is_exact: Bool = false is writable
1864
1865 init
1866 do
1867 assert not mtype.need_anchor
1868 assert not mcasttype.need_anchor
1869 end
1870
1871 redef fun to_s do return name
1872
1873 redef fun inspect
1874 do
1875 var exact_str
1876 if self.is_exact then
1877 exact_str = " exact"
1878 else
1879 exact_str = ""
1880 end
1881 var type_str
1882 if self.mtype == self.mcasttype then
1883 type_str = "{mtype}{exact_str}"
1884 else
1885 type_str = "{mtype}({mcasttype}{exact_str})"
1886 end
1887 return "<{name}:{type_str}>"
1888 end
1889 end
1890
1891 # The static context of a visited property in a `AbstractCompilerVisitor`
1892 class StaticFrame
1893
1894 type VISITOR: AbstractCompilerVisitor
1895
1896 # The associated visitor
1897 var visitor: VISITOR
1898
1899 # The executed property.
1900 # A Method in case of a call, an attribute in case of a default initialization.
1901 var mpropdef: MPropDef
1902
1903 # The static type of the receiver
1904 var receiver: MClassType
1905
1906 # Arguments of the method (the first is the receiver)
1907 var arguments: Array[RuntimeVariable]
1908
1909 # The runtime_variable associated to the return (in a function)
1910 var returnvar: nullable RuntimeVariable = null is writable
1911
1912 # The label at the end of the property
1913 var returnlabel: nullable String = null is writable
1914
1915 # Labels associated to a each escapemarks.
1916 # Because of inlinings, escape-marks must be associated to their context (the frame)
1917 private var escapemark_names = new HashMap[EscapeMark, String]
1918
1919 # The array comprehension currently filled, if any
1920 private var comprehension: nullable RuntimeVariable = null
1921 end
1922
1923 redef class MType
1924 # Return the C type associated to a given Nit static type
1925 fun ctype: String do return "val*"
1926
1927 # C type outside of the compiler code and in boxes
1928 fun ctype_extern: String do return "val*"
1929
1930 # Short name of the `ctype` to use in unions
1931 fun ctypename: String do return "val"
1932
1933 # Is the associated C type a primitive one?
1934 #
1935 # ENSURE `result == (ctype != "val*")`
1936 fun is_c_primitive: Bool do return false
1937 end
1938
1939 redef class MClassType
1940
1941 redef var ctype is lazy do
1942 if mclass.name == "Int" then
1943 return "long"
1944 else if mclass.name == "Bool" then
1945 return "short int"
1946 else if mclass.name == "Char" then
1947 return "uint32_t"
1948 else if mclass.name == "Float" then
1949 return "double"
1950 else if mclass.name == "Int8" then
1951 return "int8_t"
1952 else if mclass.name == "Byte" then
1953 return "unsigned char"
1954 else if mclass.name == "Int16" then
1955 return "int16_t"
1956 else if mclass.name == "UInt16" then
1957 return "uint16_t"
1958 else if mclass.name == "Int32" then
1959 return "int32_t"
1960 else if mclass.name == "UInt32" then
1961 return "uint32_t"
1962 else if mclass.name == "NativeString" then
1963 return "char*"
1964 else if mclass.name == "NativeArray" then
1965 return "val*"
1966 else
1967 return "val*"
1968 end
1969 end
1970
1971 redef var is_c_primitive is lazy do return ctype != "val*"
1972
1973 redef fun ctype_extern: String
1974 do
1975 if mclass.kind == extern_kind then
1976 return "void*"
1977 else
1978 return ctype
1979 end
1980 end
1981
1982 redef fun ctypename: String
1983 do
1984 if mclass.name == "Int" then
1985 return "l"
1986 else if mclass.name == "Bool" then
1987 return "s"
1988 else if mclass.name == "Char" then
1989 return "c"
1990 else if mclass.name == "Float" then
1991 return "d"
1992 else if mclass.name == "Int8" then
1993 return "i8"
1994 else if mclass.name == "Byte" then
1995 return "b"
1996 else if mclass.name == "Int16" then
1997 return "i16"
1998 else if mclass.name == "UInt16" then
1999 return "u16"
2000 else if mclass.name == "Int32" then
2001 return "i32"
2002 else if mclass.name == "UInt32" then
2003 return "u32"
2004 else if mclass.name == "NativeString" then
2005 return "str"
2006 else if mclass.name == "NativeArray" then
2007 #return "{self.arguments.first.ctype}*"
2008 return "val"
2009 else
2010 return "val"
2011 end
2012 end
2013 end
2014
2015 redef class MPropDef
2016 type VISITOR: AbstractCompilerVisitor
2017 end
2018
2019 redef class MMethodDef
2020 # Can the body be inlined?
2021 fun can_inline(v: VISITOR): Bool
2022 do
2023 if is_abstract then return true
2024 var modelbuilder = v.compiler.modelbuilder
2025 var node = modelbuilder.mpropdef2node(self)
2026 if node isa APropdef then
2027 return node.can_inline
2028 else if node isa AClassdef then
2029 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2030 return true
2031 else
2032 abort
2033 end
2034 end
2035
2036 # Inline the body in another visitor
2037 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2038 do
2039 var modelbuilder = v.compiler.modelbuilder
2040 var val = constant_value
2041 var node = modelbuilder.mpropdef2node(self)
2042
2043 if is_abstract then
2044 var cn = v.class_name_string(arguments.first)
2045 v.current_node = node
2046 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2047 v.add_raw_abort
2048 return null
2049 end
2050
2051 if node isa APropdef then
2052 var oldnode = v.current_node
2053 v.current_node = node
2054 self.compile_parameter_check(v, arguments)
2055 node.compile_to_c(v, self, arguments)
2056 v.current_node = oldnode
2057 else if node isa AClassdef then
2058 var oldnode = v.current_node
2059 v.current_node = node
2060 self.compile_parameter_check(v, arguments)
2061 node.compile_to_c(v, self, arguments)
2062 v.current_node = oldnode
2063 else if val != null then
2064 v.ret(v.value_instance(val))
2065 else
2066 abort
2067 end
2068 return null
2069 end
2070
2071 # Generate type checks in the C code to check covariant parameters
2072 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2073 do
2074 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2075
2076 for i in [0..msignature.arity[ do
2077 # skip test for vararg since the array is instantiated with the correct polymorphic type
2078 if msignature.vararg_rank == i then continue
2079
2080 # skip if the cast is not required
2081 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2082 if not origmtype.need_anchor then continue
2083
2084 # get the parameter type
2085 var mtype = self.msignature.mparameters[i].mtype
2086
2087 # generate the cast
2088 # note that v decides if and how to implements the cast
2089 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
2090 v.add_cast(arguments[i+1], mtype, "covariance")
2091 end
2092 end
2093 end
2094
2095 # Node visit
2096
2097 redef class APropdef
2098 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2099 do
2100 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2101 debug("Not yet implemented")
2102 end
2103
2104 fun can_inline: Bool do return true
2105 end
2106
2107 redef class AMethPropdef
2108 redef fun compile_to_c(v, mpropdef, arguments)
2109 do
2110 # Call the implicit super-init
2111 var auto_super_inits = self.auto_super_inits
2112 if auto_super_inits != null then
2113 var args = [arguments.first]
2114 for auto_super_init in auto_super_inits do
2115 assert auto_super_init.mproperty != mpropdef.mproperty
2116 args.clear
2117 for i in [0..auto_super_init.msignature.arity+1[ do
2118 args.add(arguments[i])
2119 end
2120 assert auto_super_init.mproperty != mpropdef.mproperty
2121 v.compile_callsite(auto_super_init, args)
2122 end
2123 end
2124 if auto_super_call then
2125 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2126 end
2127
2128 # Try special compilation
2129 if mpropdef.is_intern then
2130 if compile_intern_to_c(v, mpropdef, arguments) then return
2131 else if mpropdef.is_extern then
2132 if mpropdef.mproperty.is_init then
2133 if compile_externinit_to_c(v, mpropdef, arguments) then return
2134 else
2135 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2136 end
2137 end
2138
2139 # Compile block if any
2140 var n_block = n_block
2141 if n_block != null then
2142 for i in [0..mpropdef.msignature.arity[ do
2143 var variable = self.n_signature.n_params[i].variable.as(not null)
2144 v.assign(v.variable(variable), arguments[i+1])
2145 end
2146 v.stmt(n_block)
2147 return
2148 end
2149
2150 # We have a problem
2151 var cn = v.class_name_string(arguments.first)
2152 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2153 v.add_raw_abort
2154 end
2155
2156 redef fun can_inline
2157 do
2158 if self.auto_super_inits != null then return false
2159 var nblock = self.n_block
2160 if nblock == null then return true
2161 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2162 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2163 return false
2164 end
2165
2166 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2167 do
2168 var pname = mpropdef.mproperty.name
2169 var cname = mpropdef.mclassdef.mclass.name
2170 var ret = mpropdef.msignature.return_mtype
2171 if ret != null then
2172 ret = v.resolve_for(ret, arguments.first)
2173 end
2174 if pname != "==" and pname != "!=" then
2175 v.adapt_signature(mpropdef, arguments)
2176 v.unbox_signature_extern(mpropdef, arguments)
2177 end
2178 if cname == "Int" then
2179 if pname == "output" then
2180 v.add("printf(\"%ld\\n\", {arguments.first});")
2181 return true
2182 else if pname == "object_id" then
2183 v.ret(arguments.first)
2184 return true
2185 else if pname == "+" then
2186 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2187 return true
2188 else if pname == "-" then
2189 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2190 return true
2191 else if pname == "unary -" then
2192 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2193 return true
2194 else if pname == "unary +" then
2195 v.ret(arguments[0])
2196 return true
2197 else if pname == "*" then
2198 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2199 return true
2200 else if pname == "/" then
2201 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2202 return true
2203 else if pname == "%" then
2204 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2205 return true
2206 else if pname == "==" then
2207 v.ret(v.equal_test(arguments[0], arguments[1]))
2208 return true
2209 else if pname == "!=" then
2210 var res = v.equal_test(arguments[0], arguments[1])
2211 v.ret(v.new_expr("!{res}", ret.as(not null)))
2212 return true
2213 else if pname == "<" then
2214 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2215 return true
2216 else if pname == ">" then
2217 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2218 return true
2219 else if pname == "<=" then
2220 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2221 return true
2222 else if pname == ">=" then
2223 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2224 return true
2225 else if pname == "to_i8" then
2226 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2227 return true
2228 else if pname == "to_i16" then
2229 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2230 return true
2231 else if pname == "to_u16" then
2232 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2233 return true
2234 else if pname == "to_i32" then
2235 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2236 return true
2237 else if pname == "to_u32" then
2238 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2239 return true
2240 else if pname == "to_f" then
2241 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2242 return true
2243 else if pname == "to_b" then
2244 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2245 return true
2246 else if pname == "ascii" then
2247 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2248 return true
2249 end
2250 else if cname == "Char" then
2251 if pname == "object_id" then
2252 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2253 return true
2254 else if pname == "successor" then
2255 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2256 return true
2257 else if pname == "predecessor" then
2258 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2259 return true
2260 else if pname == "==" then
2261 v.ret(v.equal_test(arguments[0], arguments[1]))
2262 return true
2263 else if pname == "!=" then
2264 var res = v.equal_test(arguments[0], arguments[1])
2265 v.ret(v.new_expr("!{res}", ret.as(not null)))
2266 return true
2267 else if pname == "<" then
2268 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2269 return true
2270 else if pname == ">" then
2271 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2272 return true
2273 else if pname == "<=" then
2274 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2275 return true
2276 else if pname == ">=" then
2277 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2278 return true
2279 else if pname == "to_i" then
2280 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2281 return true
2282 else if pname == "ascii" then
2283 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2284 return true
2285 end
2286 else if cname == "Byte" then
2287 if pname == "output" then
2288 v.add("printf(\"%x\\n\", {arguments.first});")
2289 return true
2290 else if pname == "object_id" then
2291 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2292 return true
2293 else if pname == "+" then
2294 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2295 return true
2296 else if pname == "-" then
2297 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2298 return true
2299 else if pname == "unary -" then
2300 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2301 return true
2302 else if pname == "unary +" then
2303 v.ret(arguments[0])
2304 return true
2305 else if pname == "*" then
2306 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2307 return true
2308 else if pname == "/" then
2309 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2310 return true
2311 else if pname == "%" then
2312 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2313 return true
2314 else if pname == "==" then
2315 v.ret(v.equal_test(arguments[0], arguments[1]))
2316 return true
2317 else if pname == "!=" then
2318 var res = v.equal_test(arguments[0], arguments[1])
2319 v.ret(v.new_expr("!{res}", ret.as(not null)))
2320 return true
2321 else if pname == "<" then
2322 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2323 return true
2324 else if pname == ">" then
2325 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2326 return true
2327 else if pname == "<=" then
2328 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2329 return true
2330 else if pname == ">=" then
2331 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2332 return true
2333 else if pname == "to_i" then
2334 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2335 return true
2336 else if pname == "to_f" then
2337 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2338 return true
2339 else if pname == "to_i8" then
2340 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2341 return true
2342 else if pname == "to_i16" then
2343 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2344 return true
2345 else if pname == "to_u16" then
2346 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2347 return true
2348 else if pname == "to_i32" then
2349 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2350 return true
2351 else if pname == "to_u32" then
2352 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2353 return true
2354 else if pname == "ascii" then
2355 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2356 return true
2357 end
2358 else if cname == "Bool" then
2359 if pname == "output" then
2360 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2361 return true
2362 else if pname == "object_id" then
2363 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2364 return true
2365 else if pname == "==" then
2366 v.ret(v.equal_test(arguments[0], arguments[1]))
2367 return true
2368 else if pname == "!=" then
2369 var res = v.equal_test(arguments[0], arguments[1])
2370 v.ret(v.new_expr("!{res}", ret.as(not null)))
2371 return true
2372 end
2373 else if cname == "Float" then
2374 if pname == "output" then
2375 v.add("printf(\"%f\\n\", {arguments.first});")
2376 return true
2377 else if pname == "object_id" then
2378 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2379 return true
2380 else if pname == "+" then
2381 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2382 return true
2383 else if pname == "-" then
2384 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2385 return true
2386 else if pname == "unary -" then
2387 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2388 return true
2389 else if pname == "unary +" then
2390 v.ret(arguments[0])
2391 return true
2392 else if pname == "succ" then
2393 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2394 return true
2395 else if pname == "prec" then
2396 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2397 return true
2398 else if pname == "*" then
2399 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2400 return true
2401 else if pname == "/" then
2402 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2403 return true
2404 else if pname == "==" then
2405 v.ret(v.equal_test(arguments[0], arguments[1]))
2406 return true
2407 else if pname == "!=" then
2408 var res = v.equal_test(arguments[0], arguments[1])
2409 v.ret(v.new_expr("!{res}", ret.as(not null)))
2410 return true
2411 else if pname == "<" then
2412 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2413 return true
2414 else if pname == ">" then
2415 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2416 return true
2417 else if pname == "<=" then
2418 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2419 return true
2420 else if pname == ">=" then
2421 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2422 return true
2423 else if pname == "to_i" then
2424 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2425 return true
2426 else if pname == "to_b" then
2427 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2428 return true
2429 else if pname == "to_i8" then
2430 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2431 return true
2432 else if pname == "to_i16" then
2433 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2434 return true
2435 else if pname == "to_u16" then
2436 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2437 return true
2438 else if pname == "to_i32" then
2439 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2440 return true
2441 else if pname == "to_u32" then
2442 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2443 return true
2444 end
2445 else if cname == "NativeString" then
2446 if pname == "[]" then
2447 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2448 return true
2449 else if pname == "[]=" then
2450 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2451 return true
2452 else if pname == "copy_to" then
2453 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2454 return true
2455 else if pname == "atoi" then
2456 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2457 return true
2458 else if pname == "fast_cstring" then
2459 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2460 return true
2461 else if pname == "new" then
2462 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2463 return true
2464 end
2465 else if cname == "NativeArray" then
2466 v.native_array_def(pname, ret, arguments)
2467 return true
2468 else if cname == "Int8" then
2469 if pname == "output" then
2470 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2471 return true
2472 else if pname == "object_id" then
2473 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2474 return true
2475 else if pname == "+" then
2476 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2477 return true
2478 else if pname == "-" then
2479 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2480 return true
2481 else if pname == "unary -" then
2482 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2483 return true
2484 else if pname == "unary +" then
2485 v.ret(arguments[0])
2486 return true
2487 else if pname == "*" then
2488 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2489 return true
2490 else if pname == "/" then
2491 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2492 return true
2493 else if pname == "%" then
2494 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2495 return true
2496 else if pname == "<<" then
2497 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2498 return true
2499 else if pname == ">>" then
2500 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2501 return true
2502 else if pname == "==" then
2503 v.ret(v.equal_test(arguments[0], arguments[1]))
2504 return true
2505 else if pname == "!=" then
2506 var res = v.equal_test(arguments[0], arguments[1])
2507 v.ret(v.new_expr("!{res}", ret.as(not null)))
2508 return true
2509 else if pname == "<" then
2510 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2511 return true
2512 else if pname == ">" then
2513 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2514 return true
2515 else if pname == "<=" then
2516 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2517 return true
2518 else if pname == ">=" then
2519 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2520 return true
2521 else if pname == "to_i" then
2522 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2523 return true
2524 else if pname == "to_b" then
2525 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2526 return true
2527 else if pname == "to_i16" then
2528 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2529 return true
2530 else if pname == "to_u16" then
2531 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2532 return true
2533 else if pname == "to_i32" then
2534 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2535 return true
2536 else if pname == "to_u32" then
2537 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2538 return true
2539 else if pname == "to_f" then
2540 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2541 return true
2542 else if pname == "ascii" then
2543 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2544 return true
2545 else if pname == "&" then
2546 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2547 return true
2548 else if pname == "|" then
2549 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2550 return true
2551 else if pname == "^" then
2552 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2553 return true
2554 else if pname == "unary ~" then
2555 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2556 return true
2557 end
2558 else if cname == "Int16" then
2559 if pname == "output" then
2560 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2561 return true
2562 else if pname == "object_id" then
2563 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2564 return true
2565 else if pname == "+" then
2566 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2567 return true
2568 else if pname == "-" then
2569 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2570 return true
2571 else if pname == "unary -" then
2572 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2573 return true
2574 else if pname == "unary +" then
2575 v.ret(arguments[0])
2576 return true
2577 else if pname == "*" then
2578 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2579 return true
2580 else if pname == "/" then
2581 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2582 return true
2583 else if pname == "%" then
2584 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2585 return true
2586 else if pname == "<<" then
2587 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2588 return true
2589 else if pname == ">>" then
2590 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2591 return true
2592 else if pname == "==" then
2593 v.ret(v.equal_test(arguments[0], arguments[1]))
2594 return true
2595 else if pname == "!=" then
2596 var res = v.equal_test(arguments[0], arguments[1])
2597 v.ret(v.new_expr("!{res}", ret.as(not null)))
2598 return true
2599 else if pname == "<" then
2600 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2601 return true
2602 else if pname == ">" then
2603 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2604 return true
2605 else if pname == "<=" then
2606 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2607 return true
2608 else if pname == ">=" then
2609 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2610 return true
2611 else if pname == "to_i" then
2612 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2613 return true
2614 else if pname == "to_b" then
2615 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2616 return true
2617 else if pname == "to_i8" then
2618 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2619 return true
2620 else if pname == "to_u16" then
2621 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2622 return true
2623 else if pname == "to_i32" then
2624 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2625 return true
2626 else if pname == "to_u32" then
2627 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2628 return true
2629 else if pname == "to_f" then
2630 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2631 return true
2632 else if pname == "&" then
2633 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2634 return true
2635 else if pname == "|" then
2636 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2637 return true
2638 else if pname == "^" then
2639 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2640 return true
2641 else if pname == "unary ~" then
2642 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2643 return true
2644 else if pname == "ascii" then
2645 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2646 return true
2647 end
2648 else if cname == "UInt16" then
2649 if pname == "output" then
2650 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2651 return true
2652 else if pname == "object_id" then
2653 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2654 return true
2655 else if pname == "+" then
2656 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2657 return true
2658 else if pname == "-" then
2659 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2660 return true
2661 else if pname == "unary -" then
2662 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2663 return true
2664 else if pname == "unary +" then
2665 v.ret(arguments[0])
2666 return true
2667 else if pname == "*" then
2668 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2669 return true
2670 else if pname == "/" then
2671 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2672 return true
2673 else if pname == "%" then
2674 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2675 return true
2676 else if pname == "<<" then
2677 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2678 return true
2679 else if pname == ">>" then
2680 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2681 return true
2682 else if pname == "==" then
2683 v.ret(v.equal_test(arguments[0], arguments[1]))
2684 return true
2685 else if pname == "!=" then
2686 var res = v.equal_test(arguments[0], arguments[1])
2687 v.ret(v.new_expr("!{res}", ret.as(not null)))
2688 return true
2689 else if pname == "<" then
2690 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2691 return true
2692 else if pname == ">" then
2693 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2694 return true
2695 else if pname == "<=" then
2696 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2697 return true
2698 else if pname == ">=" then
2699 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2700 return true
2701 else if pname == "to_i" then
2702 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2703 return true
2704 else if pname == "to_b" then
2705 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2706 return true
2707 else if pname == "to_i8" then
2708 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2709 return true
2710 else if pname == "to_i16" then
2711 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2712 return true
2713 else if pname == "to_i32" then
2714 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2715 return true
2716 else if pname == "to_u32" then
2717 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2718 return true
2719 else if pname == "to_f" then
2720 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2721 return true
2722 else if pname == "&" then
2723 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2724 return true
2725 else if pname == "|" then
2726 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", 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 == "unary ~" then
2732 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2733 return true
2734 else if pname == "ascii" then
2735 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2736 return true
2737 end
2738 else if cname == "Int32" then
2739 if pname == "output" then
2740 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2741 return true
2742 else if pname == "object_id" then
2743 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2744 return true
2745 else if pname == "+" then
2746 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2747 return true
2748 else if pname == "-" then
2749 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2750 return true
2751 else if pname == "unary -" then
2752 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2753 return true
2754 else if pname == "unary +" then
2755 v.ret(arguments[0])
2756 return true
2757 else if pname == "*" then
2758 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2759 return true
2760 else if pname == "/" then
2761 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2762 return true
2763 else if pname == "%" then
2764 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2765 return true
2766 else if pname == "<<" then
2767 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2768 return true
2769 else if pname == ">>" then
2770 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2771 return true
2772 else if pname == "==" then
2773 v.ret(v.equal_test(arguments[0], arguments[1]))
2774 return true
2775 else if pname == "!=" then
2776 var res = v.equal_test(arguments[0], arguments[1])
2777 v.ret(v.new_expr("!{res}", ret.as(not null)))
2778 return true
2779 else if pname == "<" then
2780 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2781 return true
2782 else if pname == ">" then
2783 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2784 return true
2785 else if pname == "<=" then
2786 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2787 return true
2788 else if pname == ">=" then
2789 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2790 return true
2791 else if pname == "to_i" then
2792 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2793 return true
2794 else if pname == "to_b" then
2795 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2796 return true
2797 else if pname == "to_i8" then
2798 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2799 return true
2800 else if pname == "to_i16" then
2801 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2802 return true
2803 else if pname == "to_u16" then
2804 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2805 return true
2806 else if pname == "to_u32" then
2807 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2808 return true
2809 else if pname == "to_f" then
2810 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2811 return true
2812 else if pname == "&" then
2813 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", 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 == "ascii" then
2825 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2826 return true
2827 end
2828 else if cname == "UInt32" then
2829 if pname == "output" then
2830 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2831 return true
2832 else if pname == "object_id" then
2833 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2834 return true
2835 else if pname == "+" then
2836 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2837 return true
2838 else if pname == "-" then
2839 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2840 return true
2841 else if pname == "unary -" then
2842 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2843 return true
2844 else if pname == "unary +" then
2845 v.ret(arguments[0])
2846 return true
2847 else if pname == "*" then
2848 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2849 return true
2850 else if pname == "/" then
2851 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2852 return true
2853 else if pname == "%" then
2854 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2855 return true
2856 else if pname == "<<" then
2857 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2858 return true
2859 else if pname == ">>" then
2860 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2861 return true
2862 else if pname == "==" then
2863 v.ret(v.equal_test(arguments[0], arguments[1]))
2864 return true
2865 else if pname == "!=" then
2866 var res = v.equal_test(arguments[0], arguments[1])
2867 v.ret(v.new_expr("!{res}", ret.as(not null)))
2868 return true
2869 else if pname == "<" then
2870 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2871 return true
2872 else if pname == ">" then
2873 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2874 return true
2875 else if pname == "<=" then
2876 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2877 return true
2878 else if pname == ">=" then
2879 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2880 return true
2881 else if pname == "to_i" then
2882 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2883 return true
2884 else if pname == "to_b" then
2885 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2886 return true
2887 else if pname == "to_i8" then
2888 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2889 return true
2890 else if pname == "to_i16" then
2891 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2892 return true
2893 else if pname == "to_u16" then
2894 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2895 return true
2896 else if pname == "to_i32" then
2897 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2898 return true
2899 else if pname == "to_f" then
2900 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2901 return true
2902 else if pname == "&" then
2903 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2904 return true
2905 else if pname == "|" then
2906 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2907 return true
2908 else if pname == "^" then
2909 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2910 return true
2911 else if pname == "unary ~" then
2912 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2913 return true
2914 else if pname == "ascii" then
2915 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2916 return true
2917 end
2918 end
2919 if pname == "exit" then
2920 v.add("exit({arguments[1]});")
2921 return true
2922 else if pname == "sys" then
2923 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2924 return true
2925 else if pname == "calloc_string" then
2926 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2927 return true
2928 else if pname == "calloc_array" then
2929 v.calloc_array(ret.as(not null), arguments)
2930 return true
2931 else if pname == "object_id" then
2932 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2933 return true
2934 else if pname == "is_same_type" then
2935 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2936 return true
2937 else if pname == "is_same_instance" then
2938 v.ret(v.equal_test(arguments[0], arguments[1]))
2939 return true
2940 else if pname == "output_class_name" then
2941 var nat = v.class_name_string(arguments.first)
2942 v.add("printf(\"%s\\n\", {nat});")
2943 return true
2944 else if pname == "native_class_name" then
2945 var nat = v.class_name_string(arguments.first)
2946 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2947 return true
2948 else if pname == "force_garbage_collection" then
2949 v.add("nit_gcollect();")
2950 return true
2951 else if pname == "native_argc" then
2952 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2953 return true
2954 else if pname == "native_argv" then
2955 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2956 return true
2957 end
2958 return false
2959 end
2960
2961 # Compile an extern method
2962 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2963 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2964 do
2965 var externname
2966 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2967 if at != null and at.n_args.length == 1 then
2968 externname = at.arg_as_string(v.compiler.modelbuilder)
2969 if externname == null then return false
2970 else
2971 return false
2972 end
2973 v.add_extern(mpropdef.mclassdef.mmodule)
2974 var res: nullable RuntimeVariable = null
2975 var ret = mpropdef.msignature.return_mtype
2976 if ret != null then
2977 ret = v.resolve_for(ret, arguments.first)
2978 res = v.new_var_extern(ret)
2979 end
2980 v.adapt_signature(mpropdef, arguments)
2981 v.unbox_signature_extern(mpropdef, arguments)
2982
2983 if res == null then
2984 v.add("{externname}({arguments.join(", ")});")
2985 else
2986 v.add("{res} = {externname}({arguments.join(", ")});")
2987 res = v.box_extern(res, ret.as(not null))
2988 v.ret(res)
2989 end
2990 return true
2991 end
2992
2993 # Compile an extern factory
2994 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2995 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2996 do
2997 var externname
2998 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2999 if at != null then
3000 externname = at.arg_as_string(v.compiler.modelbuilder)
3001 if externname == null then return false
3002 else
3003 return false
3004 end
3005 v.add_extern(mpropdef.mclassdef.mmodule)
3006 v.adapt_signature(mpropdef, arguments)
3007 v.unbox_signature_extern(mpropdef, arguments)
3008 var ret = arguments.first.mtype
3009 var res = v.new_var_extern(ret)
3010
3011 arguments.shift
3012
3013 v.add("{res} = {externname}({arguments.join(", ")});")
3014 res = v.box_extern(res, ret)
3015 v.ret(res)
3016 return true
3017 end
3018 end
3019
3020 redef class AAttrPropdef
3021 redef fun can_inline: Bool do return not is_lazy
3022
3023 redef fun compile_to_c(v, mpropdef, arguments)
3024 do
3025 if mpropdef == mreadpropdef then
3026 assert arguments.length == 1
3027 var recv = arguments.first
3028 var res
3029 if is_lazy then
3030 var set
3031 var ret = self.mtype
3032 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3033 var guard = self.mlazypropdef.mproperty
3034 if useiset then
3035 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3036 else
3037 set = v.read_attribute(guard, recv)
3038 end
3039 v.add("if(likely({set})) \{")
3040 res = v.read_attribute(self.mpropdef.mproperty, recv)
3041 v.add("\} else \{")
3042
3043 var value = evaluate_expr(v, recv)
3044
3045 v.assign(res, value)
3046 if not useiset then
3047 var true_v = v.bool_instance(true)
3048 v.write_attribute(guard, arguments.first, true_v)
3049 end
3050 v.add("\}")
3051 else
3052 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3053 end
3054 v.assign(v.frame.returnvar.as(not null), res)
3055 else if mpropdef == mwritepropdef then
3056 assert arguments.length == 2
3057 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
3058 if is_lazy then
3059 var ret = self.mtype
3060 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3061 if not useiset then
3062 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3063 end
3064 end
3065 else
3066 abort
3067 end
3068 end
3069
3070 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3071 do
3072 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3073 end
3074
3075 # Evaluate, store and return the default value of the attribute
3076 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3077 do
3078 var oldnode = v.current_node
3079 v.current_node = self
3080 var old_frame = v.frame
3081 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3082 v.frame = frame
3083
3084 var value
3085 var mtype = self.mtype
3086 assert mtype != null
3087
3088 var nexpr = self.n_expr
3089 var nblock = self.n_block
3090 if nexpr != null then
3091 value = v.expr(nexpr, mtype)
3092 else if nblock != null then
3093 value = v.new_var(mtype)
3094 frame.returnvar = value
3095 frame.returnlabel = v.get_name("RET_LABEL")
3096 v.add("\{")
3097 v.stmt(nblock)
3098 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3099 v.add("\}")
3100 else
3101 abort
3102 end
3103
3104 v.write_attribute(self.mpropdef.mproperty, recv, value)
3105
3106 v.frame = old_frame
3107 v.current_node = oldnode
3108
3109 return value
3110 end
3111
3112 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3113 do
3114 var nexpr = self.n_expr
3115 if nexpr != null then return
3116
3117 var oldnode = v.current_node
3118 v.current_node = self
3119 var old_frame = v.frame
3120 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3121 v.frame = frame
3122 # Force read to check the initialization
3123 v.read_attribute(self.mpropdef.mproperty, recv)
3124 v.frame = old_frame
3125 v.current_node = oldnode
3126 end
3127 end
3128
3129 redef class AClassdef
3130 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3131 do
3132 if mpropdef == self.mfree_init then
3133 assert mpropdef.mproperty.is_root_init
3134 assert arguments.length == 1
3135 if not mpropdef.is_intro then
3136 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3137 end
3138 return
3139 else
3140 abort
3141 end
3142 end
3143 end
3144
3145 redef class AExpr
3146 # Try to compile self as an expression
3147 # Do not call this method directly, use `v.expr` instead
3148 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3149 do
3150 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3151 var mtype = self.mtype
3152 if mtype == null then
3153 return null
3154 else
3155 var res = v.new_var(mtype)
3156 v.add("/* {res} = NOT YET {class_name} */")
3157 return res
3158 end
3159 end
3160
3161 # Try to compile self as a statement
3162 # Do not call this method directly, use `v.stmt` instead
3163 private fun stmt(v: AbstractCompilerVisitor)
3164 do
3165 expr(v)
3166 end
3167 end
3168
3169 redef class ABlockExpr
3170 redef fun stmt(v)
3171 do
3172 for e in self.n_expr do v.stmt(e)
3173 end
3174 redef fun expr(v)
3175 do
3176 var last = self.n_expr.last
3177 for e in self.n_expr do
3178 if e == last then break
3179 v.stmt(e)
3180 end
3181 return v.expr(last, null)
3182 end
3183 end
3184
3185 redef class AVardeclExpr
3186 redef fun stmt(v)
3187 do
3188 var variable = self.variable.as(not null)
3189 var ne = self.n_expr
3190 if ne != null then
3191 var i = v.expr(ne, variable.declared_type)
3192 v.assign(v.variable(variable), i)
3193 end
3194 end
3195 end
3196
3197 redef class AVarExpr
3198 redef fun expr(v)
3199 do
3200 var res = v.variable(self.variable.as(not null))
3201 var mtype = self.mtype.as(not null)
3202 return v.autoadapt(res, mtype)
3203 end
3204 end
3205
3206 redef class AVarAssignExpr
3207 redef fun expr(v)
3208 do
3209 var variable = self.variable.as(not null)
3210 var i = v.expr(self.n_value, variable.declared_type)
3211 v.assign(v.variable(variable), i)
3212 return i
3213 end
3214 end
3215
3216 redef class AVarReassignExpr
3217 redef fun stmt(v)
3218 do
3219 var variable = self.variable.as(not null)
3220 var vari = v.variable(variable)
3221 var value = v.expr(self.n_value, variable.declared_type)
3222 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3223 assert res != null
3224 v.assign(v.variable(variable), res)
3225 end
3226 end
3227
3228 redef class ASelfExpr
3229 redef fun expr(v) do return v.frame.arguments.first
3230 end
3231
3232 redef class AImplicitSelfExpr
3233 redef fun expr(v) do
3234 if not is_sys then return super
3235 return v.new_expr("glob_sys", mtype.as(not null))
3236 end
3237 end
3238
3239 redef class AEscapeExpr
3240 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3241 end
3242
3243 redef class AReturnExpr
3244 redef fun stmt(v)
3245 do
3246 var nexpr = self.n_expr
3247 if nexpr != null then
3248 var returnvar = v.frame.returnvar.as(not null)
3249 var i = v.expr(nexpr, returnvar.mtype)
3250 v.assign(returnvar, i)
3251 end
3252 v.add("goto {v.frame.returnlabel.as(not null)};")
3253 end
3254 end
3255
3256 redef class AAbortExpr
3257 redef fun stmt(v) do v.add_abort("Aborted")
3258 end
3259
3260 redef class AIfExpr
3261 redef fun stmt(v)
3262 do
3263 var cond = v.expr_bool(self.n_expr)
3264 v.add("if ({cond})\{")
3265 v.stmt(self.n_then)
3266 v.add("\} else \{")
3267 v.stmt(self.n_else)
3268 v.add("\}")
3269 end
3270
3271 redef fun expr(v)
3272 do
3273 var res = v.new_var(self.mtype.as(not null))
3274 var cond = v.expr_bool(self.n_expr)
3275 v.add("if ({cond})\{")
3276 v.assign(res, v.expr(self.n_then.as(not null), null))
3277 v.add("\} else \{")
3278 v.assign(res, v.expr(self.n_else.as(not null), null))
3279 v.add("\}")
3280 return res
3281 end
3282 end
3283
3284 redef class AIfexprExpr
3285 redef fun expr(v)
3286 do
3287 var res = v.new_var(self.mtype.as(not null))
3288 var cond = v.expr_bool(self.n_expr)
3289 v.add("if ({cond})\{")
3290 v.assign(res, v.expr(self.n_then, null))
3291 v.add("\} else \{")
3292 v.assign(res, v.expr(self.n_else, null))
3293 v.add("\}")
3294 return res
3295 end
3296 end
3297
3298 redef class ADoExpr
3299 redef fun stmt(v)
3300 do
3301 v.stmt(self.n_block)
3302 v.add_escape_label(break_mark)
3303 end
3304 end
3305
3306 redef class AWhileExpr
3307 redef fun stmt(v)
3308 do
3309 v.add("for(;;) \{")
3310 var cond = v.expr_bool(self.n_expr)
3311 v.add("if (!{cond}) break;")
3312 v.stmt(self.n_block)
3313 v.add_escape_label(continue_mark)
3314 v.add("\}")
3315 v.add_escape_label(break_mark)
3316 end
3317 end
3318
3319 redef class ALoopExpr
3320 redef fun stmt(v)
3321 do
3322 v.add("for(;;) \{")
3323 v.stmt(self.n_block)
3324 v.add_escape_label(continue_mark)
3325 v.add("\}")
3326 v.add_escape_label(break_mark)
3327 end
3328 end
3329
3330 redef class AForExpr
3331 redef fun stmt(v)
3332 do
3333 var cl = v.expr(self.n_expr, null)
3334 var it_meth = self.method_iterator
3335 assert it_meth != null
3336 var it = v.compile_callsite(it_meth, [cl])
3337 assert it != null
3338 v.add("for(;;) \{")
3339 var isok_meth = self.method_is_ok
3340 assert isok_meth != null
3341 var ok = v.compile_callsite(isok_meth, [it])
3342 assert ok != null
3343 v.add("if(!{ok}) break;")
3344 if self.variables.length == 1 then
3345 var item_meth = self.method_item
3346 assert item_meth != null
3347 var i = v.compile_callsite(item_meth, [it])
3348 assert i != null
3349 v.assign(v.variable(variables.first), i)
3350 else if self.variables.length == 2 then
3351 var key_meth = self.method_key
3352 assert key_meth != null
3353 var i = v.compile_callsite(key_meth, [it])
3354 assert i != null
3355 v.assign(v.variable(variables[0]), i)
3356 var item_meth = self.method_item
3357 assert item_meth != null
3358 i = v.compile_callsite(item_meth, [it])
3359 assert i != null
3360 v.assign(v.variable(variables[1]), i)
3361 else
3362 abort
3363 end
3364 v.stmt(self.n_block)
3365 v.add_escape_label(continue_mark)
3366 var next_meth = self.method_next
3367 assert next_meth != null
3368 v.compile_callsite(next_meth, [it])
3369 v.add("\}")
3370 v.add_escape_label(break_mark)
3371
3372 var method_finish = self.method_finish
3373 if method_finish != null then
3374 # TODO: Find a way to call this also in long escape (e.g. return)
3375 v.compile_callsite(method_finish, [it])
3376 end
3377 end
3378 end
3379
3380 redef class AAssertExpr
3381 redef fun stmt(v)
3382 do
3383 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3384
3385 var cond = v.expr_bool(self.n_expr)
3386 v.add("if (unlikely(!{cond})) \{")
3387 v.stmt(self.n_else)
3388 var nid = self.n_id
3389 if nid != null then
3390 v.add_abort("Assert '{nid.text}' failed")
3391 else
3392 v.add_abort("Assert failed")
3393 end
3394 v.add("\}")
3395 end
3396 end
3397
3398 redef class AOrExpr
3399 redef fun expr(v)
3400 do
3401 var res = v.new_var(self.mtype.as(not null))
3402 var i1 = v.expr_bool(self.n_expr)
3403 v.add("if ({i1}) \{")
3404 v.add("{res} = 1;")
3405 v.add("\} else \{")
3406 var i2 = v.expr_bool(self.n_expr2)
3407 v.add("{res} = {i2};")
3408 v.add("\}")
3409 return res
3410 end
3411 end
3412
3413 redef class AImpliesExpr
3414 redef fun expr(v)
3415 do
3416 var res = v.new_var(self.mtype.as(not null))
3417 var i1 = v.expr_bool(self.n_expr)
3418 v.add("if (!{i1}) \{")
3419 v.add("{res} = 1;")
3420 v.add("\} else \{")
3421 var i2 = v.expr_bool(self.n_expr2)
3422 v.add("{res} = {i2};")
3423 v.add("\}")
3424 return res
3425 end
3426 end
3427
3428 redef class AAndExpr
3429 redef fun expr(v)
3430 do
3431 var res = v.new_var(self.mtype.as(not null))
3432 var i1 = v.expr_bool(self.n_expr)
3433 v.add("if (!{i1}) \{")
3434 v.add("{res} = 0;")
3435 v.add("\} else \{")
3436 var i2 = v.expr_bool(self.n_expr2)
3437 v.add("{res} = {i2};")
3438 v.add("\}")
3439 return res
3440 end
3441 end
3442
3443 redef class ANotExpr
3444 redef fun expr(v)
3445 do
3446 var cond = v.expr_bool(self.n_expr)
3447 return v.new_expr("!{cond}", self.mtype.as(not null))
3448 end
3449 end
3450
3451 redef class AOrElseExpr
3452 redef fun expr(v)
3453 do
3454 var res = v.new_var(self.mtype.as(not null))
3455 var i1 = v.expr(self.n_expr, null)
3456 v.add("if ({i1}!=NULL) \{")
3457 v.assign(res, i1)
3458 v.add("\} else \{")
3459 var i2 = v.expr(self.n_expr2, null)
3460 v.assign(res, i2)
3461 v.add("\}")
3462 return res
3463 end
3464 end
3465
3466 redef class AIntegerExpr
3467 redef fun expr(v) do
3468 if value isa Int then return v.int_instance(value.as(Int))
3469 if value isa Byte then return v.byte_instance(value.as(Byte))
3470 if value isa Int8 then return v.int8_instance(value.as(Int8))
3471 if value isa Int16 then return v.int16_instance(value.as(Int16))
3472 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3473 if value isa Int32 then return v.int32_instance(value.as(Int32))
3474 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3475 # Should never happen
3476 abort
3477 end
3478 end
3479
3480 redef class AFloatExpr
3481 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3482 end
3483
3484 redef class ACharExpr
3485 redef fun expr(v) do return v.char_instance(self.value.as(not null))
3486 end
3487
3488 redef class AArrayExpr
3489 redef fun expr(v)
3490 do
3491 var mtype = self.element_mtype.as(not null)
3492 var array = new Array[RuntimeVariable]
3493 var res = v.array_instance(array, mtype)
3494
3495 var old_comprehension = v.frame.comprehension
3496 v.frame.comprehension = res
3497 for nexpr in self.n_exprs do
3498 v.stmt(nexpr)
3499 end
3500 v.frame.comprehension = old_comprehension
3501
3502 return res
3503 end
3504 end
3505
3506 redef class AStringFormExpr
3507 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3508 end
3509
3510 redef class ASuperstringExpr
3511 redef fun expr(v)
3512 do
3513 var type_string = mtype.as(not null)
3514
3515 # Collect elements of the superstring
3516 var array = new Array[AExpr]
3517 for ne in self.n_exprs do
3518 # Drop literal empty string.
3519 # They appears in things like "{a}" that is ["", a, ""]
3520 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3521 array.add(ne)
3522 end
3523
3524 # Store the allocated native array in a static variable
3525 # For reusing later
3526 var varonce = v.get_name("varonce")
3527 v.add("if (unlikely({varonce}==NULL)) \{")
3528
3529 # The native array that will contains the elements to_s-ized.
3530 # For fast concatenation.
3531 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3532
3533 v.add_decl("static {a.mtype.ctype} {varonce};")
3534
3535 # Pre-fill the array with the literal string parts.
3536 # So they do not need to be filled again when reused
3537 for i in [0..array.length[ do
3538 var ne = array[i]
3539 if not ne isa AStringFormExpr then continue
3540 var e = v.expr(ne, null)
3541 v.native_array_set(a, i, e)
3542 end
3543
3544 v.add("\} else \{")
3545 # Take the native-array from the store.
3546 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3547 # WARNING: not thread safe! (FIXME?)
3548 v.add("{a} = {varonce};")
3549 v.add("{varonce} = NULL;")
3550 v.add("\}")
3551
3552 # Stringify the elements and put them in the native array
3553 var to_s_method = v.get_property("to_s", v.object_type)
3554 for i in [0..array.length[ do
3555 var ne = array[i]
3556 if ne isa AStringFormExpr then continue
3557 var e = v.expr(ne, null)
3558 # Skip the `to_s` if the element is already a String
3559 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3560 e = v.send(to_s_method, [e]).as(not null)
3561 end
3562 v.native_array_set(a, i, e)
3563 end
3564
3565 # Fast join the native string to get the result
3566 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3567
3568 # We finish to work with the native array,
3569 # so store it so that it can be reused
3570 v.add("{varonce} = {a};")
3571 return res
3572 end
3573 end
3574
3575 redef class ACrangeExpr
3576 redef fun expr(v)
3577 do
3578 var i1 = v.expr(self.n_expr, null)
3579 var i2 = v.expr(self.n_expr2, null)
3580 var mtype = self.mtype.as(MClassType)
3581 var res = v.init_instance(mtype)
3582 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3583 return res
3584 end
3585 end
3586
3587 redef class AOrangeExpr
3588 redef fun expr(v)
3589 do
3590 var i1 = v.expr(self.n_expr, null)
3591 var i2 = v.expr(self.n_expr2, null)
3592 var mtype = self.mtype.as(MClassType)
3593 var res = v.init_instance(mtype)
3594 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3595 return res
3596 end
3597 end
3598
3599 redef class ATrueExpr
3600 redef fun expr(v) do return v.bool_instance(true)
3601 end
3602
3603 redef class AFalseExpr
3604 redef fun expr(v) do return v.bool_instance(false)
3605 end
3606
3607 redef class ANullExpr
3608 redef fun expr(v) do return v.null_instance
3609 end
3610
3611 redef class AIsaExpr
3612 redef fun expr(v)
3613 do
3614 var i = v.expr(self.n_expr, null)
3615 var cast_type = self.cast_type
3616 if cast_type == null then return null # no-no on broken node
3617 return v.type_test(i, cast_type, "isa")
3618 end
3619 end
3620
3621 redef class AAsCastExpr
3622 redef fun expr(v)
3623 do
3624 var i = v.expr(self.n_expr, null)
3625 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3626
3627 v.add_cast(i, self.mtype.as(not null), "as")
3628 return i
3629 end
3630 end
3631
3632 redef class AAsNotnullExpr
3633 redef fun expr(v)
3634 do
3635 var i = v.expr(self.n_expr, null)
3636 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3637
3638 if i.mtype.is_c_primitive then return i
3639
3640 v.add("if (unlikely({i} == NULL)) \{")
3641 v.add_abort("Cast failed")
3642 v.add("\}")
3643 return i
3644 end
3645 end
3646
3647 redef class AParExpr
3648 redef fun expr(v) do return v.expr(self.n_expr, null)
3649 end
3650
3651 redef class AOnceExpr
3652 redef fun expr(v)
3653 do
3654 var mtype = self.mtype.as(not null)
3655 var name = v.get_name("varonce")
3656 var guard = v.get_name(name + "_guard")
3657 v.add_decl("static {mtype.ctype} {name};")
3658 v.add_decl("static int {guard};")
3659 var res = v.new_var(mtype)
3660 v.add("if (likely({guard})) \{")
3661 v.add("{res} = {name};")
3662 v.add("\} else \{")
3663 var i = v.expr(self.n_expr, mtype)
3664 v.add("{res} = {i};")
3665 v.add("{name} = {res};")
3666 v.add("{guard} = 1;")
3667 v.add("\}")
3668 return res
3669 end
3670 end
3671
3672 redef class ASendExpr
3673 redef fun expr(v)
3674 do
3675 var recv = v.expr(self.n_expr, null)
3676 var callsite = self.callsite.as(not null)
3677 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3678 return v.compile_callsite(callsite, args)
3679 end
3680 end
3681
3682 redef class ASendReassignFormExpr
3683 redef fun stmt(v)
3684 do
3685 var recv = v.expr(self.n_expr, null)
3686 var callsite = self.callsite.as(not null)
3687 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3688
3689 var value = v.expr(self.n_value, null)
3690
3691 var left = v.compile_callsite(callsite, args)
3692 assert left != null
3693
3694 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3695 assert res != null
3696
3697 args.add(res)
3698 v.compile_callsite(self.write_callsite.as(not null), args)
3699 end
3700 end
3701
3702 redef class ASuperExpr
3703 redef fun expr(v)
3704 do
3705 var recv = v.frame.arguments.first
3706
3707 var callsite = self.callsite
3708 if callsite != null then
3709 var args
3710
3711 if self.n_args.n_exprs.is_empty then
3712 # Add automatic arguments for the super init call
3713 args = [recv]
3714 for i in [0..callsite.msignature.arity[ do
3715 args.add(v.frame.arguments[i+1])
3716 end
3717 else
3718 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3719 end
3720
3721 # Super init call
3722 var res = v.compile_callsite(callsite, args)
3723 return res
3724 end
3725
3726 var mpropdef = self.mpropdef.as(not null)
3727
3728 var args
3729 if self.n_args.n_exprs.is_empty then
3730 args = v.frame.arguments
3731 else
3732 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3733 end
3734
3735 # Standard call-next-method
3736 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3737 end
3738 end
3739
3740 redef class ANewExpr
3741 redef fun expr(v)
3742 do
3743 var mtype = self.recvtype
3744 assert mtype != null
3745
3746 if mtype.mclass.name == "NativeArray" then
3747 assert self.n_args.n_exprs.length == 1
3748 var l = v.expr(self.n_args.n_exprs.first, null)
3749 assert mtype isa MGenericType
3750 var elttype = mtype.arguments.first
3751 return v.native_array_instance(elttype, l)
3752 end
3753
3754 var recv = v.init_instance_or_extern(mtype)
3755
3756 var callsite = self.callsite
3757 if callsite == null then return recv
3758
3759 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3760 var res2 = v.compile_callsite(callsite, args)
3761 if res2 != null then
3762 #self.debug("got {res2} from {mproperty}. drop {recv}")
3763 return res2
3764 end
3765 return recv
3766 end
3767 end
3768
3769 redef class AAttrExpr
3770 redef fun expr(v)
3771 do
3772 var recv = v.expr(self.n_expr, null)
3773 var mproperty = self.mproperty.as(not null)
3774 return v.read_attribute(mproperty, recv)
3775 end
3776 end
3777
3778 redef class AAttrAssignExpr
3779 redef fun expr(v)
3780 do
3781 var recv = v.expr(self.n_expr, null)
3782 var i = v.expr(self.n_value, null)
3783 var mproperty = self.mproperty.as(not null)
3784 v.write_attribute(mproperty, recv, i)
3785 return i
3786 end
3787 end
3788
3789 redef class AAttrReassignExpr
3790 redef fun stmt(v)
3791 do
3792 var recv = v.expr(self.n_expr, null)
3793 var value = v.expr(self.n_value, null)
3794 var mproperty = self.mproperty.as(not null)
3795 var attr = v.read_attribute(mproperty, recv)
3796 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3797 assert res != null
3798 v.write_attribute(mproperty, recv, res)
3799 end
3800 end
3801
3802 redef class AIssetAttrExpr
3803 redef fun expr(v)
3804 do
3805 var recv = v.expr(self.n_expr, null)
3806 var mproperty = self.mproperty.as(not null)
3807 return v.isset_attribute(mproperty, recv)
3808 end
3809 end
3810
3811 redef class AVarargExpr
3812 redef fun expr(v)
3813 do
3814 return v.expr(self.n_expr, null)
3815 end
3816 end
3817
3818 redef class ANamedargExpr
3819 redef fun expr(v)
3820 do
3821 return v.expr(self.n_expr, null)
3822 end
3823 end
3824
3825 redef class ADebugTypeExpr
3826 redef fun stmt(v)
3827 do
3828 # do nothing
3829 end
3830 end
3831
3832 # Utils
3833
3834 redef class Array[E]
3835 # Return a new `Array` with the elements only contened in self and not in `o`
3836 fun -(o: Array[E]): Array[E] do
3837 var res = new Array[E]
3838 for e in self do if not o.has(e) then res.add(e)
3839 return res
3840 end
3841 end
3842
3843 redef class MModule
3844 # All `MProperty` associated to all `MClassDef` of `mclass`
3845 fun properties(mclass: MClass): Set[MProperty] do
3846 if not self.properties_cache.has_key(mclass) then
3847 var properties = new HashSet[MProperty]
3848 var parents = new Array[MClass]
3849 if self.flatten_mclass_hierarchy.has(mclass) then
3850 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3851 end
3852 for parent in parents do
3853 properties.add_all(self.properties(parent))
3854 end
3855 for mclassdef in mclass.mclassdefs do
3856 if not self.in_importation <= mclassdef.mmodule then continue
3857 for mprop in mclassdef.intro_mproperties do
3858 properties.add(mprop)
3859 end
3860 end
3861 self.properties_cache[mclass] = properties
3862 end
3863 return properties_cache[mclass]
3864 end
3865 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3866
3867 # Write FFI and nitni results to file
3868 fun finalize_ffi(c: AbstractCompiler) do end
3869
3870 # Give requided addinional system libraries (as given to LD_LIBS)
3871 # Note: can return null instead of an empty set
3872 fun collect_linker_libs: nullable Array[String] do return null
3873 end
3874
3875 # Create a tool context to handle options and paths
3876 var toolcontext = new ToolContext
3877
3878 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3879
3880 # We do not add other options, so process them now!
3881 toolcontext.process_options(args)
3882
3883 # We need a model to collect stufs
3884 var model = new Model
3885 # An a model builder to parse files
3886 var modelbuilder = new ModelBuilder(model, toolcontext)
3887
3888 var arguments = toolcontext.option_context.rest
3889 if arguments.length > 1 and toolcontext.opt_output.value != null then
3890 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3891 exit 1
3892 end
3893
3894 # Here we load an process all modules passed on the command line
3895 var mmodules = modelbuilder.parse(arguments)
3896
3897 if mmodules.is_empty then return
3898 modelbuilder.run_phases
3899
3900 for mmodule in mmodules do
3901 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3902 var ms = [mmodule]
3903 toolcontext.run_global_phases(ms)
3904 end