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