Merge remote-tracking branch 'origin/master' into init_auto
[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 -Wno-trigraphs\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(" #ifndef be32toh")
672 self.header.add_decl(" #define be32toh(val) betoh32(val)")
673 self.header.add_decl(" #endif")
674 self.header.add_decl(" #include <android/log.h>")
675 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
676 self.header.add_decl("#else")
677 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
678 self.header.add_decl("#endif")
679
680 compile_header_structs
681 compile_nitni_structs
682
683 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
684 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
685 # Signal handler function prototype
686 self.header.add_decl("void fatal_exit(int);")
687 else
688 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
689 end
690
691 if gccd_disable.has("likely") or gccd_disable.has("all") then
692 self.header.add_decl("#define likely(x) (x)")
693 self.header.add_decl("#define unlikely(x) (x)")
694 else if gccd_disable.has("correct-likely") then
695 # invert the `likely` definition
696 # Used by masochists to bench the worst case
697 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
698 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
699 else
700 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
701 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
702 end
703
704 # Global variable used by intern methods
705 self.header.add_decl("extern int glob_argc;")
706 self.header.add_decl("extern char **glob_argv;")
707 self.header.add_decl("extern val *glob_sys;")
708 end
709
710 # Declaration of structures for live Nit types
711 protected fun compile_header_structs is abstract
712
713 # Declaration of structures for nitni undelying the FFI
714 protected fun compile_nitni_structs
715 do
716 self.header.add_decl """
717 /* Native reference to Nit objects */
718 /* This structure is used to represent every Nit type in extern methods and custom C code. */
719 struct nitni_ref {
720 struct nitni_ref *next,
721 *prev; /* adjacent global references in global list */
722 int count; /* number of time this global reference has been marked */
723 };
724
725 /* List of global references from C code to Nit objects */
726 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
727 struct nitni_global_ref_list_t {
728 struct nitni_ref *head, *tail;
729 };
730 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
731
732 /* Initializer of global reference list */
733 extern void nitni_global_ref_list_init();
734
735 /* Intern function to add a global reference to the list */
736 extern void nitni_global_ref_add( struct nitni_ref *ref );
737
738 /* Intern function to remove a global reference from the list */
739 extern void nitni_global_ref_remove( struct nitni_ref *ref );
740
741 /* Increase count on an existing global reference */
742 extern void nitni_global_ref_incr( struct nitni_ref *ref );
743
744 /* Decrease count on an existing global reference */
745 extern void nitni_global_ref_decr( struct nitni_ref *ref );
746 """
747 end
748
749 fun compile_finalizer_function
750 do
751 var finalizable_type = mainmodule.finalizable_type
752 if finalizable_type == null then return
753
754 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
755
756 if finalize_meth == null then
757 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
758 return
759 end
760
761 var v = self.new_visitor
762 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
763 var recv = v.new_expr("obj", finalizable_type)
764 v.send(finalize_meth, [recv])
765 v.add "\}"
766 end
767
768 # Generate the main C function.
769 #
770 # This function:
771 #
772 # * allocate the Sys object if it exists
773 # * call init if is exists
774 # * call main if it exists
775 fun compile_main_function
776 do
777 var v = self.new_visitor
778 v.add_decl("#include <signal.h>")
779 var platform = target_platform
780
781 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
782
783 if platform.supports_libunwind then
784 v.add_decl("#ifndef NO_STACKTRACE")
785 v.add_decl("#define UNW_LOCAL_ONLY")
786 v.add_decl("#include <libunwind.h>")
787 v.add_decl("#include \"c_functions_hash.h\"")
788 v.add_decl("#endif")
789 end
790 v.add_decl("int glob_argc;")
791 v.add_decl("char **glob_argv;")
792 v.add_decl("val *glob_sys;")
793
794 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
795 for tag in count_type_test_tags do
796 v.add_decl("long count_type_test_resolved_{tag};")
797 v.add_decl("long count_type_test_unresolved_{tag};")
798 v.add_decl("long count_type_test_skipped_{tag};")
799 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
800 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
801 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
802 end
803 end
804
805 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
806 v.add_decl("long count_invoke_by_tables;")
807 v.add_decl("long count_invoke_by_direct;")
808 v.add_decl("long count_invoke_by_inline;")
809 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
810 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
811 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
812 end
813
814 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
815 v.add_decl("long count_attr_reads = 0;")
816 v.add_decl("long count_isset_checks = 0;")
817 v.compiler.header.add_decl("extern long count_attr_reads;")
818 v.compiler.header.add_decl("extern long count_isset_checks;")
819 end
820
821 v.add_decl("static void show_backtrace(void) \{")
822 if platform.supports_libunwind then
823 v.add_decl("#ifndef NO_STACKTRACE")
824 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
825 v.add_decl("unw_cursor_t cursor;")
826 v.add_decl("if(opt==NULL)\{")
827 v.add_decl("unw_context_t uc;")
828 v.add_decl("unw_word_t ip;")
829 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
830 v.add_decl("unw_getcontext(&uc);")
831 v.add_decl("unw_init_local(&cursor, &uc);")
832 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
833 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
834 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
835 v.add_decl("while (unw_step(&cursor) > 0) \{")
836 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
837 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
838 v.add_decl(" if (recv != NULL)\{")
839 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
840 v.add_decl(" \}else\{")
841 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
842 v.add_decl(" \}")
843 v.add_decl("\}")
844 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
845 v.add_decl("free(procname);")
846 v.add_decl("\}")
847 v.add_decl("#endif /* NO_STACKTRACE */")
848 end
849 v.add_decl("\}")
850
851 v.add_decl("void sig_handler(int signo)\{")
852 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
853 v.add_decl("show_backtrace();")
854 # rethrows
855 v.add_decl("signal(signo, SIG_DFL);")
856 v.add_decl("kill(getpid(), signo);")
857 v.add_decl("\}")
858
859 v.add_decl("void fatal_exit(int status) \{")
860 v.add_decl("show_backtrace();")
861 v.add_decl("exit(status);")
862 v.add_decl("\}")
863
864 if no_main then
865 v.add_decl("int nit_main(int argc, char** argv) \{")
866 else
867 v.add_decl("int main(int argc, char** argv) \{")
868 end
869
870 v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
871 v.add("signal(SIGABRT, sig_handler);")
872 v.add("signal(SIGFPE, sig_handler);")
873 v.add("signal(SIGILL, sig_handler);")
874 v.add("signal(SIGINT, sig_handler);")
875 v.add("signal(SIGTERM, sig_handler);")
876 v.add("signal(SIGSEGV, sig_handler);")
877 v.add "#endif"
878 v.add("signal(SIGPIPE, SIG_IGN);")
879
880 v.add("glob_argc = argc; glob_argv = argv;")
881 v.add("initialize_gc_option();")
882
883 v.add "initialize_nitni_global_refs();"
884
885 var main_type = mainmodule.sys_type
886 if main_type != null then
887 var mainmodule = v.compiler.mainmodule
888 var glob_sys = v.init_instance(main_type)
889 v.add("glob_sys = {glob_sys};")
890 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
891 if main_init != null then
892 v.send(main_init, [glob_sys])
893 end
894 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
895 mainmodule.try_get_primitive_method("main", main_type.mclass)
896 if main_method != null then
897 v.send(main_method, [glob_sys])
898 end
899 end
900
901 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
902 v.add_decl("long count_type_test_resolved_total = 0;")
903 v.add_decl("long count_type_test_unresolved_total = 0;")
904 v.add_decl("long count_type_test_skipped_total = 0;")
905 v.add_decl("long count_type_test_total_total = 0;")
906 for tag in count_type_test_tags do
907 v.add_decl("long count_type_test_total_{tag};")
908 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
909 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
910 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
911 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
912 v.add("count_type_test_total_total += count_type_test_total_{tag};")
913 end
914 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
915 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
916 var tags = count_type_test_tags.to_a
917 tags.add("total")
918 for tag in tags do
919 v.add("printf(\"{tag}\");")
920 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
921 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
922 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
923 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
924 end
925 end
926
927 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
928 v.add_decl("long count_invoke_total;")
929 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
930 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
931 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
932 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
933 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
934 end
935
936 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
937 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
938 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
939 end
940
941 v.add("return 0;")
942 v.add("\}")
943
944 for m in mainmodule.in_importation.greaters do
945 var f = "FILE_"+m.c_name
946 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
947 provide_declaration(f, "extern const char {f}[];")
948 end
949 end
950
951 # Copile all C functions related to the [incr|decr]_ref features of the FFI
952 fun compile_nitni_global_ref_functions
953 do
954 var v = self.new_visitor
955 v.add """
956 struct nitni_global_ref_list_t *nitni_global_ref_list;
957 void initialize_nitni_global_refs() {
958 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
959 nitni_global_ref_list->head = NULL;
960 nitni_global_ref_list->tail = NULL;
961 }
962
963 void nitni_global_ref_add( struct nitni_ref *ref ) {
964 if ( nitni_global_ref_list->head == NULL ) {
965 nitni_global_ref_list->head = ref;
966 ref->prev = NULL;
967 } else {
968 nitni_global_ref_list->tail->next = ref;
969 ref->prev = nitni_global_ref_list->tail;
970 }
971 nitni_global_ref_list->tail = ref;
972
973 ref->next = NULL;
974 }
975
976 void nitni_global_ref_remove( struct nitni_ref *ref ) {
977 if ( ref->prev == NULL ) {
978 nitni_global_ref_list->head = ref->next;
979 } else {
980 ref->prev->next = ref->next;
981 }
982
983 if ( ref->next == NULL ) {
984 nitni_global_ref_list->tail = ref->prev;
985 } else {
986 ref->next->prev = ref->prev;
987 }
988 }
989
990 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
991 if ( ref->count == 0 ) /* not registered */
992 {
993 /* add to list */
994 nitni_global_ref_add( ref );
995 }
996
997 ref->count ++;
998 }
999
1000 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
1001 if ( ref->count == 1 ) /* was last reference */
1002 {
1003 /* remove from list */
1004 nitni_global_ref_remove( ref );
1005 }
1006
1007 ref->count --;
1008 }
1009 """
1010 end
1011
1012 # List of additional files required to compile (FFI)
1013 var extern_bodies = new Array[ExternFile]
1014
1015 # List of source files to copy over to the compile dir
1016 var files_to_copy = new Array[String]
1017
1018 # This is used to avoid adding an extern file more than once
1019 private var seen_extern = new ArraySet[String]
1020
1021 # Generate code that initialize the attributes on a new instance
1022 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1023 do
1024 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1025 self.mainmodule.linearize_mclassdefs(cds)
1026 for cd in cds do
1027 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1028 npropdef.init_expr(v, recv)
1029 end
1030 end
1031 end
1032
1033 # Generate code that check if an attribute is correctly initialized
1034 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1035 do
1036 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1037 self.mainmodule.linearize_mclassdefs(cds)
1038 for cd in cds do
1039 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1040 npropdef.check_expr(v, recv)
1041 end
1042 end
1043 end
1044
1045 # stats
1046
1047 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1048 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1049 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1050 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1051
1052 protected fun init_count_type_test_tags: HashMap[String, Int]
1053 do
1054 var res = new HashMap[String, Int]
1055 for tag in count_type_test_tags do
1056 res[tag] = 0
1057 end
1058 return res
1059 end
1060
1061 # Display stats about compilation process
1062 #
1063 # Metrics used:
1064 #
1065 # * type tests against resolved types (`x isa Collection[Animal]`)
1066 # * type tests against unresolved types (`x isa Collection[E]`)
1067 # * type tests skipped
1068 # * type tests total
1069 fun display_stats
1070 do
1071 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1072 print "# static count_type_test"
1073 print "\tresolved:\tunresolved\tskipped\ttotal"
1074 var count_type_test_total = init_count_type_test_tags
1075 count_type_test_resolved["total"] = 0
1076 count_type_test_unresolved["total"] = 0
1077 count_type_test_skipped["total"] = 0
1078 count_type_test_total["total"] = 0
1079 for tag in count_type_test_tags do
1080 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1081 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1082 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1083 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1084 count_type_test_total["total"] += count_type_test_total[tag]
1085 end
1086 var count_type_test = count_type_test_total["total"]
1087 var tags = count_type_test_tags.to_a
1088 tags.add("total")
1089 for tag in tags do
1090 printn tag
1091 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1092 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1093 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1094 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1095 print ""
1096 end
1097 end
1098 end
1099
1100 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1101 end
1102
1103 # A file unit (may be more than one file if
1104 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1105 class CodeFile
1106 var name: String
1107 var writers = new Array[CodeWriter]
1108 var required_declarations = new HashSet[String]
1109 end
1110
1111 # Where to store generated lines
1112 class CodeWriter
1113 var file: CodeFile
1114 var lines: List[String] = new List[String]
1115 var decl_lines: List[String] = new List[String]
1116
1117 # Add a line in the main part of the generated C
1118 fun add(s: String) do self.lines.add(s)
1119
1120 # Add a line in the
1121 # (used for local or global declaration)
1122 fun add_decl(s: String) do self.decl_lines.add(s)
1123
1124 init
1125 do
1126 file.writers.add(self)
1127 end
1128 end
1129
1130 # A visitor on the AST of property definition that generate the C code.
1131 abstract class AbstractCompilerVisitor
1132
1133 type COMPILER: AbstractCompiler
1134
1135 # The associated compiler
1136 var compiler: COMPILER
1137
1138 # The current visited AST node
1139 var current_node: nullable ANode = null is writable
1140
1141 # The current `StaticFrame`
1142 var frame: nullable StaticFrame = null is writable
1143
1144 # Alias for self.compiler.mainmodule.object_type
1145 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1146
1147 # Alias for self.compiler.mainmodule.bool_type
1148 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1149
1150 var writer: CodeWriter is noinit
1151
1152 init
1153 do
1154 self.writer = new CodeWriter(compiler.files.last)
1155 end
1156
1157 # Force to get the primitive property named `name` in the instance `recv` or abort
1158 fun get_property(name: String, recv: MType): MMethod
1159 do
1160 assert recv isa MClassType
1161 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1162 end
1163
1164 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1165 do
1166 if callsite.is_broken then return null
1167 return self.send(callsite.mproperty, arguments)
1168 end
1169
1170 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1171
1172 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1173
1174 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
1175
1176 # Return an element of a native array.
1177 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1178 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1179
1180 # Store an element in a native array.
1181 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1182 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1183
1184 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1185 # This method is used to manage varargs in signatures and returns the real array
1186 # of runtime variables to use in the call.
1187 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1188 do
1189 var msignature = mpropdef.msignature.as(not null)
1190 var res = new Array[RuntimeVariable]
1191 res.add(recv)
1192
1193 if msignature.arity == 0 then return res
1194
1195 if map == null then
1196 assert args.length == msignature.arity
1197 for ne in args do
1198 res.add self.expr(ne, null)
1199 end
1200 return res
1201 end
1202
1203 # Eval in order of arguments, not parameters
1204 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1205 for ne in args do
1206 exprs.add self.expr(ne, null)
1207 end
1208
1209 # Fill `res` with the result of the evaluation according to the mapping
1210 for i in [0..msignature.arity[ do
1211 var param = msignature.mparameters[i]
1212 var j = map.map.get_or_null(i)
1213 if j == null then
1214 # default value
1215 res.add(null_instance)
1216 continue
1217 end
1218 if param.is_vararg and args[i].vararg_decl > 0 then
1219 var vararg = exprs.sub(j, args[i].vararg_decl)
1220 var elttype = param.mtype
1221 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1222 res.add(arg)
1223 continue
1224 end
1225 res.add exprs[j]
1226 end
1227 return res
1228 end
1229
1230 # Type handling
1231
1232 # Anchor a type to the main module and the current receiver
1233 fun anchor(mtype: MType): MType
1234 do
1235 if not mtype.need_anchor then return mtype
1236 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1237 end
1238
1239 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1240 do
1241 if not mtype.need_anchor then return mtype
1242 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1243 end
1244
1245 # Unsafely cast a value to a new type
1246 # ie the result share the same C variable but my have a different mcasttype
1247 # NOTE: if the adaptation is useless then `value` is returned as it.
1248 # ENSURE: `result.name == value.name`
1249 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1250 do
1251 mtype = self.anchor(mtype)
1252 var valmtype = value.mcasttype
1253
1254 # Do nothing if useless autocast
1255 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1256 return value
1257 end
1258
1259 # Just as_not_null if the target is not nullable.
1260 #
1261 # eg `nullable PreciseType` adapted to `Object` gives precisetype.
1262 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1263 mtype = valmtype.mtype
1264 end
1265
1266 var res = new RuntimeVariable(value.name, value.mtype, mtype)
1267 return res
1268 end
1269
1270 # Generate a super call from a method definition
1271 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1272
1273 # Adapt the arguments of a method according to targetted `MMethodDef`
1274 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1275
1276 # Unbox all the arguments of a method when implemented `extern` or `intern`
1277 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1278
1279 # Box or unbox a value to another type iff a C type conversion is needed
1280 # ENSURE: `result.mtype.ctype == mtype.ctype`
1281 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1282
1283 # Box extern classes to be used in the generated code
1284 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1285
1286 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1287 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1288
1289 # Generate a polymorphic subtype test
1290 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1291
1292 # Generate the code required to dynamically check if 2 objects share the same runtime type
1293 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1294
1295 # Generate a Nit "is" for two runtime_variables
1296 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1297
1298 # Sends
1299
1300 # Generate a static call on a method definition
1301 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1302
1303 # Generate a polymorphic send for the method `m` and the arguments `args`
1304 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1305
1306 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1307 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1308 do
1309 assert t isa MClassType
1310 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1311 return self.call(propdef, t, args)
1312 end
1313
1314 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1315 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1316 do
1317 assert t isa MClassType
1318 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1319 return self.call(m, t, args)
1320 end
1321
1322 # Attributes handling
1323
1324 # Generate a polymorphic attribute is_set test
1325 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1326
1327 # Generate a polymorphic attribute read
1328 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1329
1330 # Generate a polymorphic attribute write
1331 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1332
1333 # Checks
1334
1335 # Can value be null? (according to current knowledge)
1336 fun maybenull(value: RuntimeVariable): Bool
1337 do
1338 return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
1339 end
1340
1341 # Add a check and an abort for a null receiver if needed
1342 fun check_recv_notnull(recv: RuntimeVariable)
1343 do
1344 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1345
1346 if maybenull(recv) then
1347 self.add("if (unlikely({recv} == NULL)) \{")
1348 self.add_abort("Receiver is null")
1349 self.add("\}")
1350 end
1351 end
1352
1353 # Names handling
1354
1355 private var names = new HashSet[String]
1356 private var last: Int = 0
1357
1358 # Return a new name based on `s` and unique in the visitor
1359 fun get_name(s: String): String
1360 do
1361 if not self.names.has(s) then
1362 self.names.add(s)
1363 return s
1364 end
1365 var i = self.last + 1
1366 loop
1367 var s2 = s + i.to_s
1368 if not self.names.has(s2) then
1369 self.last = i
1370 self.names.add(s2)
1371 return s2
1372 end
1373 i = i + 1
1374 end
1375 end
1376
1377 # Return an unique and stable identifier associated with an escapemark
1378 fun escapemark_name(e: nullable EscapeMark): String
1379 do
1380 assert e != null
1381 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1382 var name = e.name
1383 if name == null then name = "label"
1384 name = get_name(name)
1385 frame.escapemark_names[e] = name
1386 return name
1387 end
1388
1389 # Insert a C label for associated with an escapemark
1390 fun add_escape_label(e: nullable EscapeMark)
1391 do
1392 if e == null then return
1393 if e.escapes.is_empty then return
1394 add("BREAK_{escapemark_name(e)}: (void)0;")
1395 end
1396
1397 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1398 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1399 fun class_name_string(value: RuntimeVariable): String is abstract
1400
1401 # Variables handling
1402
1403 protected var variables = new HashMap[Variable, RuntimeVariable]
1404
1405 # Return the local runtime_variable associated to a Nit local variable
1406 fun variable(variable: Variable): RuntimeVariable
1407 do
1408 if self.variables.has_key(variable) then
1409 return self.variables[variable]
1410 else
1411 var name = self.get_name("var_{variable.name}")
1412 var mtype = variable.declared_type.as(not null)
1413 mtype = self.anchor(mtype)
1414 var res = new RuntimeVariable(name, mtype, mtype)
1415 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1416 self.variables[variable] = res
1417 return res
1418 end
1419 end
1420
1421 # Return a new uninitialized local runtime_variable
1422 fun new_var(mtype: MType): RuntimeVariable
1423 do
1424 mtype = self.anchor(mtype)
1425 var name = self.get_name("var")
1426 var res = new RuntimeVariable(name, mtype, mtype)
1427 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1428 return res
1429 end
1430
1431 # The difference with `new_var` is the C static type of the local variable
1432 fun new_var_extern(mtype: MType): RuntimeVariable
1433 do
1434 mtype = self.anchor(mtype)
1435 var name = self.get_name("var")
1436 var res = new RuntimeVariable(name, mtype, mtype)
1437 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1438 return res
1439 end
1440
1441 # Return a new uninitialized named runtime_variable
1442 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1443 do
1444 mtype = self.anchor(mtype)
1445 var res = new RuntimeVariable(name, mtype, mtype)
1446 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1447 return res
1448 end
1449
1450 # Correctly assign a left and a right value
1451 # Boxing and unboxing is performed if required
1452 fun assign(left, right: RuntimeVariable)
1453 do
1454 right = self.autobox(right, left.mtype)
1455 self.add("{left} = {right};")
1456 end
1457
1458 # Generate instances
1459
1460 # Generate a alloc-instance + init-attributes
1461 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1462
1463 # Allocate and init attributes of an instance of a standard or extern class
1464 #
1465 # Does not support universals and the pseudo-internal `NativeArray` class.
1466 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1467 do
1468 var recv
1469 var ctype = mtype.ctype
1470 assert mtype.mclass.name != "NativeArray"
1471 if not mtype.is_c_primitive then
1472 recv = init_instance(mtype)
1473 else if ctype == "char*" then
1474 recv = new_expr("NULL/*special!*/", mtype)
1475 else
1476 recv = new_expr("({ctype})0/*special!*/", mtype)
1477 end
1478 return recv
1479 end
1480
1481 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1482 fun set_finalizer(recv: RuntimeVariable)
1483 do
1484 var mtype = recv.mtype
1485 var finalizable_type = compiler.mainmodule.finalizable_type
1486 if finalizable_type != null and not mtype.need_anchor and
1487 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1488 add "gc_register_finalizer({recv});"
1489 end
1490 end
1491
1492 # The currently processed module
1493 #
1494 # alias for `compiler.mainmodule`
1495 fun mmodule: MModule do return compiler.mainmodule
1496
1497 # Generate an integer value
1498 fun int_instance(value: Int): RuntimeVariable
1499 do
1500 var t = mmodule.int_type
1501 var res = new RuntimeVariable("{value.to_s}l", t, t)
1502 return res
1503 end
1504
1505 # Generate a byte value
1506 fun byte_instance(value: Byte): RuntimeVariable
1507 do
1508 var t = mmodule.byte_type
1509 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1510 return res
1511 end
1512
1513 # Generate an int8 value
1514 fun int8_instance(value: Int8): RuntimeVariable
1515 do
1516 var t = mmodule.int8_type
1517 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1518 return res
1519 end
1520
1521 # Generate an int16 value
1522 fun int16_instance(value: Int16): RuntimeVariable
1523 do
1524 var t = mmodule.int16_type
1525 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1526 return res
1527 end
1528
1529 # Generate a uint16 value
1530 fun uint16_instance(value: UInt16): RuntimeVariable
1531 do
1532 var t = mmodule.uint16_type
1533 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1534 return res
1535 end
1536
1537 # Generate an int32 value
1538 fun int32_instance(value: Int32): RuntimeVariable
1539 do
1540 var t = mmodule.int32_type
1541 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1542 return res
1543 end
1544
1545 # Generate a uint32 value
1546 fun uint32_instance(value: UInt32): RuntimeVariable
1547 do
1548 var t = mmodule.uint32_type
1549 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1550 return res
1551 end
1552
1553 # Generate a char value
1554 fun char_instance(value: Char): RuntimeVariable
1555 do
1556 var t = mmodule.char_type
1557
1558 if value.code_point < 128 then
1559 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1560 else
1561 return new RuntimeVariable("{value.code_point}", t, t)
1562 end
1563 end
1564
1565 # Generate a float value
1566 #
1567 # FIXME pass a Float, not a string
1568 fun float_instance(value: String): RuntimeVariable
1569 do
1570 var t = mmodule.float_type
1571 var res = new RuntimeVariable("{value}", t, t)
1572 return res
1573 end
1574
1575 # Generate an integer value
1576 fun bool_instance(value: Bool): RuntimeVariable
1577 do
1578 var s = if value then "1" else "0"
1579 var res = new RuntimeVariable(s, bool_type, bool_type)
1580 return res
1581 end
1582
1583 # Generate the `null` value
1584 fun null_instance: RuntimeVariable
1585 do
1586 var t = compiler.mainmodule.model.null_type
1587 var res = new RuntimeVariable("((val*)NULL)", t, t)
1588 return res
1589 end
1590
1591 # Generate a string value
1592 fun string_instance(string: String): RuntimeVariable
1593 do
1594 var mtype = mmodule.string_type
1595 var name = self.get_name("varonce")
1596 self.add_decl("static {mtype.ctype} {name};")
1597 var res = self.new_var(mtype)
1598 self.add("if (likely({name}!=NULL)) \{")
1599 self.add("{res} = {name};")
1600 self.add("\} else \{")
1601 var native_mtype = mmodule.native_string_type
1602 var nat = self.new_var(native_mtype)
1603 self.add("{nat} = \"{string.escape_to_c}\";")
1604 var bytelen = self.int_instance(string.bytelen)
1605 var unilen = self.int_instance(string.length)
1606 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1607 self.add("{name} = {res};")
1608 self.add("\}")
1609 return res
1610 end
1611
1612 fun value_instance(object: Object): RuntimeVariable
1613 do
1614 if object isa Int then
1615 return int_instance(object)
1616 else if object isa Bool then
1617 return bool_instance(object)
1618 else if object isa String then
1619 return string_instance(object)
1620 else
1621 abort
1622 end
1623 end
1624
1625 # Generate an array value
1626 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1627
1628 # Get an instance of a array for a vararg
1629 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1630
1631 # Code generation
1632
1633 # Add a line in the main part of the generated C
1634 fun add(s: String) do self.writer.lines.add(s)
1635
1636 # Add a line in the
1637 # (used for local or global declaration)
1638 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1639
1640 # Request the presence of a global declaration
1641 fun require_declaration(key: String)
1642 do
1643 var reqs = self.writer.file.required_declarations
1644 if reqs.has(key) then return
1645 reqs.add(key)
1646 var node = current_node
1647 if node != null then compiler.requirers_of_declarations[key] = node
1648 end
1649
1650 # Add a declaration in the local-header
1651 # The declaration is ensured to be present once
1652 fun declare_once(s: String)
1653 do
1654 self.compiler.provide_declaration(s, s)
1655 self.require_declaration(s)
1656 end
1657
1658 # Look for a needed .h and .c file for a given module
1659 # This is used for the legacy FFI
1660 fun add_extern(mmodule: MModule)
1661 do
1662 var file = mmodule.filepath
1663 file = file.strip_extension(".nit")
1664 var tryfile = file + ".nit.h"
1665 if tryfile.file_exists then
1666 self.declare_once("#include \"{tryfile.basename}\"")
1667 self.compiler.files_to_copy.add(tryfile)
1668 end
1669 tryfile = file + "_nit.h"
1670 if tryfile.file_exists then
1671 self.declare_once("#include \"{tryfile.basename}\"")
1672 self.compiler.files_to_copy.add(tryfile)
1673 end
1674
1675 if self.compiler.seen_extern.has(file) then return
1676 self.compiler.seen_extern.add(file)
1677 tryfile = file + ".nit.c"
1678 if not tryfile.file_exists then
1679 tryfile = file + "_nit.c"
1680 if not tryfile.file_exists then return
1681 end
1682 var f = new ExternCFile(tryfile.basename, "")
1683 self.compiler.extern_bodies.add(f)
1684 self.compiler.files_to_copy.add(tryfile)
1685 end
1686
1687 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1688 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1689 do
1690 var res = new_var(mtype)
1691 self.add("{res} = {cexpr};")
1692 return res
1693 end
1694
1695 # Generate generic abort
1696 # used by aborts, asserts, casts, etc.
1697 fun add_abort(message: String)
1698 do
1699 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1700 add_raw_abort
1701 end
1702
1703 fun add_raw_abort
1704 do
1705 if self.current_node != null and self.current_node.location.file != null and
1706 self.current_node.location.file.mmodule != null then
1707 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1708 self.require_declaration(f)
1709 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1710 else
1711 self.add("PRINT_ERROR(\"\\n\");")
1712 end
1713 self.add("fatal_exit(1);")
1714 end
1715
1716 # Add a dynamic cast
1717 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1718 do
1719 var res = self.type_test(value, mtype, tag)
1720 self.add("if (unlikely(!{res})) \{")
1721 var cn = self.class_name_string(value)
1722 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1723 self.add_raw_abort
1724 self.add("\}")
1725 end
1726
1727 # Generate a return with the value `s`
1728 fun ret(s: RuntimeVariable)
1729 do
1730 self.assign(self.frame.returnvar.as(not null), s)
1731 self.add("goto {self.frame.returnlabel.as(not null)};")
1732 end
1733
1734 # Compile a statement (if any)
1735 fun stmt(nexpr: nullable AExpr)
1736 do
1737 if nexpr == null then return
1738 if nexpr.is_broken then
1739 # Untyped expression.
1740 # Might mean dead code or invalid code
1741 # so aborts
1742 add_abort("FATAL: bad statement executed.")
1743 return
1744 end
1745
1746 var narray = nexpr.comprehension
1747 if narray != null then
1748 var recv = frame.comprehension.as(not null)
1749 var val = expr(nexpr, narray.element_mtype)
1750 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1751 return
1752 end
1753
1754 var old = self.current_node
1755 self.current_node = nexpr
1756 nexpr.stmt(self)
1757 self.current_node = old
1758 end
1759
1760 # Compile an expression an return its result
1761 # `mtype` is the expected return type, pass null if no specific type is expected.
1762 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1763 do
1764 var old = self.current_node
1765 self.current_node = nexpr
1766
1767 var res = null
1768 if nexpr.mtype != null then
1769 res = nexpr.expr(self)
1770 end
1771
1772 if res == null then
1773 # Untyped expression.
1774 # Might mean dead code or invalid code.
1775 # so aborts
1776 add_abort("FATAL: bad expression executed.")
1777 # and return a placebo result to please the C compiler
1778 if mtype == null then mtype = compiler.mainmodule.object_type
1779 res = new_var(mtype)
1780
1781 self.current_node = old
1782 return res
1783 end
1784
1785 if mtype != null then
1786 mtype = self.anchor(mtype)
1787 res = self.autobox(res, mtype)
1788 end
1789 res = autoadapt(res, nexpr.mtype.as(not null))
1790 var implicit_cast_to = nexpr.implicit_cast_to
1791 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1792 add_cast(res, implicit_cast_to, "auto")
1793 res = autoadapt(res, implicit_cast_to)
1794 end
1795 self.current_node = old
1796 return res
1797 end
1798
1799 # Alias for `self.expr(nexpr, self.bool_type)`
1800 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1801
1802 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1803 fun debug(message: String)
1804 do
1805 var node = self.current_node
1806 if node == null then
1807 print "?: {message}"
1808 else
1809 node.debug(message)
1810 end
1811 self.add("/* DEBUG: {message} */")
1812 end
1813 end
1814
1815 # A C function associated to a Nit method
1816 # Because of customization, a given Nit method can be compiler more that once
1817 abstract class AbstractRuntimeFunction
1818
1819 type COMPILER: AbstractCompiler
1820 type VISITOR: AbstractCompilerVisitor
1821
1822 # The associated Nit method
1823 var mmethoddef: MMethodDef
1824
1825 # The mangled c name of the runtime_function
1826 # Subclasses should redefine `build_c_name` instead
1827 fun c_name: String
1828 do
1829 var res = self.c_name_cache
1830 if res != null then return res
1831 res = self.build_c_name
1832 self.c_name_cache = res
1833 return res
1834 end
1835
1836 # Non cached version of `c_name`
1837 protected fun build_c_name: String is abstract
1838
1839 protected var c_name_cache: nullable String = null is writable
1840
1841 # Implements a call of the runtime_function
1842 # May inline the body or generate a C function call
1843 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1844
1845 # Generate the code for the `AbstractRuntimeFunction`
1846 # Warning: compile more than once compilation makes CC unhappy
1847 fun compile_to_c(compiler: COMPILER) is abstract
1848 end
1849
1850 # A runtime variable hold a runtime value in C.
1851 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1852 #
1853 # 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.
1854 class RuntimeVariable
1855 # The name of the variable in the C code
1856 var name: String
1857
1858 # The static type of the variable (as declard in C)
1859 var mtype: MType
1860
1861 # The current casted type of the variable (as known in Nit)
1862 var mcasttype: MType is writable
1863
1864 # If the variable exaclty a mcasttype?
1865 # false (usual value) means that the variable is a mcasttype or a subtype.
1866 var is_exact: Bool = false is writable
1867
1868 init
1869 do
1870 assert not mtype.need_anchor
1871 assert not mcasttype.need_anchor
1872 end
1873
1874 redef fun to_s do return name
1875
1876 redef fun inspect
1877 do
1878 var exact_str
1879 if self.is_exact then
1880 exact_str = " exact"
1881 else
1882 exact_str = ""
1883 end
1884 var type_str
1885 if self.mtype == self.mcasttype then
1886 type_str = "{mtype}{exact_str}"
1887 else
1888 type_str = "{mtype}({mcasttype}{exact_str})"
1889 end
1890 return "<{name}:{type_str}>"
1891 end
1892 end
1893
1894 # The static context of a visited property in a `AbstractCompilerVisitor`
1895 class StaticFrame
1896
1897 type VISITOR: AbstractCompilerVisitor
1898
1899 # The associated visitor
1900 var visitor: VISITOR
1901
1902 # The executed property.
1903 # A Method in case of a call, an attribute in case of a default initialization.
1904 var mpropdef: MPropDef
1905
1906 # The static type of the receiver
1907 var receiver: MClassType
1908
1909 # Arguments of the method (the first is the receiver)
1910 var arguments: Array[RuntimeVariable]
1911
1912 # The runtime_variable associated to the return (in a function)
1913 var returnvar: nullable RuntimeVariable = null is writable
1914
1915 # The label at the end of the property
1916 var returnlabel: nullable String = null is writable
1917
1918 # Labels associated to a each escapemarks.
1919 # Because of inlinings, escape-marks must be associated to their context (the frame)
1920 private var escapemark_names = new HashMap[EscapeMark, String]
1921
1922 # The array comprehension currently filled, if any
1923 private var comprehension: nullable RuntimeVariable = null
1924 end
1925
1926 redef class MType
1927 # Return the C type associated to a given Nit static type
1928 fun ctype: String do return "val*"
1929
1930 # C type outside of the compiler code and in boxes
1931 fun ctype_extern: String do return "val*"
1932
1933 # Short name of the `ctype` to use in unions
1934 fun ctypename: String do return "val"
1935
1936 # Is the associated C type a primitive one?
1937 #
1938 # ENSURE `result == (ctype != "val*")`
1939 fun is_c_primitive: Bool do return false
1940 end
1941
1942 redef class MClassType
1943
1944 redef var ctype is lazy do
1945 if mclass.name == "Int" then
1946 return "long"
1947 else if mclass.name == "Bool" then
1948 return "short int"
1949 else if mclass.name == "Char" then
1950 return "uint32_t"
1951 else if mclass.name == "Float" then
1952 return "double"
1953 else if mclass.name == "Int8" then
1954 return "int8_t"
1955 else if mclass.name == "Byte" then
1956 return "unsigned char"
1957 else if mclass.name == "Int16" then
1958 return "int16_t"
1959 else if mclass.name == "UInt16" then
1960 return "uint16_t"
1961 else if mclass.name == "Int32" then
1962 return "int32_t"
1963 else if mclass.name == "UInt32" then
1964 return "uint32_t"
1965 else if mclass.name == "NativeString" then
1966 return "char*"
1967 else if mclass.name == "NativeArray" then
1968 return "val*"
1969 else
1970 return "val*"
1971 end
1972 end
1973
1974 redef var is_c_primitive is lazy do return ctype != "val*"
1975
1976 redef fun ctype_extern: String
1977 do
1978 if mclass.kind == extern_kind then
1979 return "void*"
1980 else
1981 return ctype
1982 end
1983 end
1984
1985 redef fun ctypename: String
1986 do
1987 if mclass.name == "Int" then
1988 return "l"
1989 else if mclass.name == "Bool" then
1990 return "s"
1991 else if mclass.name == "Char" then
1992 return "c"
1993 else if mclass.name == "Float" then
1994 return "d"
1995 else if mclass.name == "Int8" then
1996 return "i8"
1997 else if mclass.name == "Byte" then
1998 return "b"
1999 else if mclass.name == "Int16" then
2000 return "i16"
2001 else if mclass.name == "UInt16" then
2002 return "u16"
2003 else if mclass.name == "Int32" then
2004 return "i32"
2005 else if mclass.name == "UInt32" then
2006 return "u32"
2007 else if mclass.name == "NativeString" then
2008 return "str"
2009 else if mclass.name == "NativeArray" then
2010 #return "{self.arguments.first.ctype}*"
2011 return "val"
2012 else
2013 return "val"
2014 end
2015 end
2016 end
2017
2018 redef class MPropDef
2019 type VISITOR: AbstractCompilerVisitor
2020 end
2021
2022 redef class MMethodDef
2023 # Can the body be inlined?
2024 fun can_inline(v: VISITOR): Bool
2025 do
2026 if is_abstract then return true
2027 if constant_value != null then return true
2028 var modelbuilder = v.compiler.modelbuilder
2029 var node = modelbuilder.mpropdef2node(self)
2030 if node isa APropdef then
2031 return node.can_inline
2032 else if node isa AClassdef then
2033 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2034 return true
2035 else if node == null then
2036 return true
2037 else
2038 abort
2039 end
2040 end
2041
2042 # Inline the body in another visitor
2043 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2044 do
2045 var modelbuilder = v.compiler.modelbuilder
2046 var val = constant_value
2047 var node = modelbuilder.mpropdef2node(self)
2048
2049 if is_abstract then
2050 var cn = v.class_name_string(arguments.first)
2051 v.current_node = node
2052 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2053 v.add_raw_abort
2054 return null
2055 end
2056
2057 if node isa APropdef then
2058 var oldnode = v.current_node
2059 v.current_node = node
2060 self.compile_parameter_check(v, arguments)
2061 node.compile_to_c(v, self, arguments)
2062 v.current_node = oldnode
2063 else if node isa AClassdef then
2064 var oldnode = v.current_node
2065 v.current_node = node
2066 self.compile_parameter_check(v, arguments)
2067 node.compile_to_c(v, self, arguments)
2068 v.current_node = oldnode
2069 else if val != null then
2070 v.ret(v.value_instance(val))
2071 else
2072 abort
2073 end
2074 return null
2075 end
2076
2077 # Generate type checks in the C code to check covariant parameters
2078 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2079 do
2080 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2081
2082 var msignature = self.msignature.as(not null)
2083
2084 for i in [0..msignature.arity[ do
2085 var mp = msignature.mparameters[i]
2086 # skip test for vararg since the array is instantiated with the correct polymorphic type
2087 if mp.is_vararg then continue
2088
2089 # skip if the cast is not required
2090 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2091 if not origmtype.need_anchor then continue
2092
2093 # get the parameter type
2094 var mtype = mp.mtype
2095
2096 # generate the cast
2097 # note that v decides if and how to implements the cast
2098 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2099 v.add_cast(arguments[i+1], mtype, "covariance")
2100 end
2101 end
2102 end
2103
2104 # Node visit
2105
2106 redef class APropdef
2107 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2108 do
2109 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2110 debug("Not yet implemented")
2111 end
2112
2113 fun can_inline: Bool do return true
2114 end
2115
2116 redef class AMethPropdef
2117 redef fun compile_to_c(v, mpropdef, arguments)
2118 do
2119 # Call the implicit super-init
2120 var auto_super_inits = self.auto_super_inits
2121 if auto_super_inits != null then
2122 var args = [arguments.first]
2123 for auto_super_init in auto_super_inits do
2124 assert auto_super_init.mproperty != mpropdef.mproperty
2125 args.clear
2126 for i in [0..auto_super_init.msignature.arity+1[ do
2127 args.add(arguments[i])
2128 end
2129 assert auto_super_init.mproperty != mpropdef.mproperty
2130 v.compile_callsite(auto_super_init, args)
2131 end
2132 end
2133 if auto_super_call then
2134 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2135 end
2136
2137 # Try special compilation
2138 if mpropdef.is_intern then
2139 if compile_intern_to_c(v, mpropdef, arguments) then return
2140 end
2141 if mpropdef.is_extern then
2142 if mpropdef.mproperty.is_init then
2143 if compile_externinit_to_c(v, mpropdef, arguments) then return
2144 else
2145 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2146 end
2147 end
2148
2149 # Compile block if any
2150 var n_block = n_block
2151 if n_block != null then
2152 for i in [0..mpropdef.msignature.arity[ do
2153 var variable = self.n_signature.n_params[i].variable.as(not null)
2154 v.assign(v.variable(variable), arguments[i+1])
2155 end
2156 v.stmt(n_block)
2157 return
2158 end
2159
2160 # We have a problem
2161 var cn = v.class_name_string(arguments.first)
2162 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2163 v.add_raw_abort
2164 end
2165
2166 redef fun can_inline
2167 do
2168 if self.auto_super_inits != null then return false
2169 var nblock = self.n_block
2170 if nblock == null then return true
2171 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2172 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2173 return false
2174 end
2175
2176 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2177 do
2178 var pname = mpropdef.mproperty.name
2179 var cname = mpropdef.mclassdef.mclass.name
2180 var ret = mpropdef.msignature.return_mtype
2181 if ret != null then
2182 ret = v.resolve_for(ret, arguments.first)
2183 end
2184 if pname != "==" and pname != "!=" then
2185 v.adapt_signature(mpropdef, arguments)
2186 v.unbox_signature_extern(mpropdef, arguments)
2187 end
2188 if cname == "Int" then
2189 if pname == "output" then
2190 v.add("printf(\"%ld\\n\", {arguments.first});")
2191 return true
2192 else if pname == "object_id" then
2193 v.ret(arguments.first)
2194 return true
2195 else if pname == "+" then
2196 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2197 return true
2198 else if pname == "-" then
2199 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2200 return true
2201 else if pname == "unary -" then
2202 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2203 return true
2204 else if pname == "unary +" then
2205 v.ret(arguments[0])
2206 return true
2207 else if pname == "*" then
2208 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2209 return true
2210 else if pname == "/" then
2211 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2212 return true
2213 else if pname == "%" then
2214 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2215 return true
2216 else if pname == "==" then
2217 v.ret(v.equal_test(arguments[0], arguments[1]))
2218 return true
2219 else if pname == "!=" then
2220 var res = v.equal_test(arguments[0], arguments[1])
2221 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2228 return true
2229 else if pname == "<=" then
2230 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2231 return true
2232 else if pname == ">=" then
2233 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2234 return true
2235 else if pname == "to_i8" then
2236 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2237 return true
2238 else if pname == "to_i16" then
2239 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2240 return true
2241 else if pname == "to_u16" then
2242 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2243 return true
2244 else if pname == "to_i32" then
2245 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2246 return true
2247 else if pname == "to_u32" then
2248 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2249 return true
2250 else if pname == "to_f" then
2251 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2252 return true
2253 else if pname == "to_b" then
2254 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2255 return true
2256 else if pname == "code_point" then
2257 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2258 return true
2259 else if pname == "&" then
2260 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2261 return true
2262 else if pname == "|" then
2263 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2264 return true
2265 else if pname == ">>" then
2266 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2267 return true
2268 else if pname == "<<" then
2269 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2270 return true
2271 end
2272 else if cname == "Char" then
2273 if pname == "object_id" then
2274 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2275 return true
2276 else if pname == "successor" then
2277 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2278 return true
2279 else if pname == "predecessor" then
2280 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2281 return true
2282 else if pname == "==" then
2283 v.ret(v.equal_test(arguments[0], arguments[1]))
2284 return true
2285 else if pname == "!=" then
2286 var res = v.equal_test(arguments[0], arguments[1])
2287 v.ret(v.new_expr("!{res}", ret.as(not null)))
2288 return true
2289 else if pname == "<" 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2294 return true
2295 else if pname == "<=" then
2296 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2297 return true
2298 else if pname == ">=" then
2299 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2300 return true
2301 else if pname == "to_i" then
2302 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2303 return true
2304 else if pname == "code_point" then
2305 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2306 return true
2307 end
2308 else if cname == "Byte" then
2309 if pname == "output" then
2310 v.add("printf(\"%x\\n\", {arguments.first});")
2311 return true
2312 else if pname == "object_id" then
2313 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2314 return true
2315 else if pname == "+" then
2316 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2317 return true
2318 else if pname == "-" then
2319 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2320 return true
2321 else if pname == "unary -" then
2322 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2323 return true
2324 else if pname == "unary +" then
2325 v.ret(arguments[0])
2326 return true
2327 else if pname == "*" then
2328 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2329 return true
2330 else if pname == "/" then
2331 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2332 return true
2333 else if pname == "%" then
2334 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2335 return true
2336 else if pname == "==" then
2337 v.ret(v.equal_test(arguments[0], arguments[1]))
2338 return true
2339 else if pname == "!=" then
2340 var res = v.equal_test(arguments[0], arguments[1])
2341 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2348 return true
2349 else if pname == "<=" then
2350 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2351 return true
2352 else if pname == ">=" then
2353 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2354 return true
2355 else if pname == ">>" then
2356 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2357 return true
2358 else if pname == "<<" then
2359 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2360 return true
2361 else if pname == "&" then
2362 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2363 return true
2364 else if pname == "to_i" then
2365 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2366 return true
2367 else if pname == "to_f" then
2368 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2369 return true
2370 else if pname == "to_i8" then
2371 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2372 return true
2373 else if pname == "to_i16" then
2374 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2375 return true
2376 else if pname == "to_u16" then
2377 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2378 return true
2379 else if pname == "to_i32" then
2380 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2381 return true
2382 else if pname == "to_u32" then
2383 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2384 return true
2385 else if pname == "ascii" then
2386 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2387 return true
2388 end
2389 else if cname == "Bool" then
2390 if pname == "output" then
2391 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2392 return true
2393 else if pname == "object_id" then
2394 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2395 return true
2396 else if pname == "==" then
2397 v.ret(v.equal_test(arguments[0], arguments[1]))
2398 return true
2399 else if pname == "!=" then
2400 var res = v.equal_test(arguments[0], arguments[1])
2401 v.ret(v.new_expr("!{res}", ret.as(not null)))
2402 return true
2403 end
2404 else if cname == "Float" then
2405 if pname == "output" then
2406 v.add("printf(\"%f\\n\", {arguments.first});")
2407 return true
2408 else if pname == "object_id" then
2409 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2410 return true
2411 else if pname == "+" then
2412 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2413 return true
2414 else if pname == "-" then
2415 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2416 return true
2417 else if pname == "unary -" then
2418 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2419 return true
2420 else if pname == "unary +" then
2421 v.ret(arguments[0])
2422 return true
2423 else if pname == "succ" then
2424 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2425 return true
2426 else if pname == "prec" then
2427 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2428 return true
2429 else if pname == "*" then
2430 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2431 return true
2432 else if pname == "/" then
2433 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2434 return true
2435 else if pname == "==" then
2436 v.ret(v.equal_test(arguments[0], arguments[1]))
2437 return true
2438 else if pname == "!=" then
2439 var res = v.equal_test(arguments[0], arguments[1])
2440 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2447 return true
2448 else if pname == "<=" then
2449 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2450 return true
2451 else if pname == ">=" then
2452 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2453 return true
2454 else if pname == "to_i" then
2455 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2456 return true
2457 else if pname == "to_b" then
2458 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2459 return true
2460 else if pname == "to_i8" then
2461 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2462 return true
2463 else if pname == "to_i16" then
2464 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2465 return true
2466 else if pname == "to_u16" then
2467 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2468 return true
2469 else if pname == "to_i32" then
2470 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2471 return true
2472 else if pname == "to_u32" then
2473 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2474 return true
2475 end
2476 else if cname == "NativeString" then
2477 if pname == "[]" then
2478 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2479 return true
2480 else if pname == "[]=" then
2481 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2482 return true
2483 else if pname == "copy_to" then
2484 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2485 return true
2486 else if pname == "atoi" then
2487 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2488 return true
2489 else if pname == "fast_cstring" then
2490 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2491 return true
2492 else if pname == "==" then
2493 v.ret(v.equal_test(arguments[0], arguments[1]))
2494 return true
2495 else if pname == "!=" then
2496 var res = v.equal_test(arguments[0], arguments[1])
2497 v.ret(v.new_expr("!{res}", ret.as(not null)))
2498 return true
2499 else if pname == "new" then
2500 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2501 return true
2502 else if pname == "fetch_4_chars" then
2503 v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
2504 return true
2505 else if pname == "fetch_4_hchars" then
2506 v.ret(v.new_expr("(long)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
2507 return true
2508 end
2509 else if cname == "NativeArray" then
2510 return v.native_array_def(pname, ret, arguments)
2511 else if cname == "Int8" then
2512 if pname == "output" then
2513 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2514 return true
2515 else if pname == "object_id" then
2516 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2517 return true
2518 else if pname == "+" then
2519 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2520 return true
2521 else if pname == "-" then
2522 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2523 return true
2524 else if pname == "unary -" then
2525 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2526 return true
2527 else if pname == "unary +" then
2528 v.ret(arguments[0])
2529 return true
2530 else if pname == "*" then
2531 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2532 return true
2533 else if pname == "/" then
2534 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2535 return true
2536 else if pname == "%" then
2537 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2538 return true
2539 else if pname == "<<" then
2540 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2541 return true
2542 else if pname == ">>" then
2543 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2544 return true
2545 else if pname == "==" then
2546 v.ret(v.equal_test(arguments[0], arguments[1]))
2547 return true
2548 else if pname == "!=" then
2549 var res = v.equal_test(arguments[0], arguments[1])
2550 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2557 return true
2558 else if pname == "<=" then
2559 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2560 return true
2561 else if pname == ">=" then
2562 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2563 return true
2564 else if pname == "to_i" then
2565 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2566 return true
2567 else if pname == "to_b" then
2568 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2569 return true
2570 else if pname == "to_i16" then
2571 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2572 return true
2573 else if pname == "to_u16" then
2574 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2575 return true
2576 else if pname == "to_i32" then
2577 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2578 return true
2579 else if pname == "to_u32" then
2580 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2581 return true
2582 else if pname == "to_f" then
2583 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2584 return true
2585 else if pname == "&" then
2586 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2587 return true
2588 else if pname == "|" then
2589 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2590 return true
2591 else if pname == "^" then
2592 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2593 return true
2594 else if pname == "unary ~" then
2595 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2596 return true
2597 end
2598 else if cname == "Int16" then
2599 if pname == "output" then
2600 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2601 return true
2602 else if pname == "object_id" then
2603 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2604 return true
2605 else if pname == "+" then
2606 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2607 return true
2608 else if pname == "-" then
2609 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2610 return true
2611 else if pname == "unary -" then
2612 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2613 return true
2614 else if pname == "unary +" then
2615 v.ret(arguments[0])
2616 return true
2617 else if pname == "*" then
2618 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2619 return true
2620 else if pname == "/" then
2621 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2622 return true
2623 else if pname == "%" then
2624 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2625 return true
2626 else if pname == "<<" then
2627 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2628 return true
2629 else if pname == ">>" then
2630 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2631 return true
2632 else if pname == "==" then
2633 v.ret(v.equal_test(arguments[0], arguments[1]))
2634 return true
2635 else if pname == "!=" then
2636 var res = v.equal_test(arguments[0], arguments[1])
2637 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2644 return true
2645 else if pname == "<=" then
2646 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2647 return true
2648 else if pname == ">=" then
2649 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2650 return true
2651 else if pname == "to_i" then
2652 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2653 return true
2654 else if pname == "to_b" then
2655 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2656 return true
2657 else if pname == "to_i8" then
2658 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2659 return true
2660 else if pname == "to_u16" then
2661 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2662 return true
2663 else if pname == "to_i32" then
2664 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2665 return true
2666 else if pname == "to_u32" then
2667 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2668 return true
2669 else if pname == "to_f" then
2670 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2671 return true
2672 else if pname == "&" then
2673 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2674 return true
2675 else if pname == "|" then
2676 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2677 return true
2678 else if pname == "^" then
2679 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2680 return true
2681 else if pname == "unary ~" then
2682 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2683 return true
2684 end
2685 else if cname == "UInt16" then
2686 if pname == "output" then
2687 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2688 return true
2689 else if pname == "object_id" then
2690 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2691 return true
2692 else if pname == "+" then
2693 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2694 return true
2695 else if pname == "-" then
2696 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2697 return true
2698 else if pname == "unary -" then
2699 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2700 return true
2701 else if pname == "unary +" then
2702 v.ret(arguments[0])
2703 return true
2704 else if pname == "*" then
2705 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2706 return true
2707 else if pname == "/" then
2708 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2709 return true
2710 else if pname == "%" then
2711 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2712 return true
2713 else if pname == "<<" then
2714 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2715 return true
2716 else if pname == ">>" then
2717 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2718 return true
2719 else if pname == "==" then
2720 v.ret(v.equal_test(arguments[0], arguments[1]))
2721 return true
2722 else if pname == "!=" then
2723 var res = v.equal_test(arguments[0], arguments[1])
2724 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2731 return true
2732 else if pname == "<=" then
2733 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2734 return true
2735 else if pname == ">=" then
2736 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2737 return true
2738 else if pname == "to_i" then
2739 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2740 return true
2741 else if pname == "to_b" then
2742 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2743 return true
2744 else if pname == "to_i8" then
2745 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2746 return true
2747 else if pname == "to_i16" then
2748 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2749 return true
2750 else if pname == "to_i32" then
2751 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2752 return true
2753 else if pname == "to_u32" then
2754 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2755 return true
2756 else if pname == "to_f" then
2757 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2758 return true
2759 else if pname == "&" then
2760 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2761 return true
2762 else if pname == "|" then
2763 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2764 return true
2765 else if pname == "^" then
2766 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2767 return true
2768 else if pname == "unary ~" then
2769 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2770 return true
2771 end
2772 else if cname == "Int32" then
2773 if pname == "output" then
2774 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2775 return true
2776 else if pname == "object_id" then
2777 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2778 return true
2779 else if pname == "+" then
2780 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2781 return true
2782 else if pname == "-" then
2783 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2784 return true
2785 else if pname == "unary -" then
2786 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2787 return true
2788 else if pname == "unary +" then
2789 v.ret(arguments[0])
2790 return true
2791 else if pname == "*" then
2792 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2793 return true
2794 else if pname == "/" then
2795 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2796 return true
2797 else if pname == "%" then
2798 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2799 return true
2800 else if pname == "<<" then
2801 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2802 return true
2803 else if pname == ">>" then
2804 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2805 return true
2806 else if pname == "==" then
2807 v.ret(v.equal_test(arguments[0], arguments[1]))
2808 return true
2809 else if pname == "!=" then
2810 var res = v.equal_test(arguments[0], arguments[1])
2811 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2818 return true
2819 else if pname == "<=" then
2820 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2821 return true
2822 else if pname == ">=" then
2823 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2824 return true
2825 else if pname == "to_i" then
2826 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2827 return true
2828 else if pname == "to_b" then
2829 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2830 return true
2831 else if pname == "to_i8" then
2832 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2833 return true
2834 else if pname == "to_i16" then
2835 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2836 return true
2837 else if pname == "to_u16" then
2838 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2839 return true
2840 else if pname == "to_u32" then
2841 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2842 return true
2843 else if pname == "to_f" then
2844 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2845 return true
2846 else if pname == "&" then
2847 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2848 return true
2849 else if pname == "|" then
2850 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2851 return true
2852 else if pname == "^" then
2853 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2854 return true
2855 else if pname == "unary ~" then
2856 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2857 return true
2858 end
2859 else if cname == "UInt32" then
2860 if pname == "output" then
2861 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2862 return true
2863 else if pname == "object_id" then
2864 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2865 return true
2866 else if pname == "+" then
2867 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2868 return true
2869 else if pname == "-" then
2870 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2871 return true
2872 else if pname == "unary -" then
2873 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2874 return true
2875 else if pname == "unary +" then
2876 v.ret(arguments[0])
2877 return true
2878 else if pname == "*" then
2879 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2880 return true
2881 else if pname == "/" then
2882 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2883 return true
2884 else if pname == "%" then
2885 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2886 return true
2887 else if pname == "<<" then
2888 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2889 return true
2890 else if pname == ">>" then
2891 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2892 return true
2893 else if pname == "==" then
2894 v.ret(v.equal_test(arguments[0], arguments[1]))
2895 return true
2896 else if pname == "!=" then
2897 var res = v.equal_test(arguments[0], arguments[1])
2898 v.ret(v.new_expr("!{res}", 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.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2905 return true
2906 else if pname == "<=" then
2907 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2908 return true
2909 else if pname == ">=" then
2910 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2911 return true
2912 else if pname == "to_i" then
2913 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2914 return true
2915 else if pname == "to_b" then
2916 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2917 return true
2918 else if pname == "to_i8" then
2919 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2920 return true
2921 else if pname == "to_i16" then
2922 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2923 return true
2924 else if pname == "to_u16" then
2925 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2926 return true
2927 else if pname == "to_i32" then
2928 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2929 return true
2930 else if pname == "to_f" then
2931 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2932 return true
2933 else if pname == "&" then
2934 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2935 return true
2936 else if pname == "|" then
2937 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2938 return true
2939 else if pname == "^" then
2940 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2941 return true
2942 else if pname == "unary ~" then
2943 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2944 return true
2945 end
2946 end
2947 if pname == "exit" then
2948 v.add("exit({arguments[1]});")
2949 return true
2950 else if pname == "sys" then
2951 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2952 return true
2953 else if pname == "calloc_string" then
2954 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2955 return true
2956 else if pname == "calloc_array" then
2957 v.calloc_array(ret.as(not null), arguments)
2958 return true
2959 else if pname == "object_id" then
2960 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2961 return true
2962 else if pname == "is_same_type" then
2963 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2964 return true
2965 else if pname == "is_same_instance" then
2966 v.ret(v.equal_test(arguments[0], arguments[1]))
2967 return true
2968 else if pname == "output_class_name" then
2969 var nat = v.class_name_string(arguments.first)
2970 v.add("printf(\"%s\\n\", {nat});")
2971 return true
2972 else if pname == "native_class_name" then
2973 var nat = v.class_name_string(arguments.first)
2974 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2975 return true
2976 else if pname == "force_garbage_collection" then
2977 v.add("nit_gcollect();")
2978 return true
2979 else if pname == "native_argc" then
2980 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2981 return true
2982 else if pname == "native_argv" then
2983 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2984 return true
2985 end
2986 return false
2987 end
2988
2989 # Compile an extern method
2990 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2991 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2992 do
2993 var externname
2994 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2995 if at != null and at.n_args.length == 1 then
2996 externname = at.arg_as_string(v.compiler.modelbuilder)
2997 if externname == null then return false
2998 else
2999 return false
3000 end
3001 v.add_extern(mpropdef.mclassdef.mmodule)
3002 var res: nullable RuntimeVariable = null
3003 var ret = mpropdef.msignature.return_mtype
3004 if ret != null then
3005 ret = v.resolve_for(ret, arguments.first)
3006 res = v.new_var_extern(ret)
3007 end
3008 v.adapt_signature(mpropdef, arguments)
3009 v.unbox_signature_extern(mpropdef, arguments)
3010
3011 if res == null then
3012 v.add("{externname}({arguments.join(", ")});")
3013 else
3014 v.add("{res} = {externname}({arguments.join(", ")});")
3015 res = v.box_extern(res, ret.as(not null))
3016 v.ret(res)
3017 end
3018 return true
3019 end
3020
3021 # Compile an extern factory
3022 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3023 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3024 do
3025 var externname
3026 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3027 if at != null then
3028 externname = at.arg_as_string(v.compiler.modelbuilder)
3029 if externname == null then return false
3030 else
3031 return false
3032 end
3033 v.add_extern(mpropdef.mclassdef.mmodule)
3034 v.adapt_signature(mpropdef, arguments)
3035 v.unbox_signature_extern(mpropdef, arguments)
3036 var ret = arguments.first.mtype
3037 var res = v.new_var_extern(ret)
3038
3039 arguments.shift
3040
3041 v.add("{res} = {externname}({arguments.join(", ")});")
3042 res = v.box_extern(res, ret)
3043 v.ret(res)
3044 return true
3045 end
3046 end
3047
3048 redef class AAttrPropdef
3049 redef fun can_inline: Bool do return not is_lazy
3050
3051 redef fun compile_to_c(v, mpropdef, arguments)
3052 do
3053 if mpropdef == mreadpropdef then
3054 assert arguments.length == 1
3055 var recv = arguments.first
3056 var res
3057 if is_lazy then
3058 var set
3059 var ret = self.mtype
3060 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3061 var guard = self.mlazypropdef.mproperty
3062 if useiset then
3063 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3064 else
3065 set = v.read_attribute(guard, recv)
3066 end
3067 v.add("if(likely({set})) \{")
3068 res = v.read_attribute(self.mpropdef.mproperty, recv)
3069 v.add("\} else \{")
3070
3071 var value = evaluate_expr(v, recv)
3072
3073 v.assign(res, value)
3074 if not useiset then
3075 var true_v = v.bool_instance(true)
3076 v.write_attribute(guard, arguments.first, true_v)
3077 end
3078 v.add("\}")
3079 else
3080 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3081 end
3082 v.assign(v.frame.returnvar.as(not null), res)
3083 else if mpropdef == mwritepropdef then
3084 assert arguments.length == 2
3085 var recv = arguments.first
3086 var arg = arguments[1]
3087 if is_optional and v.maybenull(arg) then
3088 var value = v.new_var(self.mpropdef.static_mtype.as(not null))
3089 v.add("if ({arg} == NULL) \{")
3090 v.assign(value, evaluate_expr(v, recv))
3091 v.add("\} else \{")
3092 v.assign(value, arg)
3093 v.add("\}")
3094 arg = value
3095 end
3096 v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
3097 if is_lazy then
3098 var ret = self.mtype
3099 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3100 if not useiset then
3101 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3102 end
3103 end
3104 else
3105 abort
3106 end
3107 end
3108
3109 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3110 do
3111 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3112 end
3113
3114 # Evaluate, store and return the default value of the attribute
3115 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3116 do
3117 var oldnode = v.current_node
3118 v.current_node = self
3119 var old_frame = v.frame
3120 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3121 v.frame = frame
3122
3123 var value
3124 var mtype = self.mtype
3125 assert mtype != null
3126
3127 var nexpr = self.n_expr
3128 var nblock = self.n_block
3129 if nexpr != null then
3130 value = v.expr(nexpr, mtype)
3131 else if nblock != null then
3132 value = v.new_var(mtype)
3133 frame.returnvar = value
3134 frame.returnlabel = v.get_name("RET_LABEL")
3135 v.add("\{")
3136 v.stmt(nblock)
3137 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3138 v.add("\}")
3139 else
3140 abort
3141 end
3142
3143 v.write_attribute(self.mpropdef.mproperty, recv, value)
3144
3145 v.frame = old_frame
3146 v.current_node = oldnode
3147
3148 return value
3149 end
3150
3151 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3152 do
3153 var nexpr = self.n_expr
3154 if nexpr != null then return
3155
3156 var oldnode = v.current_node
3157 v.current_node = self
3158 var old_frame = v.frame
3159 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3160 v.frame = frame
3161 # Force read to check the initialization
3162 v.read_attribute(self.mpropdef.mproperty, recv)
3163 v.frame = old_frame
3164 v.current_node = oldnode
3165 end
3166 end
3167
3168 redef class AClassdef
3169 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3170 do
3171 if mpropdef.mproperty.is_root_init then
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 if mclassdef.auto_init == mpropdef then
3178 var recv = arguments.first
3179 var initializers = mpropdef.initializers
3180 var no_init = false
3181 if not initializers.is_empty then
3182
3183 var i = 1
3184 for p in initializers do
3185 if p isa MMethod then
3186 var args = [recv]
3187 for x in p.intro.msignature.mparameters do
3188 args.add arguments[i]
3189 i += 1
3190 end
3191 v.send(p, args)
3192 if p.intro.is_calling_init then no_init = true
3193 else if p isa MAttribute then
3194 v.write_attribute(p, recv, arguments[i])
3195 i += 1
3196 else abort
3197 end
3198 assert i == arguments.length
3199
3200 end
3201 if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
3202 return
3203 else
3204 abort
3205 end
3206 end
3207 end
3208
3209 redef class AExpr
3210 # Try to compile self as an expression
3211 # Do not call this method directly, use `v.expr` instead
3212 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3213 do
3214 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3215 var mtype = self.mtype
3216 if mtype == null then
3217 return null
3218 else
3219 var res = v.new_var(mtype)
3220 v.add("/* {res} = NOT YET {class_name} */")
3221 return res
3222 end
3223 end
3224
3225 # Try to compile self as a statement
3226 # Do not call this method directly, use `v.stmt` instead
3227 private fun stmt(v: AbstractCompilerVisitor)
3228 do
3229 expr(v)
3230 end
3231 end
3232
3233 redef class ABlockExpr
3234 redef fun stmt(v)
3235 do
3236 for e in self.n_expr do v.stmt(e)
3237 end
3238 redef fun expr(v)
3239 do
3240 var last = self.n_expr.last
3241 for e in self.n_expr do
3242 if e == last then break
3243 v.stmt(e)
3244 end
3245 return v.expr(last, null)
3246 end
3247 end
3248
3249 redef class AVardeclExpr
3250 redef fun stmt(v)
3251 do
3252 var variable = self.variable.as(not null)
3253 var ne = self.n_expr
3254 if ne != null then
3255 var i = v.expr(ne, variable.declared_type)
3256 v.assign(v.variable(variable), i)
3257 end
3258 end
3259 end
3260
3261 redef class AVarExpr
3262 redef fun expr(v)
3263 do
3264 var res = v.variable(self.variable.as(not null))
3265 var mtype = self.mtype.as(not null)
3266 return v.autoadapt(res, mtype)
3267 end
3268 end
3269
3270 redef class AVarAssignExpr
3271 redef fun expr(v)
3272 do
3273 var variable = self.variable.as(not null)
3274 var i = v.expr(self.n_value, variable.declared_type)
3275 v.assign(v.variable(variable), i)
3276 return i
3277 end
3278 end
3279
3280 redef class AVarReassignExpr
3281 redef fun stmt(v)
3282 do
3283 var variable = self.variable.as(not null)
3284 var vari = v.variable(variable)
3285 var value = v.expr(self.n_value, variable.declared_type)
3286 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3287 assert res != null
3288 v.assign(v.variable(variable), res)
3289 end
3290 end
3291
3292 redef class ASelfExpr
3293 redef fun expr(v) do return v.frame.arguments.first
3294 end
3295
3296 redef class AImplicitSelfExpr
3297 redef fun expr(v) do
3298 if not is_sys then return super
3299 return v.new_expr("glob_sys", mtype.as(not null))
3300 end
3301 end
3302
3303 redef class AEscapeExpr
3304 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3305 end
3306
3307 redef class AReturnExpr
3308 redef fun stmt(v)
3309 do
3310 var nexpr = self.n_expr
3311 if nexpr != null then
3312 var returnvar = v.frame.returnvar.as(not null)
3313 var i = v.expr(nexpr, returnvar.mtype)
3314 v.assign(returnvar, i)
3315 end
3316 v.add("goto {v.frame.returnlabel.as(not null)};")
3317 end
3318 end
3319
3320 redef class AAbortExpr
3321 redef fun stmt(v) do v.add_abort("Aborted")
3322 end
3323
3324 redef class AIfExpr
3325 redef fun stmt(v)
3326 do
3327 var cond = v.expr_bool(self.n_expr)
3328 v.add("if ({cond})\{")
3329 v.stmt(self.n_then)
3330 v.add("\} else \{")
3331 v.stmt(self.n_else)
3332 v.add("\}")
3333 end
3334
3335 redef fun expr(v)
3336 do
3337 var res = v.new_var(self.mtype.as(not null))
3338 var cond = v.expr_bool(self.n_expr)
3339 v.add("if ({cond})\{")
3340 v.assign(res, v.expr(self.n_then.as(not null), null))
3341 v.add("\} else \{")
3342 v.assign(res, v.expr(self.n_else.as(not null), null))
3343 v.add("\}")
3344 return res
3345 end
3346 end
3347
3348 redef class AIfexprExpr
3349 redef fun expr(v)
3350 do
3351 var res = v.new_var(self.mtype.as(not null))
3352 var cond = v.expr_bool(self.n_expr)
3353 v.add("if ({cond})\{")
3354 v.assign(res, v.expr(self.n_then, null))
3355 v.add("\} else \{")
3356 v.assign(res, v.expr(self.n_else, null))
3357 v.add("\}")
3358 return res
3359 end
3360 end
3361
3362 redef class ADoExpr
3363 redef fun stmt(v)
3364 do
3365 v.stmt(self.n_block)
3366 v.add_escape_label(break_mark)
3367 end
3368 end
3369
3370 redef class AWhileExpr
3371 redef fun stmt(v)
3372 do
3373 v.add("for(;;) \{")
3374 var cond = v.expr_bool(self.n_expr)
3375 v.add("if (!{cond}) break;")
3376 v.stmt(self.n_block)
3377 v.add_escape_label(continue_mark)
3378 v.add("\}")
3379 v.add_escape_label(break_mark)
3380 end
3381 end
3382
3383 redef class ALoopExpr
3384 redef fun stmt(v)
3385 do
3386 v.add("for(;;) \{")
3387 v.stmt(self.n_block)
3388 v.add_escape_label(continue_mark)
3389 v.add("\}")
3390 v.add_escape_label(break_mark)
3391 end
3392 end
3393
3394 redef class AForExpr
3395 redef fun stmt(v)
3396 do
3397 for g in n_groups do
3398 var cl = v.expr(g.n_expr, null)
3399 var it_meth = g.method_iterator
3400 assert it_meth != null
3401 var it = v.compile_callsite(it_meth, [cl])
3402 assert it != null
3403 g.it = it
3404 end
3405 v.add("for(;;) \{")
3406 for g in n_groups do
3407 var it = g.it
3408 var isok_meth = g.method_is_ok
3409 assert isok_meth != null
3410 var ok = v.compile_callsite(isok_meth, [it])
3411 assert ok != null
3412 v.add("if(!{ok}) break;")
3413 if g.variables.length == 1 then
3414 var item_meth = g.method_item
3415 assert item_meth != null
3416 var i = v.compile_callsite(item_meth, [it])
3417 assert i != null
3418 v.assign(v.variable(g.variables.first), i)
3419 else if g.variables.length == 2 then
3420 var key_meth = g.method_key
3421 assert key_meth != null
3422 var i = v.compile_callsite(key_meth, [it])
3423 assert i != null
3424 v.assign(v.variable(g.variables[0]), i)
3425 var item_meth = g.method_item
3426 assert item_meth != null
3427 i = v.compile_callsite(item_meth, [it])
3428 assert i != null
3429 v.assign(v.variable(g.variables[1]), i)
3430 else
3431 abort
3432 end
3433 end
3434 v.stmt(self.n_block)
3435 v.add_escape_label(continue_mark)
3436 for g in n_groups do
3437 var next_meth = g.method_next
3438 assert next_meth != null
3439 v.compile_callsite(next_meth, [g.it])
3440 end
3441 v.add("\}")
3442 v.add_escape_label(break_mark)
3443
3444 for g in n_groups do
3445 var method_finish = g.method_finish
3446 if method_finish != null then
3447 # TODO: Find a way to call this also in long escape (e.g. return)
3448 v.compile_callsite(method_finish, [g.it])
3449 end
3450 end
3451 end
3452 end
3453
3454 redef class AForGroup
3455 # C variable representing the iterator
3456 private var it: RuntimeVariable is noinit
3457 end
3458
3459 redef class AAssertExpr
3460 redef fun stmt(v)
3461 do
3462 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3463
3464 var cond = v.expr_bool(self.n_expr)
3465 v.add("if (unlikely(!{cond})) \{")
3466 v.stmt(self.n_else)
3467 var nid = self.n_id
3468 if nid != null then
3469 v.add_abort("Assert '{nid.text}' failed")
3470 else
3471 v.add_abort("Assert failed")
3472 end
3473 v.add("\}")
3474 end
3475 end
3476
3477 redef class AOrExpr
3478 redef fun expr(v)
3479 do
3480 var res = v.new_var(self.mtype.as(not null))
3481 var i1 = v.expr_bool(self.n_expr)
3482 v.add("if ({i1}) \{")
3483 v.add("{res} = 1;")
3484 v.add("\} else \{")
3485 var i2 = v.expr_bool(self.n_expr2)
3486 v.add("{res} = {i2};")
3487 v.add("\}")
3488 return res
3489 end
3490 end
3491
3492 redef class AImpliesExpr
3493 redef fun expr(v)
3494 do
3495 var res = v.new_var(self.mtype.as(not null))
3496 var i1 = v.expr_bool(self.n_expr)
3497 v.add("if (!{i1}) \{")
3498 v.add("{res} = 1;")
3499 v.add("\} else \{")
3500 var i2 = v.expr_bool(self.n_expr2)
3501 v.add("{res} = {i2};")
3502 v.add("\}")
3503 return res
3504 end
3505 end
3506
3507 redef class AAndExpr
3508 redef fun expr(v)
3509 do
3510 var res = v.new_var(self.mtype.as(not null))
3511 var i1 = v.expr_bool(self.n_expr)
3512 v.add("if (!{i1}) \{")
3513 v.add("{res} = 0;")
3514 v.add("\} else \{")
3515 var i2 = v.expr_bool(self.n_expr2)
3516 v.add("{res} = {i2};")
3517 v.add("\}")
3518 return res
3519 end
3520 end
3521
3522 redef class ANotExpr
3523 redef fun expr(v)
3524 do
3525 var cond = v.expr_bool(self.n_expr)
3526 return v.new_expr("!{cond}", self.mtype.as(not null))
3527 end
3528 end
3529
3530 redef class AOrElseExpr
3531 redef fun expr(v)
3532 do
3533 var res = v.new_var(self.mtype.as(not null))
3534 var i1 = v.expr(self.n_expr, null)
3535
3536 if not v.maybenull(i1) then return i1
3537
3538 v.add("if ({i1}!=NULL) \{")
3539 v.assign(res, i1)
3540 v.add("\} else \{")
3541 var i2 = v.expr(self.n_expr2, null)
3542 v.assign(res, i2)
3543 v.add("\}")
3544 return res
3545 end
3546 end
3547
3548 redef class AIntegerExpr
3549 redef fun expr(v) do
3550 if value isa Int then return v.int_instance(value.as(Int))
3551 if value isa Byte then return v.byte_instance(value.as(Byte))
3552 if value isa Int8 then return v.int8_instance(value.as(Int8))
3553 if value isa Int16 then return v.int16_instance(value.as(Int16))
3554 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3555 if value isa Int32 then return v.int32_instance(value.as(Int32))
3556 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3557 # Should never happen
3558 abort
3559 end
3560 end
3561
3562 redef class AFloatExpr
3563 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3564 end
3565
3566 redef class ACharExpr
3567 redef fun expr(v) do
3568 if is_ascii then return v.byte_instance(value.as(not null).ascii)
3569 if is_code_point then return v.int_instance(value.as(not null).code_point)
3570 return v.char_instance(self.value.as(not null))
3571 end
3572 end
3573
3574 redef class AArrayExpr
3575 redef fun expr(v)
3576 do
3577 var mtype = self.element_mtype.as(not null)
3578 var array = new Array[RuntimeVariable]
3579 var res = v.array_instance(array, mtype)
3580
3581 var old_comprehension = v.frame.comprehension
3582 v.frame.comprehension = res
3583 for nexpr in self.n_exprs do
3584 v.stmt(nexpr)
3585 end
3586 v.frame.comprehension = old_comprehension
3587
3588 return res
3589 end
3590 end
3591
3592 redef class AStringFormExpr
3593 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3594 end
3595
3596 redef class ASuperstringExpr
3597 redef fun expr(v)
3598 do
3599 var type_string = mtype.as(not null)
3600
3601 # Collect elements of the superstring
3602 var array = new Array[AExpr]
3603 for ne in self.n_exprs do
3604 # Drop literal empty string.
3605 # They appears in things like "{a}" that is ["", a, ""]
3606 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3607 array.add(ne)
3608 end
3609
3610 # Store the allocated native array in a static variable
3611 # For reusing later
3612 var varonce = v.get_name("varonce")
3613 v.add("if (unlikely({varonce}==NULL)) \{")
3614
3615 # The native array that will contains the elements to_s-ized.
3616 # For fast concatenation.
3617 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3618
3619 v.add_decl("static {a.mtype.ctype} {varonce};")
3620
3621 # Pre-fill the array with the literal string parts.
3622 # So they do not need to be filled again when reused
3623 for i in [0..array.length[ do
3624 var ne = array[i]
3625 if not ne isa AStringFormExpr then continue
3626 var e = v.expr(ne, null)
3627 v.native_array_set(a, i, e)
3628 end
3629
3630 v.add("\} else \{")
3631 # Take the native-array from the store.
3632 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3633 # WARNING: not thread safe! (FIXME?)
3634 v.add("{a} = {varonce};")
3635 v.add("{varonce} = NULL;")
3636 v.add("\}")
3637
3638 # Stringify the elements and put them in the native array
3639 var to_s_method = v.get_property("to_s", v.object_type)
3640 for i in [0..array.length[ do
3641 var ne = array[i]
3642 if ne isa AStringFormExpr then continue
3643 var e = v.expr(ne, null)
3644 # Skip the `to_s` if the element is already a String
3645 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3646 e = v.send(to_s_method, [e]).as(not null)
3647 end
3648 v.native_array_set(a, i, e)
3649 end
3650
3651 # Fast join the native string to get the result
3652 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3653
3654 # We finish to work with the native array,
3655 # so store it so that it can be reused
3656 v.add("{varonce} = {a};")
3657 return res
3658 end
3659 end
3660
3661 redef class ACrangeExpr
3662 redef fun expr(v)
3663 do
3664 var i1 = v.expr(self.n_expr, null)
3665 var i2 = v.expr(self.n_expr2, null)
3666 var mtype = self.mtype.as(MClassType)
3667 var res = v.init_instance(mtype)
3668 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3669 return res
3670 end
3671 end
3672
3673 redef class AOrangeExpr
3674 redef fun expr(v)
3675 do
3676 var i1 = v.expr(self.n_expr, null)
3677 var i2 = v.expr(self.n_expr2, null)
3678 var mtype = self.mtype.as(MClassType)
3679 var res = v.init_instance(mtype)
3680 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3681 return res
3682 end
3683 end
3684
3685 redef class ATrueExpr
3686 redef fun expr(v) do return v.bool_instance(true)
3687 end
3688
3689 redef class AFalseExpr
3690 redef fun expr(v) do return v.bool_instance(false)
3691 end
3692
3693 redef class ANullExpr
3694 redef fun expr(v) do return v.null_instance
3695 end
3696
3697 redef class AIsaExpr
3698 redef fun expr(v)
3699 do
3700 var i = v.expr(self.n_expr, null)
3701 var cast_type = self.cast_type
3702 if cast_type == null then return null # no-no on broken node
3703 return v.type_test(i, cast_type, "isa")
3704 end
3705 end
3706
3707 redef class AAsCastExpr
3708 redef fun expr(v)
3709 do
3710 var i = v.expr(self.n_expr, null)
3711 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3712
3713 v.add_cast(i, self.mtype.as(not null), "as")
3714 return i
3715 end
3716 end
3717
3718 redef class AAsNotnullExpr
3719 redef fun expr(v)
3720 do
3721 var i = v.expr(self.n_expr, null)
3722 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3723
3724 if not v.maybenull(i) then return i
3725
3726 v.add("if (unlikely({i} == NULL)) \{")
3727 v.add_abort("Cast failed")
3728 v.add("\}")
3729 return i
3730 end
3731 end
3732
3733 redef class AParExpr
3734 redef fun expr(v) do return v.expr(self.n_expr, null)
3735 end
3736
3737 redef class AOnceExpr
3738 redef fun expr(v)
3739 do
3740 var mtype = self.mtype.as(not null)
3741 var name = v.get_name("varonce")
3742 var guard = v.get_name(name + "_guard")
3743 v.add_decl("static {mtype.ctype} {name};")
3744 v.add_decl("static int {guard};")
3745 var res = v.new_var(mtype)
3746 v.add("if (likely({guard})) \{")
3747 v.add("{res} = {name};")
3748 v.add("\} else \{")
3749 var i = v.expr(self.n_expr, mtype)
3750 v.add("{res} = {i};")
3751 v.add("{name} = {res};")
3752 v.add("{guard} = 1;")
3753 v.add("\}")
3754 return res
3755 end
3756 end
3757
3758 redef class ASendExpr
3759 redef fun expr(v)
3760 do
3761 var recv = v.expr(self.n_expr, null)
3762 var callsite = self.callsite.as(not null)
3763 if callsite.is_broken then return null
3764 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3765 return v.compile_callsite(callsite, args)
3766 end
3767 end
3768
3769 redef class ASendReassignFormExpr
3770 redef fun stmt(v)
3771 do
3772 var recv = v.expr(self.n_expr, null)
3773 var callsite = self.callsite.as(not null)
3774 if callsite.is_broken then return
3775 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3776
3777 var value = v.expr(self.n_value, null)
3778
3779 var left = v.compile_callsite(callsite, args)
3780 assert left != null
3781
3782 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3783 assert res != null
3784
3785 args.add(res)
3786 v.compile_callsite(self.write_callsite.as(not null), args)
3787 end
3788 end
3789
3790 redef class ASuperExpr
3791 redef fun expr(v)
3792 do
3793 var frame = v.frame.as(not null)
3794 var recv = frame.arguments.first
3795
3796 var callsite = self.callsite
3797 if callsite != null then
3798 if callsite.is_broken then return null
3799 var args
3800
3801 if self.n_args.n_exprs.is_empty then
3802 # Add automatic arguments for the super init call
3803 args = [recv]
3804 for i in [0..callsite.msignature.arity[ do
3805 args.add(frame.arguments[i+1])
3806 end
3807 else
3808 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3809 end
3810
3811 # Super init call
3812 var res = v.compile_callsite(callsite, args)
3813 return res
3814 end
3815
3816 var mpropdef = self.mpropdef.as(not null)
3817
3818 var args
3819 if self.n_args.n_exprs.is_empty then
3820 args = frame.arguments
3821 else
3822 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3823 end
3824
3825 # Standard call-next-method
3826 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3827 end
3828 end
3829
3830 redef class ANewExpr
3831 redef fun expr(v)
3832 do
3833 var mtype = self.recvtype
3834 assert mtype != null
3835
3836 if mtype.mclass.name == "NativeArray" then
3837 assert self.n_args.n_exprs.length == 1
3838 var l = v.expr(self.n_args.n_exprs.first, null)
3839 assert mtype isa MGenericType
3840 var elttype = mtype.arguments.first
3841 return v.native_array_instance(elttype, l)
3842 end
3843
3844 var recv = v.init_instance_or_extern(mtype)
3845
3846 var callsite = self.callsite
3847 if callsite == null then return recv
3848 if callsite.is_broken then return null
3849
3850 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3851 var res2 = v.compile_callsite(callsite, args)
3852 if res2 != null then
3853 #self.debug("got {res2} from {mproperty}. drop {recv}")
3854 return res2
3855 end
3856 return recv
3857 end
3858 end
3859
3860 redef class AAttrExpr
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.read_attribute(mproperty, recv)
3866 end
3867 end
3868
3869 redef class AAttrAssignExpr
3870 redef fun expr(v)
3871 do
3872 var recv = v.expr(self.n_expr, null)
3873 var i = v.expr(self.n_value, null)
3874 var mproperty = self.mproperty.as(not null)
3875 v.write_attribute(mproperty, recv, i)
3876 return i
3877 end
3878 end
3879
3880 redef class AAttrReassignExpr
3881 redef fun stmt(v)
3882 do
3883 var recv = v.expr(self.n_expr, null)
3884 var value = v.expr(self.n_value, null)
3885 var mproperty = self.mproperty.as(not null)
3886 var attr = v.read_attribute(mproperty, recv)
3887 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3888 assert res != null
3889 v.write_attribute(mproperty, recv, res)
3890 end
3891 end
3892
3893 redef class AIssetAttrExpr
3894 redef fun expr(v)
3895 do
3896 var recv = v.expr(self.n_expr, null)
3897 var mproperty = self.mproperty.as(not null)
3898 return v.isset_attribute(mproperty, recv)
3899 end
3900 end
3901
3902 redef class AVarargExpr
3903 redef fun expr(v)
3904 do
3905 return v.expr(self.n_expr, null)
3906 end
3907 end
3908
3909 redef class ANamedargExpr
3910 redef fun expr(v)
3911 do
3912 return v.expr(self.n_expr, null)
3913 end
3914 end
3915
3916 redef class ADebugTypeExpr
3917 redef fun stmt(v)
3918 do
3919 # do nothing
3920 end
3921 end
3922
3923 # Utils
3924
3925 redef class Array[E]
3926 # Return a new `Array` with the elements only contened in self and not in `o`
3927 fun -(o: Array[E]): Array[E] do
3928 var res = new Array[E]
3929 for e in self do if not o.has(e) then res.add(e)
3930 return res
3931 end
3932 end
3933
3934 redef class MModule
3935 # All `MProperty` associated to all `MClassDef` of `mclass`
3936 fun properties(mclass: MClass): Set[MProperty] do
3937 if not self.properties_cache.has_key(mclass) then
3938 var properties = new HashSet[MProperty]
3939 var parents = new Array[MClass]
3940 if self.flatten_mclass_hierarchy.has(mclass) then
3941 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3942 end
3943 for parent in parents do
3944 properties.add_all(self.properties(parent))
3945 end
3946 for mclassdef in mclass.mclassdefs do
3947 if not self.in_importation <= mclassdef.mmodule then continue
3948 for mprop in mclassdef.intro_mproperties do
3949 properties.add(mprop)
3950 end
3951 end
3952 self.properties_cache[mclass] = properties
3953 end
3954 return properties_cache[mclass]
3955 end
3956 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3957
3958 # Write FFI and nitni results to file
3959 fun finalize_ffi(c: AbstractCompiler) do end
3960
3961 # Give requided addinional system libraries (as given to LD_LIBS)
3962 # Note: can return null instead of an empty set
3963 fun collect_linker_libs: nullable Array[String] do return null
3964 end
3965
3966 # Create a tool context to handle options and paths
3967 var toolcontext = new ToolContext
3968
3969 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3970
3971 # We do not add other options, so process them now!
3972 toolcontext.process_options(args)
3973
3974 # We need a model to collect stufs
3975 var model = new Model
3976 # An a model builder to parse files
3977 var modelbuilder = new ModelBuilder(model, toolcontext)
3978
3979 var arguments = toolcontext.option_context.rest
3980 if arguments.length > 1 and toolcontext.opt_output.value != null then
3981 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3982 exit 1
3983 end
3984
3985 # Here we load an process all modules passed on the command line
3986 var mmodules = modelbuilder.parse(arguments)
3987
3988 if mmodules.is_empty then toolcontext.quit
3989
3990 modelbuilder.run_phases
3991
3992 for mmodule in mmodules do
3993 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3994 var ms = [mmodule]
3995 toolcontext.run_global_phases(ms)
3996 end