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