nitc: use Array instead of List
[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 Array[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 # longjmp !
655 self.header.add_decl("#include <setjmp.h>\n")
656 self.header.add_decl("#include <sys/types.h>\n")
657 self.header.add_decl("#include <unistd.h>\n")
658 self.header.add_decl("#include <stdint.h>\n")
659 self.header.add_decl("#ifdef __linux__")
660 self.header.add_decl(" #include <endian.h>")
661 self.header.add_decl("#endif")
662 self.header.add_decl("#include <inttypes.h>\n")
663 self.header.add_decl("#include \"gc_chooser.h\"")
664 self.header.add_decl("#ifdef __APPLE__")
665 self.header.add_decl(" #include <libkern/OSByteOrder.h>")
666 self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
667 self.header.add_decl("#endif")
668 self.header.add_decl("#ifdef __pnacl__")
669 self.header.add_decl(" #define be16toh(val) (((val) >> 8) | ((val) << 8))")
670 self.header.add_decl(" #define be32toh(val) ((be16toh((val) << 16) | (be16toh((val) >> 16))))")
671 self.header.add_decl("#endif")
672 self.header.add_decl("#ifdef ANDROID")
673 self.header.add_decl(" #ifndef be32toh")
674 self.header.add_decl(" #define be32toh(val) betoh32(val)")
675 self.header.add_decl(" #endif")
676 self.header.add_decl(" #include <android/log.h>")
677 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
678 self.header.add_decl("#else")
679 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
680 self.header.add_decl("#endif")
681
682 compile_header_structs
683 compile_nitni_structs
684 compile_catch_stack
685
686 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
687 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
688 # Signal handler function prototype
689 self.header.add_decl("void fatal_exit(int);")
690 else
691 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
692 end
693
694 if gccd_disable.has("likely") or gccd_disable.has("all") then
695 self.header.add_decl("#define likely(x) (x)")
696 self.header.add_decl("#define unlikely(x) (x)")
697 else if gccd_disable.has("correct-likely") then
698 # invert the `likely` definition
699 # Used by masochists to bench the worst case
700 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
701 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
702 else
703 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
704 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
705 end
706
707 # Global variable used by intern methods
708 self.header.add_decl("extern int glob_argc;")
709 self.header.add_decl("extern char **glob_argv;")
710 self.header.add_decl("extern val *glob_sys;")
711 end
712
713 # Stack stocking environment for longjumps
714 protected fun compile_catch_stack do
715 self.header.add_decl """
716 struct catch_stack_t {
717 int cursor;
718 jmp_buf envs[100];
719 };
720 extern struct catch_stack_t catchStack;
721 """
722 end
723
724 # Declaration of structures for live Nit types
725 protected fun compile_header_structs is abstract
726
727 # Declaration of structures for nitni undelying the FFI
728 protected fun compile_nitni_structs
729 do
730 self.header.add_decl """
731 /* Native reference to Nit objects */
732 /* This structure is used to represent every Nit type in extern methods and custom C code. */
733 struct nitni_ref {
734 struct nitni_ref *next,
735 *prev; /* adjacent global references in global list */
736 int count; /* number of time this global reference has been marked */
737 };
738
739 /* List of global references from C code to Nit objects */
740 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
741 struct nitni_global_ref_list_t {
742 struct nitni_ref *head, *tail;
743 };
744 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
745
746 /* Initializer of global reference list */
747 extern void nitni_global_ref_list_init();
748
749 /* Intern function to add a global reference to the list */
750 extern void nitni_global_ref_add( struct nitni_ref *ref );
751
752 /* Intern function to remove a global reference from the list */
753 extern void nitni_global_ref_remove( struct nitni_ref *ref );
754
755 /* Increase count on an existing global reference */
756 extern void nitni_global_ref_incr( struct nitni_ref *ref );
757
758 /* Decrease count on an existing global reference */
759 extern void nitni_global_ref_decr( struct nitni_ref *ref );
760 """
761 end
762
763 fun compile_finalizer_function
764 do
765 var finalizable_type = mainmodule.finalizable_type
766 if finalizable_type == null then return
767
768 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
769
770 if finalize_meth == null then
771 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
772 return
773 end
774
775 var v = self.new_visitor
776 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
777 var recv = v.new_expr("obj", finalizable_type)
778 v.send(finalize_meth, [recv])
779 v.add "\}"
780 end
781
782 # Generate the main C function.
783 #
784 # This function:
785 #
786 # * allocate the Sys object if it exists
787 # * call init if is exists
788 # * call main if it exists
789 fun compile_main_function
790 do
791 var v = self.new_visitor
792 v.add_decl("#include <signal.h>")
793 var platform = target_platform
794
795 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
796
797 if platform.supports_libunwind then
798 v.add_decl("#ifndef NO_STACKTRACE")
799 v.add_decl("#define UNW_LOCAL_ONLY")
800 v.add_decl("#include <libunwind.h>")
801 v.add_decl("#include \"c_functions_hash.h\"")
802 v.add_decl("#endif")
803 end
804 v.add_decl("int glob_argc;")
805 v.add_decl("char **glob_argv;")
806 v.add_decl("val *glob_sys;")
807 v.add_decl("struct catch_stack_t catchStack;")
808
809 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
810 for tag in count_type_test_tags do
811 v.add_decl("long count_type_test_resolved_{tag};")
812 v.add_decl("long count_type_test_unresolved_{tag};")
813 v.add_decl("long count_type_test_skipped_{tag};")
814 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
815 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
816 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
817 end
818 end
819
820 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
821 v.add_decl("long count_invoke_by_tables;")
822 v.add_decl("long count_invoke_by_direct;")
823 v.add_decl("long count_invoke_by_inline;")
824 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
825 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
826 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
827 end
828
829 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
830 v.add_decl("long count_attr_reads = 0;")
831 v.add_decl("long count_isset_checks = 0;")
832 v.compiler.header.add_decl("extern long count_attr_reads;")
833 v.compiler.header.add_decl("extern long count_isset_checks;")
834 end
835
836 v.add_decl("static void show_backtrace(void) \{")
837 if platform.supports_libunwind then
838 v.add_decl("#ifndef NO_STACKTRACE")
839 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
840 v.add_decl("unw_cursor_t cursor;")
841 v.add_decl("if(opt==NULL)\{")
842 v.add_decl("unw_context_t uc;")
843 v.add_decl("unw_word_t ip;")
844 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
845 v.add_decl("unw_getcontext(&uc);")
846 v.add_decl("unw_init_local(&cursor, &uc);")
847 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
848 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
849 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
850 v.add_decl("while (unw_step(&cursor) > 0) \{")
851 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
852 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
853 v.add_decl(" if (recv != NULL)\{")
854 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
855 v.add_decl(" \}else\{")
856 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
857 v.add_decl(" \}")
858 v.add_decl("\}")
859 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
860 v.add_decl("free(procname);")
861 v.add_decl("\}")
862 v.add_decl("#endif /* NO_STACKTRACE */")
863 end
864 v.add_decl("\}")
865
866 v.add_decl("void sig_handler(int signo)\{")
867 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
868 v.add_decl("show_backtrace();")
869 # rethrows
870 v.add_decl("signal(signo, SIG_DFL);")
871 v.add_decl("kill(getpid(), signo);")
872 v.add_decl("\}")
873
874 v.add_decl("void fatal_exit(int status) \{")
875 v.add_decl("show_backtrace();")
876 v.add_decl("exit(status);")
877 v.add_decl("\}")
878
879 if no_main then
880 v.add_decl("int nit_main(int argc, char** argv) \{")
881 else
882 v.add_decl("int main(int argc, char** argv) \{")
883 end
884
885 v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
886 v.add("signal(SIGABRT, sig_handler);")
887 v.add("signal(SIGFPE, sig_handler);")
888 v.add("signal(SIGILL, sig_handler);")
889 v.add("signal(SIGINT, sig_handler);")
890 v.add("signal(SIGTERM, sig_handler);")
891 v.add("signal(SIGSEGV, sig_handler);")
892 v.add "#endif"
893 v.add("signal(SIGPIPE, SIG_IGN);")
894
895 v.add("glob_argc = argc; glob_argv = argv;")
896 v.add("catchStack.cursor = -1;")
897 v.add("initialize_gc_option();")
898
899 v.add "initialize_nitni_global_refs();"
900
901 var main_type = mainmodule.sys_type
902 if main_type != null then
903 var mainmodule = v.compiler.mainmodule
904 var glob_sys = v.init_instance(main_type)
905 v.add("glob_sys = {glob_sys};")
906 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
907 if main_init != null then
908 v.send(main_init, [glob_sys])
909 end
910 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
911 mainmodule.try_get_primitive_method("main", main_type.mclass)
912 if main_method != null then
913 v.send(main_method, [glob_sys])
914 end
915 end
916
917 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
918 v.add_decl("long count_type_test_resolved_total = 0;")
919 v.add_decl("long count_type_test_unresolved_total = 0;")
920 v.add_decl("long count_type_test_skipped_total = 0;")
921 v.add_decl("long count_type_test_total_total = 0;")
922 for tag in count_type_test_tags do
923 v.add_decl("long count_type_test_total_{tag};")
924 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
925 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
926 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
927 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
928 v.add("count_type_test_total_total += count_type_test_total_{tag};")
929 end
930 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
931 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
932 var tags = count_type_test_tags.to_a
933 tags.add("total")
934 for tag in tags do
935 v.add("printf(\"{tag}\");")
936 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
937 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
938 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
939 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
940 end
941 end
942
943 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
944 v.add_decl("long count_invoke_total;")
945 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
946 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
947 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
948 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
949 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
950 end
951
952 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
953 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
954 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
955 end
956
957 v.add("return 0;")
958 v.add("\}")
959
960 for m in mainmodule.in_importation.greaters do
961 var f = "FILE_"+m.c_name
962 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
963 provide_declaration(f, "extern const char {f}[];")
964 end
965 end
966
967 # Copile all C functions related to the [incr|decr]_ref features of the FFI
968 fun compile_nitni_global_ref_functions
969 do
970 var v = self.new_visitor
971 v.add """
972 struct nitni_global_ref_list_t *nitni_global_ref_list;
973 void initialize_nitni_global_refs() {
974 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
975 nitni_global_ref_list->head = NULL;
976 nitni_global_ref_list->tail = NULL;
977 }
978
979 void nitni_global_ref_add( struct nitni_ref *ref ) {
980 if ( nitni_global_ref_list->head == NULL ) {
981 nitni_global_ref_list->head = ref;
982 ref->prev = NULL;
983 } else {
984 nitni_global_ref_list->tail->next = ref;
985 ref->prev = nitni_global_ref_list->tail;
986 }
987 nitni_global_ref_list->tail = ref;
988
989 ref->next = NULL;
990 }
991
992 void nitni_global_ref_remove( struct nitni_ref *ref ) {
993 if ( ref->prev == NULL ) {
994 nitni_global_ref_list->head = ref->next;
995 } else {
996 ref->prev->next = ref->next;
997 }
998
999 if ( ref->next == NULL ) {
1000 nitni_global_ref_list->tail = ref->prev;
1001 } else {
1002 ref->next->prev = ref->prev;
1003 }
1004 }
1005
1006 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
1007 if ( ref->count == 0 ) /* not registered */
1008 {
1009 /* add to list */
1010 nitni_global_ref_add( ref );
1011 }
1012
1013 ref->count ++;
1014 }
1015
1016 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
1017 if ( ref->count == 1 ) /* was last reference */
1018 {
1019 /* remove from list */
1020 nitni_global_ref_remove( ref );
1021 }
1022
1023 ref->count --;
1024 }
1025 """
1026 end
1027
1028 # List of additional files required to compile (FFI)
1029 var extern_bodies = new Array[ExternFile]
1030
1031 # List of source files to copy over to the compile dir
1032 var files_to_copy = new Array[String]
1033
1034 # This is used to avoid adding an extern file more than once
1035 private var seen_extern = new ArraySet[String]
1036
1037 # Generate code that initialize the attributes on a new instance
1038 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1039 do
1040 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1041 self.mainmodule.linearize_mclassdefs(cds)
1042 for cd in cds do
1043 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1044 npropdef.init_expr(v, recv)
1045 end
1046 end
1047 end
1048
1049 # Generate code that check if an attribute is correctly initialized
1050 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1051 do
1052 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1053 self.mainmodule.linearize_mclassdefs(cds)
1054 for cd in cds do
1055 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1056 npropdef.check_expr(v, recv)
1057 end
1058 end
1059 end
1060
1061 # stats
1062
1063 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1064 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1065 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1066 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1067
1068 protected fun init_count_type_test_tags: HashMap[String, Int]
1069 do
1070 var res = new HashMap[String, Int]
1071 for tag in count_type_test_tags do
1072 res[tag] = 0
1073 end
1074 return res
1075 end
1076
1077 # Display stats about compilation process
1078 #
1079 # Metrics used:
1080 #
1081 # * type tests against resolved types (`x isa Collection[Animal]`)
1082 # * type tests against unresolved types (`x isa Collection[E]`)
1083 # * type tests skipped
1084 # * type tests total
1085 fun display_stats
1086 do
1087 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1088 print "# static count_type_test"
1089 print "\tresolved:\tunresolved\tskipped\ttotal"
1090 var count_type_test_total = init_count_type_test_tags
1091 count_type_test_resolved["total"] = 0
1092 count_type_test_unresolved["total"] = 0
1093 count_type_test_skipped["total"] = 0
1094 count_type_test_total["total"] = 0
1095 for tag in count_type_test_tags do
1096 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1097 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1098 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1099 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1100 count_type_test_total["total"] += count_type_test_total[tag]
1101 end
1102 var count_type_test = count_type_test_total["total"]
1103 var tags = count_type_test_tags.to_a
1104 tags.add("total")
1105 for tag in tags do
1106 printn tag
1107 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1108 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1109 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1110 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1111 print ""
1112 end
1113 end
1114 end
1115
1116 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1117 end
1118
1119 # A file unit (may be more than one file if
1120 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1121 class CodeFile
1122 var name: String
1123 var writers = new Array[CodeWriter]
1124 var required_declarations = new HashSet[String]
1125 end
1126
1127 # Where to store generated lines
1128 class CodeWriter
1129 var file: CodeFile
1130 var lines = new Array[String]
1131 var decl_lines = new Array[String]
1132
1133 # Add a line in the main part of the generated C
1134 fun add(s: String) do self.lines.add(s)
1135
1136 # Add a line in the
1137 # (used for local or global declaration)
1138 fun add_decl(s: String) do self.decl_lines.add(s)
1139
1140 init
1141 do
1142 file.writers.add(self)
1143 end
1144 end
1145
1146 # A visitor on the AST of property definition that generate the C code.
1147 abstract class AbstractCompilerVisitor
1148
1149 type COMPILER: AbstractCompiler
1150
1151 # The associated compiler
1152 var compiler: COMPILER
1153
1154 # The current visited AST node
1155 var current_node: nullable ANode = null is writable
1156
1157 # The current `StaticFrame`
1158 var frame: nullable StaticFrame = null is writable
1159
1160 # Alias for self.compiler.mainmodule.object_type
1161 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1162
1163 # Alias for self.compiler.mainmodule.bool_type
1164 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1165
1166 var writer: CodeWriter is noinit
1167
1168 init
1169 do
1170 self.writer = new CodeWriter(compiler.files.last)
1171 end
1172
1173 # Force to get the primitive property named `name` in the instance `recv` or abort
1174 fun get_property(name: String, recv: MType): MMethod
1175 do
1176 assert recv isa MClassType
1177 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1178 end
1179
1180 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1181 do
1182 if callsite.is_broken then return null
1183 var initializers = callsite.mpropdef.initializers
1184 if not initializers.is_empty then
1185 var recv = arguments.first
1186
1187 var i = 1
1188 for p in initializers do
1189 if p isa MMethod then
1190 var args = [recv]
1191 for x in p.intro.msignature.mparameters do
1192 args.add arguments[i]
1193 i += 1
1194 end
1195 self.send(p, args)
1196 else if p isa MAttribute then
1197 self.write_attribute(p, recv, arguments[i])
1198 i += 1
1199 else abort
1200 end
1201 assert i == arguments.length
1202
1203 return self.send(callsite.mproperty, [recv])
1204 end
1205
1206 return self.send(callsite.mproperty, arguments)
1207 end
1208
1209 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1210
1211 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1212
1213 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
1214
1215 # Return an element of a native array.
1216 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1217 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1218
1219 # Store an element in a native array.
1220 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1221 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1222
1223 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1224 # This method is used to manage varargs in signatures and returns the real array
1225 # of runtime variables to use in the call.
1226 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1227 do
1228 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1229 var res = new Array[RuntimeVariable]
1230 res.add(recv)
1231
1232 if msignature.arity == 0 then return res
1233
1234 if map == null then
1235 assert args.length == msignature.arity
1236 for ne in args do
1237 res.add self.expr(ne, null)
1238 end
1239 return res
1240 end
1241
1242 # Eval in order of arguments, not parameters
1243 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1244 for ne in args do
1245 exprs.add self.expr(ne, null)
1246 end
1247
1248 # Fill `res` with the result of the evaluation according to the mapping
1249 for i in [0..msignature.arity[ do
1250 var param = msignature.mparameters[i]
1251 var j = map.map.get_or_null(i)
1252 if j == null then
1253 # default value
1254 res.add(null_instance)
1255 continue
1256 end
1257 if param.is_vararg and args[i].vararg_decl > 0 then
1258 var vararg = exprs.sub(j, args[i].vararg_decl)
1259 var elttype = param.mtype
1260 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1261 res.add(arg)
1262 continue
1263 end
1264 res.add exprs[j]
1265 end
1266 return res
1267 end
1268
1269 # Type handling
1270
1271 # Anchor a type to the main module and the current receiver
1272 fun anchor(mtype: MType): MType
1273 do
1274 if not mtype.need_anchor then return mtype
1275 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1276 end
1277
1278 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1279 do
1280 if not mtype.need_anchor then return mtype
1281 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1282 end
1283
1284 # Unsafely cast a value to a new type
1285 # ie the result share the same C variable but my have a different mcasttype
1286 # NOTE: if the adaptation is useless then `value` is returned as it.
1287 # ENSURE: `result.name == value.name`
1288 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1289 do
1290 mtype = self.anchor(mtype)
1291 var valmtype = value.mcasttype
1292
1293 # Do nothing if useless autocast
1294 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1295 return value
1296 end
1297
1298 # Just as_not_null if the target is not nullable.
1299 #
1300 # eg `nullable PreciseType` adapted to `Object` gives precisetype.
1301 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1302 mtype = valmtype.mtype
1303 end
1304
1305 var res = new RuntimeVariable(value.name, value.mtype, mtype)
1306 return res
1307 end
1308
1309 # Generate a super call from a method definition
1310 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1311
1312 # Adapt the arguments of a method according to targetted `MMethodDef`
1313 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1314
1315 # Unbox all the arguments of a method when implemented `extern` or `intern`
1316 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1317
1318 # Box or unbox a value to another type iff a C type conversion is needed
1319 # ENSURE: `result.mtype.ctype == mtype.ctype`
1320 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1321
1322 # Box extern classes to be used in the generated code
1323 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1324
1325 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1326 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1327
1328 # Generate a polymorphic subtype test
1329 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1330
1331 # Generate the code required to dynamically check if 2 objects share the same runtime type
1332 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1333
1334 # Generate a Nit "is" for two runtime_variables
1335 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1336
1337 # Sends
1338
1339 # Generate a static call on a method definition
1340 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1341
1342 # Generate a polymorphic send for the method `m` and the arguments `args`
1343 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1344
1345 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1346 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1347 do
1348 assert t isa MClassType
1349 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1350 return self.call(propdef, t, args)
1351 end
1352
1353 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1354 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1355 do
1356 assert t isa MClassType
1357 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1358 return self.call(m, t, args)
1359 end
1360
1361 # Attributes handling
1362
1363 # Generate a polymorphic attribute is_set test
1364 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1365
1366 # Generate a polymorphic attribute read
1367 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1368
1369 # Generate a polymorphic attribute write
1370 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1371
1372 # Checks
1373
1374 # Can value be null? (according to current knowledge)
1375 fun maybenull(value: RuntimeVariable): Bool
1376 do
1377 return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
1378 end
1379
1380 # Add a check and an abort for a null receiver if needed
1381 fun check_recv_notnull(recv: RuntimeVariable)
1382 do
1383 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1384
1385 if maybenull(recv) then
1386 self.add("if (unlikely({recv} == NULL)) \{")
1387 self.add_abort("Receiver is null")
1388 self.add("\}")
1389 end
1390 end
1391
1392 # Names handling
1393
1394 private var names = new HashSet[String]
1395 private var last: Int = 0
1396
1397 # Return a new name based on `s` and unique in the visitor
1398 fun get_name(s: String): String
1399 do
1400 if not self.names.has(s) then
1401 self.names.add(s)
1402 return s
1403 end
1404 var i = self.last + 1
1405 loop
1406 var s2 = s + i.to_s
1407 if not self.names.has(s2) then
1408 self.last = i
1409 self.names.add(s2)
1410 return s2
1411 end
1412 i = i + 1
1413 end
1414 end
1415
1416 # Return an unique and stable identifier associated with an escapemark
1417 fun escapemark_name(e: nullable EscapeMark): String
1418 do
1419 assert e != null
1420 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1421 var name = e.name
1422 if name == null then name = "label"
1423 name = get_name(name)
1424 frame.escapemark_names[e] = name
1425 return name
1426 end
1427
1428 # Insert a C label for associated with an escapemark
1429 fun add_escape_label(e: nullable EscapeMark)
1430 do
1431 if e == null then return
1432 if e.escapes.is_empty then return
1433 add("BREAK_{escapemark_name(e)}: (void)0;")
1434 end
1435
1436 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1437 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1438 fun class_name_string(value: RuntimeVariable): String is abstract
1439
1440 # Variables handling
1441
1442 protected var variables = new HashMap[Variable, RuntimeVariable]
1443
1444 # Return the local runtime_variable associated to a Nit local variable
1445 fun variable(variable: Variable): RuntimeVariable
1446 do
1447 if self.variables.has_key(variable) then
1448 return self.variables[variable]
1449 else
1450 var name = self.get_name("var_{variable.name}")
1451 var mtype = variable.declared_type.as(not null)
1452 mtype = self.anchor(mtype)
1453 var res = new RuntimeVariable(name, mtype, mtype)
1454 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1455 self.variables[variable] = res
1456 return res
1457 end
1458 end
1459
1460 # Return a new uninitialized local runtime_variable
1461 fun new_var(mtype: MType): RuntimeVariable
1462 do
1463 mtype = self.anchor(mtype)
1464 var name = self.get_name("var")
1465 var res = new RuntimeVariable(name, mtype, mtype)
1466 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1467 return res
1468 end
1469
1470 # The difference with `new_var` is the C static type of the local variable
1471 fun new_var_extern(mtype: MType): RuntimeVariable
1472 do
1473 mtype = self.anchor(mtype)
1474 var name = self.get_name("var")
1475 var res = new RuntimeVariable(name, mtype, mtype)
1476 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1477 return res
1478 end
1479
1480 # Return a new uninitialized named runtime_variable
1481 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1482 do
1483 mtype = self.anchor(mtype)
1484 var res = new RuntimeVariable(name, mtype, mtype)
1485 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1486 return res
1487 end
1488
1489 # Correctly assign a left and a right value
1490 # Boxing and unboxing is performed if required
1491 fun assign(left, right: RuntimeVariable)
1492 do
1493 right = self.autobox(right, left.mtype)
1494 self.add("{left} = {right};")
1495 end
1496
1497 # Generate instances
1498
1499 # Generate a alloc-instance + init-attributes
1500 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1501
1502 # Allocate and init attributes of an instance of a standard or extern class
1503 #
1504 # Does not support universals and the pseudo-internal `NativeArray` class.
1505 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1506 do
1507 var recv
1508 var ctype = mtype.ctype
1509 assert mtype.mclass.name != "NativeArray"
1510 if not mtype.is_c_primitive then
1511 recv = init_instance(mtype)
1512 else if ctype == "char*" then
1513 recv = new_expr("NULL/*special!*/", mtype)
1514 else
1515 recv = new_expr("({ctype})0/*special!*/", mtype)
1516 end
1517 return recv
1518 end
1519
1520 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1521 fun set_finalizer(recv: RuntimeVariable)
1522 do
1523 var mtype = recv.mtype
1524 var finalizable_type = compiler.mainmodule.finalizable_type
1525 if finalizable_type != null and not mtype.need_anchor and
1526 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1527 add "gc_register_finalizer({recv});"
1528 end
1529 end
1530
1531 # The currently processed module
1532 #
1533 # alias for `compiler.mainmodule`
1534 fun mmodule: MModule do return compiler.mainmodule
1535
1536 # Generate an integer value
1537 fun int_instance(value: Int): RuntimeVariable
1538 do
1539 var t = mmodule.int_type
1540 var res = new RuntimeVariable("{value.to_s}l", t, t)
1541 return res
1542 end
1543
1544 # Generate a byte value
1545 fun byte_instance(value: Byte): RuntimeVariable
1546 do
1547 var t = mmodule.byte_type
1548 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1549 return res
1550 end
1551
1552 # Generate an int8 value
1553 fun int8_instance(value: Int8): RuntimeVariable
1554 do
1555 var t = mmodule.int8_type
1556 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1557 return res
1558 end
1559
1560 # Generate an int16 value
1561 fun int16_instance(value: Int16): RuntimeVariable
1562 do
1563 var t = mmodule.int16_type
1564 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1565 return res
1566 end
1567
1568 # Generate a uint16 value
1569 fun uint16_instance(value: UInt16): RuntimeVariable
1570 do
1571 var t = mmodule.uint16_type
1572 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1573 return res
1574 end
1575
1576 # Generate an int32 value
1577 fun int32_instance(value: Int32): RuntimeVariable
1578 do
1579 var t = mmodule.int32_type
1580 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1581 return res
1582 end
1583
1584 # Generate a uint32 value
1585 fun uint32_instance(value: UInt32): RuntimeVariable
1586 do
1587 var t = mmodule.uint32_type
1588 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1589 return res
1590 end
1591
1592 # Generate a char value
1593 fun char_instance(value: Char): RuntimeVariable
1594 do
1595 var t = mmodule.char_type
1596
1597 if value.code_point < 128 then
1598 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1599 else
1600 return new RuntimeVariable("{value.code_point}", t, t)
1601 end
1602 end
1603
1604 # Generate a float value
1605 #
1606 # FIXME pass a Float, not a string
1607 fun float_instance(value: String): RuntimeVariable
1608 do
1609 var t = mmodule.float_type
1610 var res = new RuntimeVariable("{value}", t, t)
1611 return res
1612 end
1613
1614 # Generate an integer value
1615 fun bool_instance(value: Bool): RuntimeVariable
1616 do
1617 var s = if value then "1" else "0"
1618 var res = new RuntimeVariable(s, bool_type, bool_type)
1619 return res
1620 end
1621
1622 # Generate the `null` value
1623 fun null_instance: RuntimeVariable
1624 do
1625 var t = compiler.mainmodule.model.null_type
1626 var res = new RuntimeVariable("((val*)NULL)", t, t)
1627 return res
1628 end
1629
1630 # Generates a NativeString instance fully escaped in C-style \xHH fashion
1631 fun native_string_instance(ns: NativeString, len: Int): RuntimeVariable do
1632 var mtype = mmodule.native_string_type
1633 var nat = new_var(mtype)
1634 var byte_esc = new Buffer.with_cap(len * 4)
1635 for i in [0 .. len[ do
1636 byte_esc.append("\\x{ns[i].to_s.substring_from(2)}")
1637 end
1638 self.add("{nat} = \"{byte_esc}\";")
1639 return nat
1640 end
1641
1642 # Generate a string value
1643 fun string_instance(string: String): RuntimeVariable
1644 do
1645 var mtype = mmodule.string_type
1646 var name = self.get_name("varonce")
1647 self.add_decl("static {mtype.ctype} {name};")
1648 var res = self.new_var(mtype)
1649 self.add("if (likely({name}!=NULL)) \{")
1650 self.add("{res} = {name};")
1651 self.add("\} else \{")
1652 var native_mtype = mmodule.native_string_type
1653 var nat = self.new_var(native_mtype)
1654 self.add("{nat} = \"{string.escape_to_c}\";")
1655 var bytelen = self.int_instance(string.bytelen)
1656 var unilen = self.int_instance(string.length)
1657 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1658 self.add("{name} = {res};")
1659 self.add("\}")
1660 return res
1661 end
1662
1663 fun value_instance(object: Object): RuntimeVariable
1664 do
1665 if object isa Int then
1666 return int_instance(object)
1667 else if object isa Bool then
1668 return bool_instance(object)
1669 else if object isa String then
1670 return string_instance(object)
1671 else
1672 abort
1673 end
1674 end
1675
1676 # Generate an array value
1677 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1678
1679 # Get an instance of a array for a vararg
1680 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1681
1682 # Code generation
1683
1684 # Add a line in the main part of the generated C
1685 fun add(s: String) do self.writer.lines.add(s)
1686
1687 # Add a line in the
1688 # (used for local or global declaration)
1689 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1690
1691 # Request the presence of a global declaration
1692 fun require_declaration(key: String)
1693 do
1694 var reqs = self.writer.file.required_declarations
1695 if reqs.has(key) then return
1696 reqs.add(key)
1697 var node = current_node
1698 if node != null then compiler.requirers_of_declarations[key] = node
1699 end
1700
1701 # Add a declaration in the local-header
1702 # The declaration is ensured to be present once
1703 fun declare_once(s: String)
1704 do
1705 self.compiler.provide_declaration(s, s)
1706 self.require_declaration(s)
1707 end
1708
1709 # Look for a needed .h and .c file for a given module
1710 # This is used for the legacy FFI
1711 fun add_extern(mmodule: MModule)
1712 do
1713 var file = mmodule.filepath
1714 file = file.strip_extension(".nit")
1715 var tryfile = file + ".nit.h"
1716 if tryfile.file_exists then
1717 self.declare_once("#include \"{tryfile.basename}\"")
1718 self.compiler.files_to_copy.add(tryfile)
1719 end
1720 tryfile = file + "_nit.h"
1721 if tryfile.file_exists then
1722 self.declare_once("#include \"{tryfile.basename}\"")
1723 self.compiler.files_to_copy.add(tryfile)
1724 end
1725
1726 if self.compiler.seen_extern.has(file) then return
1727 self.compiler.seen_extern.add(file)
1728 tryfile = file + ".nit.c"
1729 if not tryfile.file_exists then
1730 tryfile = file + "_nit.c"
1731 if not tryfile.file_exists then return
1732 end
1733 var f = new ExternCFile(tryfile.basename, "")
1734 self.compiler.extern_bodies.add(f)
1735 self.compiler.files_to_copy.add(tryfile)
1736 end
1737
1738 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1739 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1740 do
1741 var res = new_var(mtype)
1742 self.add("{res} = {cexpr};")
1743 return res
1744 end
1745
1746 # Generate generic abort
1747 # used by aborts, asserts, casts, etc.
1748 fun add_abort(message: String)
1749 do
1750 self.add("if(catchStack.cursor >= 0)\{")
1751 self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
1752 self.add("\}")
1753 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1754 add_raw_abort
1755 end
1756
1757 fun add_raw_abort
1758 do
1759 if self.current_node != null and self.current_node.location.file != null and
1760 self.current_node.location.file.mmodule != null then
1761 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1762 self.require_declaration(f)
1763 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1764 else
1765 self.add("PRINT_ERROR(\"\\n\");")
1766 end
1767 self.add("fatal_exit(1);")
1768 end
1769
1770 # Add a dynamic cast
1771 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1772 do
1773 var res = self.type_test(value, mtype, tag)
1774 self.add("if (unlikely(!{res})) \{")
1775 var cn = self.class_name_string(value)
1776 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1777 self.add_raw_abort
1778 self.add("\}")
1779 end
1780
1781 # Generate a return with the value `s`
1782 fun ret(s: RuntimeVariable)
1783 do
1784 self.assign(self.frame.returnvar.as(not null), s)
1785 self.add("goto {self.frame.returnlabel.as(not null)};")
1786 end
1787
1788 # Compile a statement (if any)
1789 fun stmt(nexpr: nullable AExpr)
1790 do
1791 if nexpr == null then return
1792 if nexpr.is_broken then
1793 # Untyped expression.
1794 # Might mean dead code or invalid code
1795 # so aborts
1796 add_abort("FATAL: bad statement executed.")
1797 return
1798 end
1799
1800 var narray = nexpr.comprehension
1801 if narray != null then
1802 var recv = frame.comprehension.as(not null)
1803 var val = expr(nexpr, narray.element_mtype)
1804 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1805 return
1806 end
1807
1808 var old = self.current_node
1809 self.current_node = nexpr
1810 nexpr.stmt(self)
1811 self.current_node = old
1812 end
1813
1814 # Compile an expression an return its result
1815 # `mtype` is the expected return type, pass null if no specific type is expected.
1816 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1817 do
1818 var old = self.current_node
1819 self.current_node = nexpr
1820
1821 var res = null
1822 if nexpr.mtype != null then
1823 res = nexpr.expr(self)
1824 end
1825
1826 if res == null then
1827 # Untyped expression.
1828 # Might mean dead code or invalid code.
1829 # so aborts
1830 add_abort("FATAL: bad expression executed.")
1831 # and return a placebo result to please the C compiler
1832 if mtype == null then mtype = compiler.mainmodule.object_type
1833 res = new_var(mtype)
1834
1835 self.current_node = old
1836 return res
1837 end
1838
1839 if mtype != null then
1840 mtype = self.anchor(mtype)
1841 res = self.autobox(res, mtype)
1842 end
1843 res = autoadapt(res, nexpr.mtype.as(not null))
1844 var implicit_cast_to = nexpr.implicit_cast_to
1845 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1846 add_cast(res, implicit_cast_to, "auto")
1847 res = autoadapt(res, implicit_cast_to)
1848 end
1849 self.current_node = old
1850 return res
1851 end
1852
1853 # Alias for `self.expr(nexpr, self.bool_type)`
1854 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1855
1856 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1857 fun debug(message: String)
1858 do
1859 var node = self.current_node
1860 if node == null then
1861 print "?: {message}"
1862 else
1863 node.debug(message)
1864 end
1865 self.add("/* DEBUG: {message} */")
1866 end
1867 end
1868
1869 # A C function associated to a Nit method
1870 # Because of customization, a given Nit method can be compiler more that once
1871 abstract class AbstractRuntimeFunction
1872
1873 type COMPILER: AbstractCompiler
1874 type VISITOR: AbstractCompilerVisitor
1875
1876 # The associated Nit method
1877 var mmethoddef: MMethodDef
1878
1879 # The mangled c name of the runtime_function
1880 # Subclasses should redefine `build_c_name` instead
1881 fun c_name: String
1882 do
1883 var res = self.c_name_cache
1884 if res != null then return res
1885 res = self.build_c_name
1886 self.c_name_cache = res
1887 return res
1888 end
1889
1890 # Non cached version of `c_name`
1891 protected fun build_c_name: String is abstract
1892
1893 protected var c_name_cache: nullable String = null is writable
1894
1895 # Implements a call of the runtime_function
1896 # May inline the body or generate a C function call
1897 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1898
1899 # Generate the code for the `AbstractRuntimeFunction`
1900 # Warning: compile more than once compilation makes CC unhappy
1901 fun compile_to_c(compiler: COMPILER) is abstract
1902 end
1903
1904 # A runtime variable hold a runtime value in C.
1905 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1906 #
1907 # 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.
1908 class RuntimeVariable
1909 # The name of the variable in the C code
1910 var name: String
1911
1912 # The static type of the variable (as declard in C)
1913 var mtype: MType
1914
1915 # The current casted type of the variable (as known in Nit)
1916 var mcasttype: MType is writable
1917
1918 # If the variable exaclty a mcasttype?
1919 # false (usual value) means that the variable is a mcasttype or a subtype.
1920 var is_exact: Bool = false is writable
1921
1922 init
1923 do
1924 assert not mtype.need_anchor
1925 assert not mcasttype.need_anchor
1926 end
1927
1928 redef fun to_s do return name
1929
1930 redef fun inspect
1931 do
1932 var exact_str
1933 if self.is_exact then
1934 exact_str = " exact"
1935 else
1936 exact_str = ""
1937 end
1938 var type_str
1939 if self.mtype == self.mcasttype then
1940 type_str = "{mtype}{exact_str}"
1941 else
1942 type_str = "{mtype}({mcasttype}{exact_str})"
1943 end
1944 return "<{name}:{type_str}>"
1945 end
1946 end
1947
1948 # The static context of a visited property in a `AbstractCompilerVisitor`
1949 class StaticFrame
1950
1951 type VISITOR: AbstractCompilerVisitor
1952
1953 # The associated visitor
1954 var visitor: VISITOR
1955
1956 # The executed property.
1957 # A Method in case of a call, an attribute in case of a default initialization.
1958 var mpropdef: MPropDef
1959
1960 # The static type of the receiver
1961 var receiver: MClassType
1962
1963 # Arguments of the method (the first is the receiver)
1964 var arguments: Array[RuntimeVariable]
1965
1966 # The runtime_variable associated to the return (in a function)
1967 var returnvar: nullable RuntimeVariable = null is writable
1968
1969 # The label at the end of the property
1970 var returnlabel: nullable String = null is writable
1971
1972 # Labels associated to a each escapemarks.
1973 # Because of inlinings, escape-marks must be associated to their context (the frame)
1974 private var escapemark_names = new HashMap[EscapeMark, String]
1975
1976 # The array comprehension currently filled, if any
1977 private var comprehension: nullable RuntimeVariable = null
1978 end
1979
1980 redef class MType
1981 # Return the C type associated to a given Nit static type
1982 fun ctype: String do return "val*"
1983
1984 # C type outside of the compiler code and in boxes
1985 fun ctype_extern: String do return "val*"
1986
1987 # Short name of the `ctype` to use in unions
1988 fun ctypename: String do return "val"
1989
1990 # Is the associated C type a primitive one?
1991 #
1992 # ENSURE `result == (ctype != "val*")`
1993 fun is_c_primitive: Bool do return false
1994 end
1995
1996 redef class MClassType
1997
1998 redef var ctype is lazy do
1999 if mclass.name == "Int" then
2000 return "long"
2001 else if mclass.name == "Bool" then
2002 return "short int"
2003 else if mclass.name == "Char" then
2004 return "uint32_t"
2005 else if mclass.name == "Float" then
2006 return "double"
2007 else if mclass.name == "Int8" then
2008 return "int8_t"
2009 else if mclass.name == "Byte" then
2010 return "unsigned char"
2011 else if mclass.name == "Int16" then
2012 return "int16_t"
2013 else if mclass.name == "UInt16" then
2014 return "uint16_t"
2015 else if mclass.name == "Int32" then
2016 return "int32_t"
2017 else if mclass.name == "UInt32" then
2018 return "uint32_t"
2019 else if mclass.name == "NativeString" then
2020 return "char*"
2021 else if mclass.name == "NativeArray" then
2022 return "val*"
2023 else
2024 return "val*"
2025 end
2026 end
2027
2028 redef var is_c_primitive is lazy do return ctype != "val*"
2029
2030 redef fun ctype_extern: String
2031 do
2032 if mclass.kind == extern_kind then
2033 return "void*"
2034 else
2035 return ctype
2036 end
2037 end
2038
2039 redef fun ctypename: String
2040 do
2041 if mclass.name == "Int" then
2042 return "l"
2043 else if mclass.name == "Bool" then
2044 return "s"
2045 else if mclass.name == "Char" then
2046 return "c"
2047 else if mclass.name == "Float" then
2048 return "d"
2049 else if mclass.name == "Int8" then
2050 return "i8"
2051 else if mclass.name == "Byte" then
2052 return "b"
2053 else if mclass.name == "Int16" then
2054 return "i16"
2055 else if mclass.name == "UInt16" then
2056 return "u16"
2057 else if mclass.name == "Int32" then
2058 return "i32"
2059 else if mclass.name == "UInt32" then
2060 return "u32"
2061 else if mclass.name == "NativeString" then
2062 return "str"
2063 else if mclass.name == "NativeArray" then
2064 #return "{self.arguments.first.ctype}*"
2065 return "val"
2066 else
2067 return "val"
2068 end
2069 end
2070 end
2071
2072 redef class MPropDef
2073 type VISITOR: AbstractCompilerVisitor
2074 end
2075
2076 redef class MMethodDef
2077 # Can the body be inlined?
2078 fun can_inline(v: VISITOR): Bool
2079 do
2080 if is_abstract then return true
2081 if constant_value != null then return true
2082 var modelbuilder = v.compiler.modelbuilder
2083 var node = modelbuilder.mpropdef2node(self)
2084 if node isa APropdef then
2085 return node.can_inline
2086 else if node isa AClassdef then
2087 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2088 return true
2089 else if node == null then
2090 return true
2091 else
2092 abort
2093 end
2094 end
2095
2096 # Inline the body in another visitor
2097 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2098 do
2099 var modelbuilder = v.compiler.modelbuilder
2100 var val = constant_value
2101 var node = modelbuilder.mpropdef2node(self)
2102
2103 if is_abstract then
2104 var cn = v.class_name_string(arguments.first)
2105 v.current_node = node
2106 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2107 v.add_raw_abort
2108 return null
2109 end
2110
2111 if node isa APropdef then
2112 var oldnode = v.current_node
2113 v.current_node = node
2114 self.compile_parameter_check(v, arguments)
2115 node.compile_to_c(v, self, arguments)
2116 v.current_node = oldnode
2117 else if node isa AClassdef then
2118 var oldnode = v.current_node
2119 v.current_node = node
2120 self.compile_parameter_check(v, arguments)
2121 node.compile_to_c(v, self, arguments)
2122 v.current_node = oldnode
2123 else if val != null then
2124 v.ret(v.value_instance(val))
2125 else
2126 abort
2127 end
2128 return null
2129 end
2130
2131 # Generate type checks in the C code to check covariant parameters
2132 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2133 do
2134 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2135
2136 var msignature = self.msignature.as(not null)
2137
2138 for i in [0..msignature.arity[ do
2139 var mp = msignature.mparameters[i]
2140 # skip test for vararg since the array is instantiated with the correct polymorphic type
2141 if mp.is_vararg then continue
2142
2143 # skip if the cast is not required
2144 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2145 if not origmtype.need_anchor then continue
2146
2147 # get the parameter type
2148 var mtype = mp.mtype
2149
2150 # generate the cast
2151 # note that v decides if and how to implements the cast
2152 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2153 v.add_cast(arguments[i+1], mtype, "covariance")
2154 end
2155 end
2156 end
2157
2158 # Node visit
2159
2160 redef class APropdef
2161 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2162 do
2163 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2164 debug("Not yet implemented")
2165 end
2166
2167 fun can_inline: Bool do return true
2168 end
2169
2170 redef class AMethPropdef
2171 redef fun compile_to_c(v, mpropdef, arguments)
2172 do
2173 # Call the implicit super-init
2174 var auto_super_inits = self.auto_super_inits
2175 if auto_super_inits != null then
2176 var args = [arguments.first]
2177 for auto_super_init in auto_super_inits do
2178 assert auto_super_init.mproperty != mpropdef.mproperty
2179 args.clear
2180 for i in [0..auto_super_init.msignature.arity+1[ do
2181 args.add(arguments[i])
2182 end
2183 assert auto_super_init.mproperty != mpropdef.mproperty
2184 v.compile_callsite(auto_super_init, args)
2185 end
2186 end
2187 if auto_super_call then
2188 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2189 end
2190
2191 # Try special compilation
2192 if mpropdef.is_intern then
2193 if compile_intern_to_c(v, mpropdef, arguments) then return
2194 end
2195 if mpropdef.is_extern then
2196 if mpropdef.mproperty.is_init then
2197 if compile_externinit_to_c(v, mpropdef, arguments) then return
2198 else
2199 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2200 end
2201 end
2202
2203 # Compile block if any
2204 var n_block = n_block
2205 if n_block != null then
2206 for i in [0..mpropdef.msignature.arity[ do
2207 var variable = self.n_signature.n_params[i].variable.as(not null)
2208 v.assign(v.variable(variable), arguments[i+1])
2209 end
2210 v.stmt(n_block)
2211 return
2212 end
2213
2214 # We have a problem
2215 var cn = v.class_name_string(arguments.first)
2216 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2217 v.add_raw_abort
2218 end
2219
2220 redef fun can_inline
2221 do
2222 if self.auto_super_inits != null then return false
2223 var nblock = self.n_block
2224 if nblock == null then return true
2225 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2226 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2227 return false
2228 end
2229
2230 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2231 do
2232 var pname = mpropdef.mproperty.name
2233 var cname = mpropdef.mclassdef.mclass.name
2234 var ret = mpropdef.msignature.return_mtype
2235 if ret != null then
2236 ret = v.resolve_for(ret, arguments.first)
2237 end
2238 if pname != "==" and pname != "!=" then
2239 v.adapt_signature(mpropdef, arguments)
2240 v.unbox_signature_extern(mpropdef, arguments)
2241 end
2242 if cname == "Int" then
2243 if pname == "output" then
2244 v.add("printf(\"%ld\\n\", {arguments.first});")
2245 return true
2246 else if pname == "object_id" then
2247 v.ret(arguments.first)
2248 return true
2249 else if pname == "+" then
2250 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2251 return true
2252 else if pname == "-" then
2253 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2254 return true
2255 else if pname == "unary -" then
2256 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2257 return true
2258 else if pname == "unary +" then
2259 v.ret(arguments[0])
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 == "==" then
2271 v.ret(v.equal_test(arguments[0], arguments[1]))
2272 return true
2273 else if pname == "!=" then
2274 var res = v.equal_test(arguments[0], arguments[1])
2275 v.ret(v.new_expr("!{res}", ret.as(not null)))
2276 return true
2277 else if pname == "<" then
2278 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2279 return true
2280 else if pname == ">" then
2281 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2282 return true
2283 else if pname == "<=" then
2284 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2285 return true
2286 else if pname == ">=" then
2287 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2288 return true
2289 else if pname == "to_i8" then
2290 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2291 return true
2292 else if pname == "to_i16" then
2293 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2294 return true
2295 else if pname == "to_u16" then
2296 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2297 return true
2298 else if pname == "to_i32" then
2299 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2300 return true
2301 else if pname == "to_u32" then
2302 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2303 return true
2304 else if pname == "to_f" then
2305 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2306 return true
2307 else if pname == "to_b" then
2308 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2309 return true
2310 else if pname == "code_point" then
2311 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2312 return true
2313 else if pname == "&" then
2314 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2315 return true
2316 else if pname == "|" then
2317 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2318 return true
2319 else if pname == ">>" then
2320 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2321 return true
2322 else if pname == "<<" then
2323 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2324 return true
2325 end
2326 else if cname == "Char" then
2327 if pname == "object_id" then
2328 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2329 return true
2330 else if pname == "successor" then
2331 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2332 return true
2333 else if pname == "predecessor" then
2334 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2335 return true
2336 else if pname == "==" then
2337 v.ret(v.equal_test(arguments[0], arguments[1]))
2338 return true
2339 else if pname == "!=" then
2340 var res = v.equal_test(arguments[0], arguments[1])
2341 v.ret(v.new_expr("!{res}", ret.as(not null)))
2342 return true
2343 else if pname == "<" then
2344 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2345 return true
2346 else if pname == ">" then
2347 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2348 return true
2349 else if pname == "<=" then
2350 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2351 return true
2352 else if pname == ">=" then
2353 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2354 return true
2355 else if pname == "to_i" then
2356 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2357 return true
2358 else if pname == "code_point" then
2359 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2360 return true
2361 end
2362 else if cname == "Byte" then
2363 if pname == "output" then
2364 v.add("printf(\"%x\\n\", {arguments.first});")
2365 return true
2366 else if pname == "object_id" then
2367 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2368 return true
2369 else if pname == "+" then
2370 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2371 return true
2372 else if pname == "-" then
2373 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2374 return true
2375 else if pname == "unary -" then
2376 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2377 return true
2378 else if pname == "unary +" then
2379 v.ret(arguments[0])
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.equal_test(arguments[0], arguments[1]))
2392 return true
2393 else if pname == "!=" then
2394 var res = v.equal_test(arguments[0], arguments[1])
2395 v.ret(v.new_expr("!{res}", ret.as(not null)))
2396 return true
2397 else if pname == "<" then
2398 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2399 return true
2400 else if pname == ">" then
2401 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2402 return true
2403 else if pname == "<=" then
2404 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2405 return true
2406 else if pname == ">=" then
2407 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2408 return true
2409 else if pname == ">>" then
2410 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2411 return true
2412 else if pname == "<<" then
2413 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2414 return true
2415 else if pname == "&" then
2416 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2417 return true
2418 else if pname == "to_i" then
2419 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2420 return true
2421 else if pname == "to_f" then
2422 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2423 return true
2424 else if pname == "to_i8" then
2425 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2426 return true
2427 else if pname == "to_i16" then
2428 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2429 return true
2430 else if pname == "to_u16" then
2431 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2432 return true
2433 else if pname == "to_i32" then
2434 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2435 return true
2436 else if pname == "to_u32" then
2437 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2438 return true
2439 else if pname == "ascii" then
2440 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2441 return true
2442 end
2443 else if cname == "Bool" then
2444 if pname == "output" then
2445 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2446 return true
2447 else if pname == "object_id" then
2448 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2449 return true
2450 else if pname == "==" then
2451 v.ret(v.equal_test(arguments[0], arguments[1]))
2452 return true
2453 else if pname == "!=" then
2454 var res = v.equal_test(arguments[0], arguments[1])
2455 v.ret(v.new_expr("!{res}", ret.as(not null)))
2456 return true
2457 end
2458 else if cname == "Float" then
2459 if pname == "output" then
2460 v.add("printf(\"%f\\n\", {arguments.first});")
2461 return true
2462 else if pname == "object_id" then
2463 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2464 return true
2465 else if pname == "+" then
2466 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2467 return true
2468 else if pname == "-" then
2469 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2470 return true
2471 else if pname == "unary -" then
2472 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2473 return true
2474 else if pname == "unary +" then
2475 v.ret(arguments[0])
2476 return true
2477 else if pname == "succ" then
2478 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2479 return true
2480 else if pname == "prec" then
2481 v.ret(v.new_expr("{arguments[0]}-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 == "==" then
2490 v.ret(v.equal_test(arguments[0], arguments[1]))
2491 return true
2492 else if pname == "!=" then
2493 var res = v.equal_test(arguments[0], arguments[1])
2494 v.ret(v.new_expr("!{res}", ret.as(not null)))
2495 return true
2496 else if pname == "<" then
2497 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2498 return true
2499 else if pname == ">" then
2500 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2501 return true
2502 else if pname == "<=" then
2503 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2504 return true
2505 else if pname == ">=" then
2506 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2507 return true
2508 else if pname == "to_i" then
2509 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2510 return true
2511 else if pname == "to_b" then
2512 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2513 return true
2514 else if pname == "to_i8" then
2515 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2516 return true
2517 else if pname == "to_i16" then
2518 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2519 return true
2520 else if pname == "to_u16" then
2521 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2522 return true
2523 else if pname == "to_i32" then
2524 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2525 return true
2526 else if pname == "to_u32" then
2527 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2528 return true
2529 end
2530 else if cname == "NativeString" then
2531 if pname == "[]" then
2532 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2533 return true
2534 else if pname == "[]=" then
2535 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2536 return true
2537 else if pname == "copy_to" then
2538 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2539 return true
2540 else if pname == "atoi" then
2541 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2542 return true
2543 else if pname == "fast_cstring" then
2544 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2545 return true
2546 else if pname == "==" then
2547 v.ret(v.equal_test(arguments[0], arguments[1]))
2548 return true
2549 else if pname == "!=" then
2550 var res = v.equal_test(arguments[0], arguments[1])
2551 v.ret(v.new_expr("!{res}", ret.as(not null)))
2552 return true
2553 else if pname == "new" then
2554 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2555 return true
2556 else if pname == "fetch_4_chars" then
2557 v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
2558 return true
2559 else if pname == "fetch_4_hchars" then
2560 v.ret(v.new_expr("(long)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
2561 return true
2562 end
2563 else if cname == "NativeArray" then
2564 return v.native_array_def(pname, ret, arguments)
2565 else if cname == "Int8" then
2566 if pname == "output" then
2567 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2568 return true
2569 else if pname == "object_id" then
2570 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2571 return true
2572 else if pname == "+" then
2573 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2574 return true
2575 else if pname == "-" then
2576 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2577 return true
2578 else if pname == "unary -" then
2579 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2580 return true
2581 else if pname == "unary +" then
2582 v.ret(arguments[0])
2583 return true
2584 else if pname == "*" then
2585 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2586 return true
2587 else if pname == "/" 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 == "==" then
2600 v.ret(v.equal_test(arguments[0], arguments[1]))
2601 return true
2602 else if pname == "!=" then
2603 var res = v.equal_test(arguments[0], arguments[1])
2604 v.ret(v.new_expr("!{res}", ret.as(not null)))
2605 return true
2606 else if pname == "<" then
2607 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2608 return true
2609 else if pname == ">" then
2610 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2611 return true
2612 else if pname == "<=" then
2613 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2614 return true
2615 else if pname == ">=" then
2616 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2617 return true
2618 else if pname == "to_i" then
2619 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2620 return true
2621 else if pname == "to_b" then
2622 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2623 return true
2624 else if pname == "to_i16" then
2625 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2626 return true
2627 else if pname == "to_u16" then
2628 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2629 return true
2630 else if pname == "to_i32" then
2631 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2632 return true
2633 else if pname == "to_u32" then
2634 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2635 return true
2636 else if pname == "to_f" then
2637 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2638 return true
2639 else if pname == "&" then
2640 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2641 return true
2642 else if pname == "|" then
2643 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2644 return true
2645 else if pname == "^" then
2646 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2647 return true
2648 else if pname == "unary ~" then
2649 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2650 return true
2651 end
2652 else if cname == "Int16" then
2653 if pname == "output" then
2654 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2655 return true
2656 else if pname == "object_id" then
2657 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2658 return true
2659 else if pname == "+" then
2660 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2661 return true
2662 else if pname == "-" then
2663 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2664 return true
2665 else if pname == "unary -" then
2666 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2667 return true
2668 else if pname == "unary +" then
2669 v.ret(arguments[0])
2670 return true
2671 else if pname == "*" then
2672 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2673 return true
2674 else if pname == "/" 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 == "==" then
2687 v.ret(v.equal_test(arguments[0], arguments[1]))
2688 return true
2689 else if pname == "!=" then
2690 var res = v.equal_test(arguments[0], arguments[1])
2691 v.ret(v.new_expr("!{res}", ret.as(not null)))
2692 return true
2693 else if pname == "<" then
2694 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2695 return true
2696 else if pname == ">" then
2697 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2698 return true
2699 else if pname == "<=" then
2700 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2701 return true
2702 else if pname == ">=" then
2703 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2704 return true
2705 else if pname == "to_i" then
2706 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2707 return true
2708 else if pname == "to_b" then
2709 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2710 return true
2711 else if pname == "to_i8" then
2712 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2713 return true
2714 else if pname == "to_u16" then
2715 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2716 return true
2717 else if pname == "to_i32" then
2718 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2719 return true
2720 else if pname == "to_u32" then
2721 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2722 return true
2723 else if pname == "to_f" then
2724 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2725 return true
2726 else if pname == "&" then
2727 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2728 return true
2729 else if pname == "|" then
2730 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2731 return true
2732 else if pname == "^" then
2733 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2734 return true
2735 else if pname == "unary ~" then
2736 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2737 return true
2738 end
2739 else if cname == "UInt16" then
2740 if pname == "output" then
2741 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2742 return true
2743 else if pname == "object_id" then
2744 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2745 return true
2746 else if pname == "+" then
2747 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2748 return true
2749 else if pname == "-" then
2750 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2751 return true
2752 else if pname == "unary -" then
2753 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2754 return true
2755 else if pname == "unary +" then
2756 v.ret(arguments[0])
2757 return true
2758 else if pname == "*" then
2759 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2760 return true
2761 else if pname == "/" 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 == "==" then
2774 v.ret(v.equal_test(arguments[0], arguments[1]))
2775 return true
2776 else if pname == "!=" then
2777 var res = v.equal_test(arguments[0], arguments[1])
2778 v.ret(v.new_expr("!{res}", ret.as(not null)))
2779 return true
2780 else if pname == "<" then
2781 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2782 return true
2783 else if pname == ">" then
2784 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2785 return true
2786 else if pname == "<=" then
2787 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2788 return true
2789 else if pname == ">=" then
2790 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2791 return true
2792 else if pname == "to_i" then
2793 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2794 return true
2795 else if pname == "to_b" then
2796 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2797 return true
2798 else if pname == "to_i8" then
2799 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2800 return true
2801 else if pname == "to_i16" then
2802 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2803 return true
2804 else if pname == "to_i32" then
2805 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2806 return true
2807 else if pname == "to_u32" then
2808 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2809 return true
2810 else if pname == "to_f" then
2811 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2812 return true
2813 else if pname == "&" then
2814 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2815 return true
2816 else if pname == "|" then
2817 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2818 return true
2819 else if pname == "^" then
2820 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2821 return true
2822 else if pname == "unary ~" then
2823 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2824 return true
2825 end
2826 else if cname == "Int32" then
2827 if pname == "output" then
2828 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2829 return true
2830 else if pname == "object_id" then
2831 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2832 return true
2833 else if pname == "+" then
2834 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2835 return true
2836 else if pname == "-" then
2837 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2838 return true
2839 else if pname == "unary -" then
2840 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2841 return true
2842 else if pname == "unary +" then
2843 v.ret(arguments[0])
2844 return true
2845 else if pname == "*" then
2846 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2847 return true
2848 else if pname == "/" 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 == "==" then
2861 v.ret(v.equal_test(arguments[0], arguments[1]))
2862 return true
2863 else if pname == "!=" then
2864 var res = v.equal_test(arguments[0], arguments[1])
2865 v.ret(v.new_expr("!{res}", ret.as(not null)))
2866 return true
2867 else if pname == "<" then
2868 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2869 return true
2870 else if pname == ">" then
2871 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2872 return true
2873 else if pname == "<=" then
2874 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2875 return true
2876 else if pname == ">=" then
2877 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2878 return true
2879 else if pname == "to_i" then
2880 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2881 return true
2882 else if pname == "to_b" then
2883 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2884 return true
2885 else if pname == "to_i8" then
2886 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2887 return true
2888 else if pname == "to_i16" then
2889 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2890 return true
2891 else if pname == "to_u16" then
2892 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2893 return true
2894 else if pname == "to_u32" then
2895 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2896 return true
2897 else if pname == "to_f" then
2898 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2899 return true
2900 else if pname == "&" then
2901 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2902 return true
2903 else if pname == "|" then
2904 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2905 return true
2906 else if pname == "^" then
2907 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2908 return true
2909 else if pname == "unary ~" then
2910 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2911 return true
2912 end
2913 else if cname == "UInt32" then
2914 if pname == "output" then
2915 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2916 return true
2917 else if pname == "object_id" then
2918 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2919 return true
2920 else if pname == "+" then
2921 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2922 return true
2923 else if pname == "-" then
2924 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2925 return true
2926 else if pname == "unary -" then
2927 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2928 return true
2929 else if pname == "unary +" then
2930 v.ret(arguments[0])
2931 return true
2932 else if pname == "*" then
2933 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2934 return true
2935 else if pname == "/" 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 == "==" then
2948 v.ret(v.equal_test(arguments[0], arguments[1]))
2949 return true
2950 else if pname == "!=" then
2951 var res = v.equal_test(arguments[0], arguments[1])
2952 v.ret(v.new_expr("!{res}", ret.as(not null)))
2953 return true
2954 else if pname == "<" then
2955 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2956 return true
2957 else if pname == ">" then
2958 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2959 return true
2960 else if pname == "<=" then
2961 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2962 return true
2963 else if pname == ">=" then
2964 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2965 return true
2966 else if pname == "to_i" then
2967 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2968 return true
2969 else if pname == "to_b" then
2970 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2971 return true
2972 else if pname == "to_i8" then
2973 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2974 return true
2975 else if pname == "to_i16" then
2976 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2977 return true
2978 else if pname == "to_u16" then
2979 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2980 return true
2981 else if pname == "to_i32" then
2982 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2983 return true
2984 else if pname == "to_f" then
2985 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2986 return true
2987 else if pname == "&" then
2988 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2989 return true
2990 else if pname == "|" then
2991 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2992 return true
2993 else if pname == "^" then
2994 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2995 return true
2996 else if pname == "unary ~" then
2997 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2998 return true
2999 end
3000 end
3001 if pname == "exit" then
3002 v.add("exit({arguments[1]});")
3003 return true
3004 else if pname == "sys" then
3005 v.ret(v.new_expr("glob_sys", ret.as(not null)))
3006 return true
3007 else if pname == "calloc_string" then
3008 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
3009 return true
3010 else if pname == "calloc_array" then
3011 v.calloc_array(ret.as(not null), arguments)
3012 return true
3013 else if pname == "object_id" then
3014 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3015 return true
3016 else if pname == "is_same_type" then
3017 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
3018 return true
3019 else if pname == "is_same_instance" then
3020 v.ret(v.equal_test(arguments[0], arguments[1]))
3021 return true
3022 else if pname == "output_class_name" then
3023 var nat = v.class_name_string(arguments.first)
3024 v.add("printf(\"%s\\n\", {nat});")
3025 return true
3026 else if pname == "native_class_name" then
3027 var nat = v.class_name_string(arguments.first)
3028 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
3029 return true
3030 else if pname == "force_garbage_collection" then
3031 v.add("nit_gcollect();")
3032 return true
3033 else if pname == "native_argc" then
3034 v.ret(v.new_expr("glob_argc", ret.as(not null)))
3035 return true
3036 else if pname == "native_argv" then
3037 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
3038 return true
3039 end
3040 return false
3041 end
3042
3043 # Compile an extern method
3044 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3045 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3046 do
3047 var externname
3048 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3049 if at != null and at.n_args.length == 1 then
3050 externname = at.arg_as_string(v.compiler.modelbuilder)
3051 if externname == null then return false
3052 else
3053 return false
3054 end
3055 v.add_extern(mpropdef.mclassdef.mmodule)
3056 var res: nullable RuntimeVariable = null
3057 var ret = mpropdef.msignature.return_mtype
3058 if ret != null then
3059 ret = v.resolve_for(ret, arguments.first)
3060 res = v.new_var_extern(ret)
3061 end
3062 v.adapt_signature(mpropdef, arguments)
3063 v.unbox_signature_extern(mpropdef, arguments)
3064
3065 if res == null then
3066 v.add("{externname}({arguments.join(", ")});")
3067 else
3068 v.add("{res} = {externname}({arguments.join(", ")});")
3069 res = v.box_extern(res, ret.as(not null))
3070 v.ret(res)
3071 end
3072 return true
3073 end
3074
3075 # Compile an extern factory
3076 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3077 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3078 do
3079 var externname
3080 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3081 if at != null then
3082 externname = at.arg_as_string(v.compiler.modelbuilder)
3083 if externname == null then return false
3084 else
3085 return false
3086 end
3087 v.add_extern(mpropdef.mclassdef.mmodule)
3088 v.adapt_signature(mpropdef, arguments)
3089 v.unbox_signature_extern(mpropdef, arguments)
3090 var ret = arguments.first.mtype
3091 var res = v.new_var_extern(ret)
3092
3093 arguments.shift
3094
3095 v.add("{res} = {externname}({arguments.join(", ")});")
3096 res = v.box_extern(res, ret)
3097 v.ret(res)
3098 return true
3099 end
3100 end
3101
3102 redef class AAttrPropdef
3103 redef fun can_inline: Bool do return not is_lazy
3104
3105 redef fun compile_to_c(v, mpropdef, arguments)
3106 do
3107 if mpropdef == mreadpropdef then
3108 assert arguments.length == 1
3109 var recv = arguments.first
3110 var res
3111 if is_lazy then
3112 var set
3113 var ret = self.mtype
3114 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3115 var guard = self.mlazypropdef.mproperty
3116 if useiset then
3117 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3118 else
3119 set = v.read_attribute(guard, recv)
3120 end
3121 v.add("if(likely({set})) \{")
3122 res = v.read_attribute(self.mpropdef.mproperty, recv)
3123 v.add("\} else \{")
3124
3125 var value = evaluate_expr(v, recv)
3126
3127 v.assign(res, value)
3128 if not useiset then
3129 var true_v = v.bool_instance(true)
3130 v.write_attribute(guard, arguments.first, true_v)
3131 end
3132 v.add("\}")
3133 else
3134 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3135 end
3136 v.assign(v.frame.returnvar.as(not null), res)
3137 else if mpropdef == mwritepropdef then
3138 assert arguments.length == 2
3139 var recv = arguments.first
3140 var arg = arguments[1]
3141 if is_optional and v.maybenull(arg) then
3142 var value = v.new_var(self.mpropdef.static_mtype.as(not null))
3143 v.add("if ({arg} == NULL) \{")
3144 v.assign(value, evaluate_expr(v, recv))
3145 v.add("\} else \{")
3146 v.assign(value, arg)
3147 v.add("\}")
3148 arg = value
3149 end
3150 v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
3151 if is_lazy then
3152 var ret = self.mtype
3153 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3154 if not useiset then
3155 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3156 end
3157 end
3158 else
3159 abort
3160 end
3161 end
3162
3163 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3164 do
3165 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3166 end
3167
3168 # Evaluate, store and return the default value of the attribute
3169 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3170 do
3171 var oldnode = v.current_node
3172 v.current_node = self
3173 var old_frame = v.frame
3174 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3175 v.frame = frame
3176
3177 var value
3178 var mtype = self.mtype
3179 assert mtype != null
3180
3181 var nexpr = self.n_expr
3182 var nblock = self.n_block
3183 if nexpr != null then
3184 value = v.expr(nexpr, mtype)
3185 else if nblock != null then
3186 value = v.new_var(mtype)
3187 frame.returnvar = value
3188 frame.returnlabel = v.get_name("RET_LABEL")
3189 v.add("\{")
3190 v.stmt(nblock)
3191 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3192 v.add("\}")
3193 else
3194 abort
3195 end
3196
3197 v.write_attribute(self.mpropdef.mproperty, recv, value)
3198
3199 v.frame = old_frame
3200 v.current_node = oldnode
3201
3202 return value
3203 end
3204
3205 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3206 do
3207 var nexpr = self.n_expr
3208 if nexpr != null then return
3209
3210 var oldnode = v.current_node
3211 v.current_node = self
3212 var old_frame = v.frame
3213 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3214 v.frame = frame
3215 # Force read to check the initialization
3216 v.read_attribute(self.mpropdef.mproperty, recv)
3217 v.frame = old_frame
3218 v.current_node = oldnode
3219 end
3220 end
3221
3222 redef class AClassdef
3223 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3224 do
3225 if mpropdef.mproperty.is_root_init then
3226 assert arguments.length == 1
3227 if not mpropdef.is_intro then
3228 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3229 end
3230 return
3231 else
3232 abort
3233 end
3234 end
3235 end
3236
3237 redef class AExpr
3238 # Try to compile self as an expression
3239 # Do not call this method directly, use `v.expr` instead
3240 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3241 do
3242 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3243 var mtype = self.mtype
3244 if mtype == null then
3245 return null
3246 else
3247 var res = v.new_var(mtype)
3248 v.add("/* {res} = NOT YET {class_name} */")
3249 return res
3250 end
3251 end
3252
3253 # Try to compile self as a statement
3254 # Do not call this method directly, use `v.stmt` instead
3255 private fun stmt(v: AbstractCompilerVisitor)
3256 do
3257 expr(v)
3258 end
3259 end
3260
3261 redef class ABlockExpr
3262 redef fun stmt(v)
3263 do
3264 for e in self.n_expr do v.stmt(e)
3265 end
3266 redef fun expr(v)
3267 do
3268 var last = self.n_expr.last
3269 for e in self.n_expr do
3270 if e == last then break
3271 v.stmt(e)
3272 end
3273 return v.expr(last, null)
3274 end
3275 end
3276
3277 redef class AVardeclExpr
3278 redef fun stmt(v)
3279 do
3280 var variable = self.variable.as(not null)
3281 var ne = self.n_expr
3282 if ne != null then
3283 var i = v.expr(ne, variable.declared_type)
3284 v.assign(v.variable(variable), i)
3285 end
3286 end
3287 end
3288
3289 redef class AVarExpr
3290 redef fun expr(v)
3291 do
3292 var res = v.variable(self.variable.as(not null))
3293 var mtype = self.mtype.as(not null)
3294 return v.autoadapt(res, mtype)
3295 end
3296 end
3297
3298 redef class AVarAssignExpr
3299 redef fun expr(v)
3300 do
3301 var variable = self.variable.as(not null)
3302 var i = v.expr(self.n_value, variable.declared_type)
3303 v.assign(v.variable(variable), i)
3304 return i
3305 end
3306 end
3307
3308 redef class AVarReassignExpr
3309 redef fun stmt(v)
3310 do
3311 var variable = self.variable.as(not null)
3312 var vari = v.variable(variable)
3313 var value = v.expr(self.n_value, variable.declared_type)
3314 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3315 assert res != null
3316 v.assign(v.variable(variable), res)
3317 end
3318 end
3319
3320 redef class ASelfExpr
3321 redef fun expr(v) do return v.frame.arguments.first
3322 end
3323
3324 redef class AImplicitSelfExpr
3325 redef fun expr(v) do
3326 if not is_sys then return super
3327 return v.new_expr("glob_sys", mtype.as(not null))
3328 end
3329 end
3330
3331 redef class AEscapeExpr
3332 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3333 end
3334
3335 redef class AReturnExpr
3336 redef fun stmt(v)
3337 do
3338 var nexpr = self.n_expr
3339 if nexpr != null then
3340 var returnvar = v.frame.returnvar.as(not null)
3341 var i = v.expr(nexpr, returnvar.mtype)
3342 v.assign(returnvar, i)
3343 end
3344 v.add("goto {v.frame.returnlabel.as(not null)};")
3345 end
3346 end
3347
3348 redef class AAbortExpr
3349 redef fun stmt(v) do v.add_abort("Aborted")
3350 end
3351
3352 redef class AIfExpr
3353 redef fun stmt(v)
3354 do
3355 var cond = v.expr_bool(self.n_expr)
3356 v.add("if ({cond})\{")
3357 v.stmt(self.n_then)
3358 v.add("\} else \{")
3359 v.stmt(self.n_else)
3360 v.add("\}")
3361 end
3362
3363 redef fun expr(v)
3364 do
3365 var res = v.new_var(self.mtype.as(not null))
3366 var cond = v.expr_bool(self.n_expr)
3367 v.add("if ({cond})\{")
3368 v.assign(res, v.expr(self.n_then.as(not null), null))
3369 v.add("\} else \{")
3370 v.assign(res, v.expr(self.n_else.as(not null), null))
3371 v.add("\}")
3372 return res
3373 end
3374 end
3375
3376 redef class AIfexprExpr
3377 redef fun expr(v)
3378 do
3379 var res = v.new_var(self.mtype.as(not null))
3380 var cond = v.expr_bool(self.n_expr)
3381 v.add("if ({cond})\{")
3382 v.assign(res, v.expr(self.n_then, null))
3383 v.add("\} else \{")
3384 v.assign(res, v.expr(self.n_else, null))
3385 v.add("\}")
3386 return res
3387 end
3388 end
3389
3390 redef class ADoExpr
3391 redef fun stmt(v)
3392 do
3393 if self.n_catch != null then
3394 v.add("catchStack.cursor += 1;")
3395 v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
3396 v.stmt(self.n_block)
3397 v.add("catchStack.cursor -= 1;")
3398 v.add("\}else \{")
3399 v.add("catchStack.cursor -= 1;")
3400 v.stmt(self.n_catch)
3401 v.add("\}")
3402 else
3403 v.stmt(self.n_block)
3404 end
3405 v.add_escape_label(break_mark)
3406 end
3407 end
3408
3409 redef class AWhileExpr
3410 redef fun stmt(v)
3411 do
3412 v.add("for(;;) \{")
3413 var cond = v.expr_bool(self.n_expr)
3414 v.add("if (!{cond}) break;")
3415 v.stmt(self.n_block)
3416 v.add_escape_label(continue_mark)
3417 v.add("\}")
3418 v.add_escape_label(break_mark)
3419 end
3420 end
3421
3422 redef class ALoopExpr
3423 redef fun stmt(v)
3424 do
3425 v.add("for(;;) \{")
3426 v.stmt(self.n_block)
3427 v.add_escape_label(continue_mark)
3428 v.add("\}")
3429 v.add_escape_label(break_mark)
3430 end
3431 end
3432
3433 redef class AForExpr
3434 redef fun stmt(v)
3435 do
3436 for g in n_groups do
3437 var cl = v.expr(g.n_expr, null)
3438 var it_meth = g.method_iterator
3439 assert it_meth != null
3440 var it = v.compile_callsite(it_meth, [cl])
3441 assert it != null
3442 g.it = it
3443 end
3444 v.add("for(;;) \{")
3445 for g in n_groups do
3446 var it = g.it
3447 var isok_meth = g.method_is_ok
3448 assert isok_meth != null
3449 var ok = v.compile_callsite(isok_meth, [it])
3450 assert ok != null
3451 v.add("if(!{ok}) break;")
3452 if g.variables.length == 1 then
3453 var item_meth = g.method_item
3454 assert item_meth != null
3455 var i = v.compile_callsite(item_meth, [it])
3456 assert i != null
3457 v.assign(v.variable(g.variables.first), i)
3458 else if g.variables.length == 2 then
3459 var key_meth = g.method_key
3460 assert key_meth != null
3461 var i = v.compile_callsite(key_meth, [it])
3462 assert i != null
3463 v.assign(v.variable(g.variables[0]), i)
3464 var item_meth = g.method_item
3465 assert item_meth != null
3466 i = v.compile_callsite(item_meth, [it])
3467 assert i != null
3468 v.assign(v.variable(g.variables[1]), i)
3469 else
3470 abort
3471 end
3472 end
3473 v.stmt(self.n_block)
3474 v.add_escape_label(continue_mark)
3475 for g in n_groups do
3476 var next_meth = g.method_next
3477 assert next_meth != null
3478 v.compile_callsite(next_meth, [g.it])
3479 end
3480 v.add("\}")
3481 v.add_escape_label(break_mark)
3482
3483 for g in n_groups do
3484 var method_finish = g.method_finish
3485 if method_finish != null then
3486 # TODO: Find a way to call this also in long escape (e.g. return)
3487 v.compile_callsite(method_finish, [g.it])
3488 end
3489 end
3490 end
3491 end
3492
3493 redef class AForGroup
3494 # C variable representing the iterator
3495 private var it: RuntimeVariable is noinit
3496 end
3497
3498 redef class AAssertExpr
3499 redef fun stmt(v)
3500 do
3501 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3502
3503 var cond = v.expr_bool(self.n_expr)
3504 v.add("if (unlikely(!{cond})) \{")
3505 v.stmt(self.n_else)
3506 var nid = self.n_id
3507 if nid != null then
3508 v.add_abort("Assert '{nid.text}' failed")
3509 else
3510 v.add_abort("Assert failed")
3511 end
3512 v.add("\}")
3513 end
3514 end
3515
3516 redef class AOrExpr
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} = 1;")
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 AImpliesExpr
3532 redef fun expr(v)
3533 do
3534 var res = v.new_var(self.mtype.as(not null))
3535 var i1 = v.expr_bool(self.n_expr)
3536 v.add("if (!{i1}) \{")
3537 v.add("{res} = 1;")
3538 v.add("\} else \{")
3539 var i2 = v.expr_bool(self.n_expr2)
3540 v.add("{res} = {i2};")
3541 v.add("\}")
3542 return res
3543 end
3544 end
3545
3546 redef class AAndExpr
3547 redef fun expr(v)
3548 do
3549 var res = v.new_var(self.mtype.as(not null))
3550 var i1 = v.expr_bool(self.n_expr)
3551 v.add("if (!{i1}) \{")
3552 v.add("{res} = 0;")
3553 v.add("\} else \{")
3554 var i2 = v.expr_bool(self.n_expr2)
3555 v.add("{res} = {i2};")
3556 v.add("\}")
3557 return res
3558 end
3559 end
3560
3561 redef class ANotExpr
3562 redef fun expr(v)
3563 do
3564 var cond = v.expr_bool(self.n_expr)
3565 return v.new_expr("!{cond}", self.mtype.as(not null))
3566 end
3567 end
3568
3569 redef class AOrElseExpr
3570 redef fun expr(v)
3571 do
3572 var res = v.new_var(self.mtype.as(not null))
3573 var i1 = v.expr(self.n_expr, null)
3574
3575 if not v.maybenull(i1) then return i1
3576
3577 v.add("if ({i1}!=NULL) \{")
3578 v.assign(res, i1)
3579 v.add("\} else \{")
3580 var i2 = v.expr(self.n_expr2, null)
3581 v.assign(res, i2)
3582 v.add("\}")
3583 return res
3584 end
3585 end
3586
3587 redef class AIntegerExpr
3588 redef fun expr(v) do
3589 if value isa Int then return v.int_instance(value.as(Int))
3590 if value isa Byte then return v.byte_instance(value.as(Byte))
3591 if value isa Int8 then return v.int8_instance(value.as(Int8))
3592 if value isa Int16 then return v.int16_instance(value.as(Int16))
3593 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3594 if value isa Int32 then return v.int32_instance(value.as(Int32))
3595 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3596 # Should never happen
3597 abort
3598 end
3599 end
3600
3601 redef class AFloatExpr
3602 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3603 end
3604
3605 redef class ACharExpr
3606 redef fun expr(v) do
3607 if is_ascii then return v.byte_instance(value.as(not null).ascii)
3608 if is_code_point then return v.int_instance(value.as(not null).code_point)
3609 return v.char_instance(self.value.as(not null))
3610 end
3611 end
3612
3613 redef class AArrayExpr
3614 redef fun expr(v)
3615 do
3616 var mtype = self.element_mtype.as(not null)
3617 var array = new Array[RuntimeVariable]
3618 var res = v.array_instance(array, mtype)
3619
3620 var old_comprehension = v.frame.comprehension
3621 v.frame.comprehension = res
3622 for nexpr in self.n_exprs do
3623 v.stmt(nexpr)
3624 end
3625 v.frame.comprehension = old_comprehension
3626
3627 return res
3628 end
3629 end
3630
3631 redef class AugmentedStringFormExpr
3632 # Factorize the making of a `Regex` object from a literal prefixed string
3633 protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
3634 var re = to_re
3635 assert re != null
3636 var res = v.compile_callsite(re, [rs])
3637 if res == null then
3638 print "Cannot call property `to_re` on {self}"
3639 abort
3640 end
3641 for i in suffix.chars do
3642 if i == 'i' then
3643 var ign = ignore_case
3644 assert ign != null
3645 v.compile_callsite(ign, [res, v.bool_instance(true)])
3646 continue
3647 end
3648 if i == 'm' then
3649 var nl = newline
3650 assert nl != null
3651 v.compile_callsite(nl, [res, v.bool_instance(true)])
3652 continue
3653 end
3654 if i == 'b' then
3655 var ext = extended
3656 assert ext != null
3657 v.compile_callsite(ext, [res, v.bool_instance(false)])
3658 continue
3659 end
3660 # Should not happen, this needs to be updated
3661 # along with the addition of new suffixes
3662 abort
3663 end
3664 return res
3665 end
3666 end
3667
3668 redef class AStringFormExpr
3669 redef fun expr(v) do return v.string_instance(value)
3670 end
3671
3672 redef class AStringExpr
3673 redef fun expr(v) do
3674 var s = v.string_instance(value)
3675 if is_string then return s
3676 if is_bytestring then
3677 var ns = v.native_string_instance(bytes.items, bytes.length)
3678 var ln = v.int_instance(bytes.length)
3679 var cs = to_bytes_with_copy
3680 assert cs != null
3681 var res = v.compile_callsite(cs, [ns, ln])
3682 assert res != null
3683 s = res
3684 else if is_re then
3685 var res = make_re(v, s)
3686 assert res != null
3687 s = res
3688 else
3689 print "Unimplemented prefix or suffix for {self}"
3690 abort
3691 end
3692 return s
3693 end
3694 end
3695
3696 redef class ASuperstringExpr
3697 redef fun expr(v)
3698 do
3699 var type_string = v.mmodule.string_type
3700
3701 # Collect elements of the superstring
3702 var array = new Array[AExpr]
3703 for ne in self.n_exprs do
3704 # Drop literal empty string.
3705 # They appears in things like "{a}" that is ["", a, ""]
3706 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3707 array.add(ne)
3708 end
3709
3710 # Store the allocated native array in a static variable
3711 # For reusing later
3712 var varonce = v.get_name("varonce")
3713 v.add("if (unlikely({varonce}==NULL)) \{")
3714
3715 # The native array that will contains the elements to_s-ized.
3716 # For fast concatenation.
3717 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3718
3719 v.add_decl("static {a.mtype.ctype} {varonce};")
3720
3721 # Pre-fill the array with the literal string parts.
3722 # So they do not need to be filled again when reused
3723 for i in [0..array.length[ do
3724 var ne = array[i]
3725 if not ne isa AStringFormExpr then continue
3726 var e = v.expr(ne, null)
3727 v.native_array_set(a, i, e)
3728 end
3729
3730 v.add("\} else \{")
3731 # Take the native-array from the store.
3732 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3733 # WARNING: not thread safe! (FIXME?)
3734 v.add("{a} = {varonce};")
3735 v.add("{varonce} = NULL;")
3736 v.add("\}")
3737
3738 # Stringify the elements and put them in the native array
3739 var to_s_method = v.get_property("to_s", v.object_type)
3740 for i in [0..array.length[ do
3741 var ne = array[i]
3742 if ne isa AStringFormExpr then continue
3743 var e = v.expr(ne, null)
3744 # Skip the `to_s` if the element is already a String
3745 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3746 e = v.send(to_s_method, [e]).as(not null)
3747 end
3748 v.native_array_set(a, i, e)
3749 end
3750
3751 # Fast join the native string to get the result
3752 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3753 assert res != null
3754
3755 if is_re then res = make_re(v, res)
3756
3757 # We finish to work with the native array,
3758 # so store it so that it can be reused
3759 v.add("{varonce} = {a};")
3760
3761 return res
3762 end
3763 end
3764
3765 redef class ACrangeExpr
3766 redef fun expr(v)
3767 do
3768 var i1 = v.expr(self.n_expr, null)
3769 var i2 = v.expr(self.n_expr2, null)
3770 var mtype = self.mtype.as(MClassType)
3771 var res = v.init_instance(mtype)
3772 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3773 return res
3774 end
3775 end
3776
3777 redef class AOrangeExpr
3778 redef fun expr(v)
3779 do
3780 var i1 = v.expr(self.n_expr, null)
3781 var i2 = v.expr(self.n_expr2, null)
3782 var mtype = self.mtype.as(MClassType)
3783 var res = v.init_instance(mtype)
3784 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3785 return res
3786 end
3787 end
3788
3789 redef class ATrueExpr
3790 redef fun expr(v) do return v.bool_instance(true)
3791 end
3792
3793 redef class AFalseExpr
3794 redef fun expr(v) do return v.bool_instance(false)
3795 end
3796
3797 redef class ANullExpr
3798 redef fun expr(v) do return v.null_instance
3799 end
3800
3801 redef class AIsaExpr
3802 redef fun expr(v)
3803 do
3804 var i = v.expr(self.n_expr, null)
3805 var cast_type = self.cast_type
3806 if cast_type == null then return null # no-no on broken node
3807 return v.type_test(i, cast_type, "isa")
3808 end
3809 end
3810
3811 redef class AAsCastExpr
3812 redef fun expr(v)
3813 do
3814 var i = v.expr(self.n_expr, null)
3815 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3816
3817 v.add_cast(i, self.mtype.as(not null), "as")
3818 return i
3819 end
3820 end
3821
3822 redef class AAsNotnullExpr
3823 redef fun expr(v)
3824 do
3825 var i = v.expr(self.n_expr, null)
3826 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3827
3828 if not v.maybenull(i) then return i
3829
3830 v.add("if (unlikely({i} == NULL)) \{")
3831 v.add_abort("Cast failed")
3832 v.add("\}")
3833 return i
3834 end
3835 end
3836
3837 redef class AParExpr
3838 redef fun expr(v) do return v.expr(self.n_expr, null)
3839 end
3840
3841 redef class AOnceExpr
3842 redef fun expr(v)
3843 do
3844 var mtype = self.mtype.as(not null)
3845 var name = v.get_name("varonce")
3846 var guard = v.get_name(name + "_guard")
3847 v.add_decl("static {mtype.ctype} {name};")
3848 v.add_decl("static int {guard};")
3849 var res = v.new_var(mtype)
3850 v.add("if (likely({guard})) \{")
3851 v.add("{res} = {name};")
3852 v.add("\} else \{")
3853 var i = v.expr(self.n_expr, mtype)
3854 v.add("{res} = {i};")
3855 v.add("{name} = {res};")
3856 v.add("{guard} = 1;")
3857 v.add("\}")
3858 return res
3859 end
3860 end
3861
3862 redef class ASendExpr
3863 redef fun expr(v)
3864 do
3865 var recv = v.expr(self.n_expr, null)
3866 var callsite = self.callsite.as(not null)
3867 if callsite.is_broken then return null
3868 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3869 return v.compile_callsite(callsite, args)
3870 end
3871 end
3872
3873 redef class ASendReassignFormExpr
3874 redef fun stmt(v)
3875 do
3876 var recv = v.expr(self.n_expr, null)
3877 var callsite = self.callsite.as(not null)
3878 if callsite.is_broken then return
3879 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3880
3881 var value = v.expr(self.n_value, null)
3882
3883 var left = v.compile_callsite(callsite, args)
3884 assert left != null
3885
3886 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3887 assert res != null
3888
3889 args.add(res)
3890 v.compile_callsite(self.write_callsite.as(not null), args)
3891 end
3892 end
3893
3894 redef class ASuperExpr
3895 redef fun expr(v)
3896 do
3897 var frame = v.frame.as(not null)
3898 var recv = frame.arguments.first
3899
3900 var callsite = self.callsite
3901 if callsite != null then
3902 if callsite.is_broken then return null
3903 var args
3904
3905 if self.n_args.n_exprs.is_empty then
3906 # Add automatic arguments for the super init call
3907 args = [recv]
3908 for i in [0..callsite.msignature.arity[ do
3909 args.add(frame.arguments[i+1])
3910 end
3911 else
3912 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3913 end
3914
3915 # Super init call
3916 var res = v.compile_callsite(callsite, args)
3917 return res
3918 end
3919
3920 var mpropdef = self.mpropdef.as(not null)
3921
3922 var args
3923 if self.n_args.n_exprs.is_empty then
3924 args = frame.arguments
3925 else
3926 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3927 end
3928
3929 # Standard call-next-method
3930 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3931 end
3932 end
3933
3934 redef class ANewExpr
3935 redef fun expr(v)
3936 do
3937 var mtype = self.recvtype
3938 assert mtype != null
3939
3940 if mtype.mclass.name == "NativeArray" then
3941 assert self.n_args.n_exprs.length == 1
3942 var l = v.expr(self.n_args.n_exprs.first, null)
3943 assert mtype isa MGenericType
3944 var elttype = mtype.arguments.first
3945 return v.native_array_instance(elttype, l)
3946 end
3947
3948 var recv = v.init_instance_or_extern(mtype)
3949
3950 var callsite = self.callsite
3951 if callsite == null then return recv
3952 if callsite.is_broken then return null
3953
3954 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3955 var res2 = v.compile_callsite(callsite, args)
3956 if res2 != null then
3957 #self.debug("got {res2} from {mproperty}. drop {recv}")
3958 return res2
3959 end
3960 return recv
3961 end
3962 end
3963
3964 redef class AAttrExpr
3965 redef fun expr(v)
3966 do
3967 var recv = v.expr(self.n_expr, null)
3968 var mproperty = self.mproperty.as(not null)
3969 return v.read_attribute(mproperty, recv)
3970 end
3971 end
3972
3973 redef class AAttrAssignExpr
3974 redef fun expr(v)
3975 do
3976 var recv = v.expr(self.n_expr, null)
3977 var i = v.expr(self.n_value, null)
3978 var mproperty = self.mproperty.as(not null)
3979 v.write_attribute(mproperty, recv, i)
3980 return i
3981 end
3982 end
3983
3984 redef class AAttrReassignExpr
3985 redef fun stmt(v)
3986 do
3987 var recv = v.expr(self.n_expr, null)
3988 var value = v.expr(self.n_value, null)
3989 var mproperty = self.mproperty.as(not null)
3990 var attr = v.read_attribute(mproperty, recv)
3991 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3992 assert res != null
3993 v.write_attribute(mproperty, recv, res)
3994 end
3995 end
3996
3997 redef class AIssetAttrExpr
3998 redef fun expr(v)
3999 do
4000 var recv = v.expr(self.n_expr, null)
4001 var mproperty = self.mproperty.as(not null)
4002 return v.isset_attribute(mproperty, recv)
4003 end
4004 end
4005
4006 redef class AVarargExpr
4007 redef fun expr(v)
4008 do
4009 return v.expr(self.n_expr, null)
4010 end
4011 end
4012
4013 redef class ANamedargExpr
4014 redef fun expr(v)
4015 do
4016 return v.expr(self.n_expr, null)
4017 end
4018 end
4019
4020 redef class ADebugTypeExpr
4021 redef fun stmt(v)
4022 do
4023 # do nothing
4024 end
4025 end
4026
4027 # Utils
4028
4029 redef class Array[E]
4030 # Return a new `Array` with the elements only contened in self and not in `o`
4031 fun -(o: Array[E]): Array[E] do
4032 var res = new Array[E]
4033 for e in self do if not o.has(e) then res.add(e)
4034 return res
4035 end
4036 end
4037
4038 redef class MModule
4039 # All `MProperty` associated to all `MClassDef` of `mclass`
4040 fun properties(mclass: MClass): Set[MProperty] do
4041 if not self.properties_cache.has_key(mclass) then
4042 var properties = new HashSet[MProperty]
4043 var parents = new Array[MClass]
4044 if self.flatten_mclass_hierarchy.has(mclass) then
4045 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
4046 end
4047 for parent in parents do
4048 properties.add_all(self.properties(parent))
4049 end
4050 for mclassdef in mclass.mclassdefs do
4051 if not self.in_importation <= mclassdef.mmodule then continue
4052 for mprop in mclassdef.intro_mproperties do
4053 properties.add(mprop)
4054 end
4055 end
4056 self.properties_cache[mclass] = properties
4057 end
4058 return properties_cache[mclass]
4059 end
4060 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
4061
4062 # Write FFI and nitni results to file
4063 fun finalize_ffi(c: AbstractCompiler) do end
4064
4065 # Give requided addinional system libraries (as given to LD_LIBS)
4066 # Note: can return null instead of an empty set
4067 fun collect_linker_libs: nullable Array[String] do return null
4068 end
4069
4070 # Create a tool context to handle options and paths
4071 var toolcontext = new ToolContext
4072
4073 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
4074
4075 # We do not add other options, so process them now!
4076 toolcontext.process_options(args)
4077
4078 # We need a model to collect stufs
4079 var model = new Model
4080 # An a model builder to parse files
4081 var modelbuilder = new ModelBuilder(model, toolcontext)
4082
4083 var arguments = toolcontext.option_context.rest
4084 if arguments.length > 1 and toolcontext.opt_output.value != null then
4085 print "Option Error: --output needs a single source file. Do you prefer --dir?"
4086 exit 1
4087 end
4088
4089 # Here we load an process all modules passed on the command line
4090 var mmodules = modelbuilder.parse(arguments)
4091
4092 if mmodules.is_empty then toolcontext.quit
4093
4094 modelbuilder.run_phases
4095
4096 for mmodule in mmodules do
4097 toolcontext.info("*** PROCESS {mmodule} ***", 1)
4098 var ms = [mmodule]
4099 toolcontext.run_global_phases(ms)
4100 end