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