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