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