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