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