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