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