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