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