nitc: document add_raw_abort
[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 ifneq ($(findstring MINGW64,$(uname_S)),)
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 maybe_null(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 maybe_null(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 # Generate abort without a message.
1836 #
1837 # Used when one need a more complex message.
1838 fun add_raw_abort
1839 do
1840 var current_node = self.current_node
1841 if current_node != null and current_node.location.file != null and
1842 current_node.location.file.mmodule != null then
1843 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1844 self.require_declaration(f)
1845 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1846 else
1847 self.add("PRINT_ERROR(\"\\n\");")
1848 end
1849 self.add("fatal_exit(1);")
1850 end
1851
1852 # Add a dynamic cast
1853 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1854 do
1855 var res = self.type_test(value, mtype, tag)
1856 self.add("if (unlikely(!{res})) \{")
1857 var cn = self.class_name_string(value)
1858 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1859 self.add_raw_abort
1860 self.add("\}")
1861 end
1862
1863 # Generate a return with the value `s`
1864 fun ret(s: RuntimeVariable)
1865 do
1866 self.assign(self.frame.returnvar.as(not null), s)
1867 self.add("goto {self.frame.returnlabel.as(not null)};")
1868 end
1869
1870 # Compile a statement (if any)
1871 fun stmt(nexpr: nullable AExpr)
1872 do
1873 if nexpr == null then return
1874 if nexpr.is_broken then
1875 # Untyped expression.
1876 # Might mean dead code or invalid code
1877 # so aborts
1878 add_abort("FATAL: bad statement executed.")
1879 return
1880 end
1881
1882 var narray = nexpr.comprehension
1883 if narray != null then
1884 var recv = frame.comprehension.as(not null)
1885 var val = expr(nexpr, narray.element_mtype)
1886 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1887 return
1888 end
1889
1890 var old = self.current_node
1891 self.current_node = nexpr
1892 nexpr.stmt(self)
1893 self.current_node = old
1894 end
1895
1896 # Compile an expression an return its result
1897 # `mtype` is the expected return type, pass null if no specific type is expected.
1898 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1899 do
1900 var old = self.current_node
1901 self.current_node = nexpr
1902
1903 var res = null
1904 if nexpr.mtype != null then
1905 res = nexpr.expr(self)
1906 end
1907
1908 if res == null then
1909 # Untyped expression.
1910 # Might mean dead code or invalid code.
1911 # so aborts
1912 add_abort("FATAL: bad expression executed.")
1913 # and return a placebo result to please the C compiler
1914 if mtype == null then mtype = compiler.mainmodule.object_type
1915 res = new_var(mtype)
1916
1917 self.current_node = old
1918 return res
1919 end
1920
1921 if mtype != null then
1922 mtype = self.anchor(mtype)
1923 res = self.autobox(res, mtype)
1924 end
1925 res = autoadapt(res, nexpr.mtype.as(not null))
1926 var implicit_cast_to = nexpr.implicit_cast_to
1927 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1928 add_cast(res, implicit_cast_to, "auto")
1929 res = autoadapt(res, implicit_cast_to)
1930 end
1931 self.current_node = old
1932 return res
1933 end
1934
1935 # Alias for `self.expr(nexpr, self.bool_type)`
1936 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1937
1938 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1939 fun debug(message: String)
1940 do
1941 var node = self.current_node
1942 if node == null then
1943 print "?: {message}"
1944 else
1945 node.debug(message)
1946 end
1947 self.add("/* DEBUG: {message} */")
1948 end
1949 end
1950
1951 # A C function associated to a Nit method
1952 # Because of customization, a given Nit method can be compiler more that once
1953 abstract class AbstractRuntimeFunction
1954
1955 type COMPILER: AbstractCompiler
1956 type VISITOR: AbstractCompilerVisitor
1957
1958 # The associated Nit method
1959 var mmethoddef: MMethodDef
1960
1961 # The mangled c name of the runtime_function
1962 # Subclasses should redefine `build_c_name` instead
1963 fun c_name: String
1964 do
1965 var res = self.c_name_cache
1966 if res != null then return res
1967 res = self.build_c_name
1968 self.c_name_cache = res
1969 return res
1970 end
1971
1972 # Non cached version of `c_name`
1973 protected fun build_c_name: String is abstract
1974
1975 protected var c_name_cache: nullable String = null is writable
1976
1977 # Implements a call of the runtime_function
1978 # May inline the body or generate a C function call
1979 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1980
1981 # Generate the code for the `AbstractRuntimeFunction`
1982 # Warning: compile more than once compilation makes CC unhappy
1983 fun compile_to_c(compiler: COMPILER) is abstract
1984 end
1985
1986 # A runtime variable hold a runtime value in C.
1987 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1988 #
1989 # 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.
1990 class RuntimeVariable
1991 # The name of the variable in the C code
1992 var name: String
1993
1994 # The static type of the variable (as declard in C)
1995 var mtype: MType
1996
1997 # The current casted type of the variable (as known in Nit)
1998 var mcasttype: MType is writable
1999
2000 # If the variable exaclty a mcasttype?
2001 # false (usual value) means that the variable is a mcasttype or a subtype.
2002 var is_exact: Bool = false is writable
2003
2004 init
2005 do
2006 assert not mtype.need_anchor
2007 assert not mcasttype.need_anchor
2008 end
2009
2010 redef fun to_s do return name
2011
2012 redef fun inspect
2013 do
2014 var exact_str
2015 if self.is_exact then
2016 exact_str = " exact"
2017 else
2018 exact_str = ""
2019 end
2020 var type_str
2021 if self.mtype == self.mcasttype then
2022 type_str = "{mtype}{exact_str}"
2023 else
2024 type_str = "{mtype}({mcasttype}{exact_str})"
2025 end
2026 return "<{name}:{type_str}>"
2027 end
2028 end
2029
2030 # The static context of a visited property in a `AbstractCompilerVisitor`
2031 class StaticFrame
2032
2033 type VISITOR: AbstractCompilerVisitor
2034
2035 # The associated visitor
2036 var visitor: VISITOR
2037
2038 # The executed property.
2039 # A Method in case of a call, an attribute in case of a default initialization.
2040 var mpropdef: MPropDef
2041
2042 # The static type of the receiver
2043 var receiver: MClassType
2044
2045 # Arguments of the method (the first is the receiver)
2046 var arguments: Array[RuntimeVariable]
2047
2048 # The runtime_variable associated to the return (in a function)
2049 var returnvar: nullable RuntimeVariable = null is writable
2050
2051 # The label at the end of the property
2052 var returnlabel: nullable String = null is writable
2053
2054 # Labels associated to a each escapemarks.
2055 # Because of inlinings, escape-marks must be associated to their context (the frame)
2056 private var escapemark_names = new HashMap[EscapeMark, String]
2057
2058 # The array comprehension currently filled, if any
2059 private var comprehension: nullable RuntimeVariable = null
2060 end
2061
2062 redef class MType
2063 # Return the C type associated to a given Nit static type
2064 fun ctype: String do return "val*"
2065
2066 # C type outside of the compiler code and in boxes
2067 fun ctype_extern: String do return "val*"
2068
2069 # Short name of the `ctype` to use in unions
2070 fun ctypename: String do return "val"
2071
2072 # Is the associated C type a primitive one?
2073 #
2074 # ENSURE `result == (ctype != "val*")`
2075 fun is_c_primitive: Bool do return false
2076 end
2077
2078 redef class MClassType
2079
2080 redef var ctype is lazy do
2081 if mclass.name == "Int" then
2082 return "long"
2083 else if mclass.name == "Bool" then
2084 return "short int"
2085 else if mclass.name == "Char" then
2086 return "uint32_t"
2087 else if mclass.name == "Float" then
2088 return "double"
2089 else if mclass.name == "Int8" then
2090 return "int8_t"
2091 else if mclass.name == "Byte" then
2092 return "unsigned char"
2093 else if mclass.name == "Int16" then
2094 return "int16_t"
2095 else if mclass.name == "UInt16" then
2096 return "uint16_t"
2097 else if mclass.name == "Int32" then
2098 return "int32_t"
2099 else if mclass.name == "UInt32" then
2100 return "uint32_t"
2101 else if mclass.name == "CString" then
2102 return "char*"
2103 else if mclass.name == "NativeArray" then
2104 return "val*"
2105 else
2106 return "val*"
2107 end
2108 end
2109
2110 redef var is_c_primitive is lazy do return ctype != "val*"
2111
2112 redef fun ctype_extern: String
2113 do
2114 if mclass.kind == extern_kind then
2115 return "void*"
2116 else
2117 return ctype
2118 end
2119 end
2120
2121 redef fun ctypename: String
2122 do
2123 if mclass.name == "Int" then
2124 return "l"
2125 else if mclass.name == "Bool" then
2126 return "s"
2127 else if mclass.name == "Char" then
2128 return "c"
2129 else if mclass.name == "Float" then
2130 return "d"
2131 else if mclass.name == "Int8" then
2132 return "i8"
2133 else if mclass.name == "Byte" then
2134 return "b"
2135 else if mclass.name == "Int16" then
2136 return "i16"
2137 else if mclass.name == "UInt16" then
2138 return "u16"
2139 else if mclass.name == "Int32" then
2140 return "i32"
2141 else if mclass.name == "UInt32" then
2142 return "u32"
2143 else if mclass.name == "CString" then
2144 return "str"
2145 else if mclass.name == "NativeArray" then
2146 #return "{self.arguments.first.ctype}*"
2147 return "val"
2148 else
2149 return "val"
2150 end
2151 end
2152 end
2153
2154 redef class MPropDef
2155 type VISITOR: AbstractCompilerVisitor
2156 end
2157
2158 redef class MMethodDef
2159 # Can the body be inlined?
2160 fun can_inline(v: VISITOR): Bool
2161 do
2162 if is_abstract then return true
2163 if constant_value != null then return true
2164 var modelbuilder = v.compiler.modelbuilder
2165 var node = modelbuilder.mpropdef2node(self)
2166 if node isa APropdef then
2167 return node.can_inline
2168 else if node isa AClassdef then
2169 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2170 return true
2171 else if node == null then
2172 return true
2173 else
2174 abort
2175 end
2176 end
2177
2178 # Inline the body in another visitor
2179 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2180 do
2181 var modelbuilder = v.compiler.modelbuilder
2182 var val = constant_value
2183 var node = modelbuilder.mpropdef2node(self)
2184
2185 if is_abstract then
2186 var cn = v.class_name_string(arguments.first)
2187 v.current_node = node
2188 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2189 v.add_raw_abort
2190 return null
2191 end
2192
2193 if node isa APropdef then
2194 var oldnode = v.current_node
2195 v.current_node = node
2196 self.compile_parameter_check(v, arguments)
2197 node.compile_to_c(v, self, arguments)
2198 v.current_node = oldnode
2199 else if node isa AClassdef then
2200 var oldnode = v.current_node
2201 v.current_node = node
2202 self.compile_parameter_check(v, arguments)
2203 node.compile_to_c(v, self, arguments)
2204 v.current_node = oldnode
2205 else if val != null then
2206 v.ret(v.value_instance(val))
2207 else
2208 abort
2209 end
2210 return null
2211 end
2212
2213 # Generate type checks in the C code to check covariant parameters
2214 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2215 do
2216 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2217
2218 var msignature = self.msignature.as(not null)
2219
2220 for i in [0..msignature.arity[ do
2221 var mp = msignature.mparameters[i]
2222 # skip test for vararg since the array is instantiated with the correct polymorphic type
2223 if mp.is_vararg then continue
2224
2225 # skip if the cast is not required
2226 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2227 if not origmtype.need_anchor then continue
2228
2229 # get the parameter type
2230 var mtype = mp.mtype
2231
2232 # generate the cast
2233 # note that v decides if and how to implements the cast
2234 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2235 v.add_cast(arguments[i+1], mtype, "covariance")
2236 end
2237 end
2238 end
2239
2240 # Node visit
2241
2242 redef class APropdef
2243 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2244 do
2245 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2246 debug("Not yet implemented")
2247 end
2248
2249 fun can_inline: Bool do return true
2250 end
2251
2252 redef class AMethPropdef
2253 redef fun compile_to_c(v, mpropdef, arguments)
2254 do
2255 # Call the implicit super-init
2256 var auto_super_inits = self.auto_super_inits
2257 if auto_super_inits != null then
2258 var args = [arguments.first]
2259 for auto_super_init in auto_super_inits do
2260 assert auto_super_init.mproperty != mpropdef.mproperty
2261 args.clear
2262 for i in [0..auto_super_init.msignature.arity+1[ do
2263 args.add(arguments[i])
2264 end
2265 assert auto_super_init.mproperty != mpropdef.mproperty
2266 v.compile_callsite(auto_super_init, args)
2267 end
2268 end
2269 if auto_super_call then
2270 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2271 end
2272
2273 # Try special compilation
2274 if mpropdef.is_intern then
2275 if compile_intern_to_c(v, mpropdef, arguments) then return
2276 end
2277 if mpropdef.is_extern then
2278 if mpropdef.mproperty.is_init then
2279 if compile_externinit_to_c(v, mpropdef, arguments) then return
2280 else
2281 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2282 end
2283 end
2284
2285 # Compile block if any
2286 var n_block = n_block
2287 if n_block != null then
2288 for i in [0..mpropdef.msignature.arity[ do
2289 var variable = self.n_signature.n_params[i].variable.as(not null)
2290 v.assign(v.variable(variable), arguments[i+1])
2291 end
2292 v.stmt(n_block)
2293 return
2294 end
2295
2296 # We have a problem
2297 var cn = v.class_name_string(arguments.first)
2298 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2299 v.add_raw_abort
2300 end
2301
2302 redef fun can_inline
2303 do
2304 if self.auto_super_inits != null then return false
2305 var nblock = self.n_block
2306 if nblock == null then return true
2307 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2308 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2309 return false
2310 end
2311
2312 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2313 do
2314 var pname = mpropdef.mproperty.name
2315 var cname = mpropdef.mclassdef.mclass.name
2316 var ret = mpropdef.msignature.return_mtype
2317 if ret != null then
2318 ret = v.resolve_for(ret, arguments.first)
2319 end
2320 if pname != "==" and pname != "!=" then
2321 v.adapt_signature(mpropdef, arguments)
2322 v.unbox_signature_extern(mpropdef, arguments)
2323 end
2324 if cname == "Int" then
2325 if pname == "output" then
2326 v.add("printf(\"%ld\\n\", {arguments.first});")
2327 return true
2328 else if pname == "object_id" then
2329 v.ret(arguments.first)
2330 return true
2331 else if pname == "+" then
2332 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2333 return true
2334 else if pname == "-" then
2335 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2336 return true
2337 else if pname == "unary -" then
2338 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2339 return true
2340 else if pname == "unary +" then
2341 v.ret(arguments[0])
2342 return true
2343 else if pname == "*" then
2344 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2345 return true
2346 else if pname == "/" then
2347 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2348 return true
2349 else if pname == "%" then
2350 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2351 return true
2352 else if pname == "==" then
2353 v.ret(v.equal_test(arguments[0], arguments[1]))
2354 return true
2355 else if pname == "!=" then
2356 var res = v.equal_test(arguments[0], arguments[1])
2357 v.ret(v.new_expr("!{res}", ret.as(not null)))
2358 return true
2359 else if pname == "<" then
2360 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2361 return true
2362 else if pname == ">" then
2363 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2364 return true
2365 else if pname == "<=" then
2366 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2367 return true
2368 else if pname == ">=" then
2369 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2370 return true
2371 else if pname == "to_i8" then
2372 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2373 return true
2374 else if pname == "to_i16" then
2375 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2376 return true
2377 else if pname == "to_u16" then
2378 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2379 return true
2380 else if pname == "to_i32" then
2381 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2382 return true
2383 else if pname == "to_u32" then
2384 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2385 return true
2386 else if pname == "to_f" then
2387 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2388 return true
2389 else if pname == "to_b" then
2390 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2391 return true
2392 else if pname == "code_point" then
2393 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2394 return true
2395 else if pname == "&" then
2396 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2397 return true
2398 else if pname == "|" then
2399 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2400 return true
2401 else if pname == ">>" then
2402 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2403 return true
2404 else if pname == "<<" then
2405 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2406 return true
2407 end
2408 else if cname == "Char" then
2409 if pname == "object_id" then
2410 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2411 return true
2412 else if pname == "successor" then
2413 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2414 return true
2415 else if pname == "predecessor" then
2416 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2417 return true
2418 else if pname == "==" then
2419 v.ret(v.equal_test(arguments[0], arguments[1]))
2420 return true
2421 else if pname == "!=" then
2422 var res = v.equal_test(arguments[0], arguments[1])
2423 v.ret(v.new_expr("!{res}", ret.as(not null)))
2424 return true
2425 else if pname == "<" then
2426 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2427 return true
2428 else if pname == ">" then
2429 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2430 return true
2431 else if pname == "<=" then
2432 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2433 return true
2434 else if pname == ">=" then
2435 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2436 return true
2437 else if pname == "to_i" then
2438 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2439 return true
2440 else if pname == "code_point" then
2441 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2442 return true
2443 end
2444 else if cname == "Byte" then
2445 if pname == "output" then
2446 v.add("printf(\"%x\\n\", {arguments.first});")
2447 return true
2448 else if pname == "object_id" then
2449 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2450 return true
2451 else if pname == "+" then
2452 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2453 return true
2454 else if pname == "-" then
2455 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2456 return true
2457 else if pname == "unary -" then
2458 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2459 return true
2460 else if pname == "unary +" then
2461 v.ret(arguments[0])
2462 return true
2463 else if pname == "*" then
2464 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2465 return true
2466 else if pname == "/" then
2467 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2468 return true
2469 else if pname == "%" then
2470 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2471 return true
2472 else if pname == "==" then
2473 v.ret(v.equal_test(arguments[0], arguments[1]))
2474 return true
2475 else if pname == "!=" then
2476 var res = v.equal_test(arguments[0], arguments[1])
2477 v.ret(v.new_expr("!{res}", ret.as(not null)))
2478 return true
2479 else if pname == "<" then
2480 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2481 return true
2482 else if pname == ">" then
2483 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2484 return true
2485 else if pname == "<=" then
2486 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2487 return true
2488 else if pname == ">=" then
2489 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2490 return true
2491 else if pname == ">>" then
2492 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2493 return true
2494 else if pname == "<<" then
2495 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2496 return true
2497 else if pname == "&" then
2498 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2499 return true
2500 else if pname == "to_i" then
2501 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2502 return true
2503 else if pname == "to_f" then
2504 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2505 return true
2506 else if pname == "to_i8" then
2507 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2508 return true
2509 else if pname == "to_i16" then
2510 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2511 return true
2512 else if pname == "to_u16" then
2513 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2514 return true
2515 else if pname == "to_i32" then
2516 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2517 return true
2518 else if pname == "to_u32" then
2519 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2520 return true
2521 else if pname == "ascii" then
2522 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2523 return true
2524 end
2525 else if cname == "Bool" then
2526 if pname == "output" then
2527 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2528 return true
2529 else if pname == "object_id" then
2530 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2531 return true
2532 else if pname == "==" then
2533 v.ret(v.equal_test(arguments[0], arguments[1]))
2534 return true
2535 else if pname == "!=" then
2536 var res = v.equal_test(arguments[0], arguments[1])
2537 v.ret(v.new_expr("!{res}", ret.as(not null)))
2538 return true
2539 end
2540 else if cname == "Float" then
2541 if pname == "output" then
2542 v.add("printf(\"%f\\n\", {arguments.first});")
2543 return true
2544 else if pname == "object_id" then
2545 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2546 return true
2547 else if pname == "+" then
2548 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2549 return true
2550 else if pname == "-" then
2551 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2552 return true
2553 else if pname == "unary -" then
2554 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2555 return true
2556 else if pname == "unary +" then
2557 v.ret(arguments[0])
2558 return true
2559 else if pname == "succ" then
2560 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2561 return true
2562 else if pname == "prec" then
2563 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2564 return true
2565 else if pname == "*" then
2566 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2567 return true
2568 else if pname == "/" then
2569 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2570 return true
2571 else if pname == "==" then
2572 v.ret(v.equal_test(arguments[0], arguments[1]))
2573 return true
2574 else if pname == "!=" then
2575 var res = v.equal_test(arguments[0], arguments[1])
2576 v.ret(v.new_expr("!{res}", ret.as(not null)))
2577 return true
2578 else if pname == "<" then
2579 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2580 return true
2581 else if pname == ">" then
2582 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2583 return true
2584 else if pname == "<=" then
2585 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2586 return true
2587 else if pname == ">=" then
2588 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2589 return true
2590 else if pname == "to_i" then
2591 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2592 return true
2593 else if pname == "to_b" then
2594 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2595 return true
2596 else if pname == "to_i8" then
2597 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2598 return true
2599 else if pname == "to_i16" then
2600 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2601 return true
2602 else if pname == "to_u16" then
2603 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2604 return true
2605 else if pname == "to_i32" then
2606 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2607 return true
2608 else if pname == "to_u32" then
2609 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2610 return true
2611 end
2612 else if cname == "CString" then
2613 if pname == "[]" then
2614 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2615 return true
2616 else if pname == "[]=" then
2617 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2618 return true
2619 else if pname == "copy_to" then
2620 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2621 return true
2622 else if pname == "atoi" then
2623 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2624 return true
2625 else if pname == "fast_cstring" then
2626 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2627 return true
2628 else if pname == "==" then
2629 v.ret(v.equal_test(arguments[0], arguments[1]))
2630 return true
2631 else if pname == "!=" then
2632 var res = v.equal_test(arguments[0], arguments[1])
2633 v.ret(v.new_expr("!{res}", ret.as(not null)))
2634 return true
2635 else if pname == "new" then
2636 var alloc = v.nit_alloc(arguments[1].to_s, "CString")
2637 v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
2638 return true
2639 else if pname == "fetch_4_chars" then
2640 v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
2641 return true
2642 else if pname == "fetch_4_hchars" then
2643 v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
2644 return true
2645 end
2646 else if cname == "NativeArray" then
2647 return v.native_array_def(pname, ret, arguments)
2648 else if cname == "Int8" then
2649 if pname == "output" then
2650 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2651 return true
2652 else if pname == "object_id" then
2653 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2654 return true
2655 else if pname == "+" then
2656 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2657 return true
2658 else if pname == "-" then
2659 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2660 return true
2661 else if pname == "unary -" then
2662 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2663 return true
2664 else if pname == "unary +" then
2665 v.ret(arguments[0])
2666 return true
2667 else if pname == "*" then
2668 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2669 return true
2670 else if pname == "/" then
2671 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2672 return true
2673 else if pname == "%" then
2674 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2675 return true
2676 else if pname == "<<" then
2677 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2678 return true
2679 else if pname == ">>" then
2680 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2681 return true
2682 else if pname == "==" then
2683 v.ret(v.equal_test(arguments[0], arguments[1]))
2684 return true
2685 else if pname == "!=" then
2686 var res = v.equal_test(arguments[0], arguments[1])
2687 v.ret(v.new_expr("!{res}", ret.as(not null)))
2688 return true
2689 else if pname == "<" then
2690 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2691 return true
2692 else if pname == ">" then
2693 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2694 return true
2695 else if pname == "<=" then
2696 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2697 return true
2698 else if pname == ">=" then
2699 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2700 return true
2701 else if pname == "to_i" then
2702 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2703 return true
2704 else if pname == "to_b" then
2705 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2706 return true
2707 else if pname == "to_i16" then
2708 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2709 return true
2710 else if pname == "to_u16" then
2711 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2712 return true
2713 else if pname == "to_i32" then
2714 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2715 return true
2716 else if pname == "to_u32" then
2717 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2718 return true
2719 else if pname == "to_f" then
2720 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2721 return true
2722 else if pname == "&" then
2723 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2724 return true
2725 else if pname == "|" then
2726 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2727 return true
2728 else if pname == "^" then
2729 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2730 return true
2731 else if pname == "unary ~" then
2732 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2733 return true
2734 end
2735 else if cname == "Int16" then
2736 if pname == "output" then
2737 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2738 return true
2739 else if pname == "object_id" then
2740 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2741 return true
2742 else if pname == "+" then
2743 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2744 return true
2745 else if pname == "-" then
2746 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2747 return true
2748 else if pname == "unary -" then
2749 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2750 return true
2751 else if pname == "unary +" then
2752 v.ret(arguments[0])
2753 return true
2754 else if pname == "*" then
2755 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2756 return true
2757 else if pname == "/" then
2758 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2759 return true
2760 else if pname == "%" then
2761 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2762 return true
2763 else if pname == "<<" then
2764 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2765 return true
2766 else if pname == ">>" then
2767 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2768 return true
2769 else if pname == "==" then
2770 v.ret(v.equal_test(arguments[0], arguments[1]))
2771 return true
2772 else if pname == "!=" then
2773 var res = v.equal_test(arguments[0], arguments[1])
2774 v.ret(v.new_expr("!{res}", ret.as(not null)))
2775 return true
2776 else if pname == "<" then
2777 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2778 return true
2779 else if pname == ">" then
2780 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2781 return true
2782 else if pname == "<=" then
2783 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2784 return true
2785 else if pname == ">=" then
2786 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2787 return true
2788 else if pname == "to_i" then
2789 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2790 return true
2791 else if pname == "to_b" then
2792 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2793 return true
2794 else if pname == "to_i8" then
2795 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2796 return true
2797 else if pname == "to_u16" then
2798 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2799 return true
2800 else if pname == "to_i32" then
2801 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2802 return true
2803 else if pname == "to_u32" then
2804 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2805 return true
2806 else if pname == "to_f" then
2807 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2808 return true
2809 else if pname == "&" then
2810 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2811 return true
2812 else if pname == "|" then
2813 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2814 return true
2815 else if pname == "^" then
2816 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2817 return true
2818 else if pname == "unary ~" then
2819 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2820 return true
2821 end
2822 else if cname == "UInt16" then
2823 if pname == "output" then
2824 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2825 return true
2826 else if pname == "object_id" then
2827 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2828 return true
2829 else if pname == "+" then
2830 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2831 return true
2832 else if pname == "-" then
2833 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2834 return true
2835 else if pname == "unary -" then
2836 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2837 return true
2838 else if pname == "unary +" then
2839 v.ret(arguments[0])
2840 return true
2841 else if pname == "*" then
2842 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2843 return true
2844 else if pname == "/" then
2845 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2846 return true
2847 else if pname == "%" then
2848 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2849 return true
2850 else if pname == "<<" then
2851 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2852 return true
2853 else if pname == ">>" then
2854 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2855 return true
2856 else if pname == "==" then
2857 v.ret(v.equal_test(arguments[0], arguments[1]))
2858 return true
2859 else if pname == "!=" then
2860 var res = v.equal_test(arguments[0], arguments[1])
2861 v.ret(v.new_expr("!{res}", ret.as(not null)))
2862 return true
2863 else if pname == "<" then
2864 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2865 return true
2866 else if pname == ">" then
2867 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2868 return true
2869 else if pname == "<=" then
2870 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2871 return true
2872 else if pname == ">=" then
2873 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2874 return true
2875 else if pname == "to_i" then
2876 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2877 return true
2878 else if pname == "to_b" then
2879 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2880 return true
2881 else if pname == "to_i8" then
2882 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2883 return true
2884 else if pname == "to_i16" then
2885 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2886 return true
2887 else if pname == "to_i32" then
2888 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2889 return true
2890 else if pname == "to_u32" then
2891 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2892 return true
2893 else if pname == "to_f" then
2894 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2895 return true
2896 else if pname == "&" then
2897 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2898 return true
2899 else if pname == "|" then
2900 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2901 return true
2902 else if pname == "^" then
2903 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2904 return true
2905 else if pname == "unary ~" then
2906 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2907 return true
2908 end
2909 else if cname == "Int32" then
2910 if pname == "output" then
2911 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2912 return true
2913 else if pname == "object_id" then
2914 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2915 return true
2916 else if pname == "+" then
2917 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2918 return true
2919 else if pname == "-" then
2920 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2921 return true
2922 else if pname == "unary -" then
2923 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2924 return true
2925 else if pname == "unary +" then
2926 v.ret(arguments[0])
2927 return true
2928 else if pname == "*" then
2929 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2930 return true
2931 else if pname == "/" then
2932 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2933 return true
2934 else if pname == "%" then
2935 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2936 return true
2937 else if pname == "<<" then
2938 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2939 return true
2940 else if pname == ">>" then
2941 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2942 return true
2943 else if pname == "==" then
2944 v.ret(v.equal_test(arguments[0], arguments[1]))
2945 return true
2946 else if pname == "!=" then
2947 var res = v.equal_test(arguments[0], arguments[1])
2948 v.ret(v.new_expr("!{res}", ret.as(not null)))
2949 return true
2950 else if pname == "<" then
2951 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2952 return true
2953 else if pname == ">" then
2954 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2955 return true
2956 else if pname == "<=" then
2957 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2958 return true
2959 else if pname == ">=" then
2960 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2961 return true
2962 else if pname == "to_i" then
2963 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2964 return true
2965 else if pname == "to_b" then
2966 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2967 return true
2968 else if pname == "to_i8" then
2969 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2970 return true
2971 else if pname == "to_i16" then
2972 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2973 return true
2974 else if pname == "to_u16" then
2975 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2976 return true
2977 else if pname == "to_u32" then
2978 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2979 return true
2980 else if pname == "to_f" then
2981 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2982 return true
2983 else if pname == "&" then
2984 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2985 return true
2986 else if pname == "|" then
2987 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2988 return true
2989 else if pname == "^" then
2990 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2991 return true
2992 else if pname == "unary ~" then
2993 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2994 return true
2995 end
2996 else if cname == "UInt32" then
2997 if pname == "output" then
2998 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2999 return true
3000 else if pname == "object_id" then
3001 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3002 return true
3003 else if pname == "+" then
3004 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3005 return true
3006 else if pname == "-" then
3007 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3008 return true
3009 else if pname == "unary -" then
3010 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3011 return true
3012 else if pname == "unary +" then
3013 v.ret(arguments[0])
3014 return true
3015 else if pname == "*" then
3016 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3017 return true
3018 else if pname == "/" then
3019 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3020 return true
3021 else if pname == "%" then
3022 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3023 return true
3024 else if pname == "<<" then
3025 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3026 return true
3027 else if pname == ">>" then
3028 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3029 return true
3030 else if pname == "==" then
3031 v.ret(v.equal_test(arguments[0], arguments[1]))
3032 return true
3033 else if pname == "!=" then
3034 var res = v.equal_test(arguments[0], arguments[1])
3035 v.ret(v.new_expr("!{res}", ret.as(not null)))
3036 return true
3037 else if pname == "<" then
3038 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3039 return true
3040 else if pname == ">" then
3041 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3042 return true
3043 else if pname == "<=" then
3044 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3045 return true
3046 else if pname == ">=" then
3047 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3048 return true
3049 else if pname == "to_i" then
3050 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3051 return true
3052 else if pname == "to_b" then
3053 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3054 return true
3055 else if pname == "to_i8" then
3056 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3057 return true
3058 else if pname == "to_i16" then
3059 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3060 return true
3061 else if pname == "to_u16" then
3062 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3063 return true
3064 else if pname == "to_i32" then
3065 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3066 return true
3067 else if pname == "to_f" then
3068 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3069 return true
3070 else if pname == "&" then
3071 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3072 return true
3073 else if pname == "|" then
3074 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3075 return true
3076 else if pname == "^" then
3077 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3078 return true
3079 else if pname == "unary ~" then
3080 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3081 return true
3082 end
3083 end
3084 if pname == "exit" then
3085 v.add("exit((int){arguments[1]});")
3086 return true
3087 else if pname == "sys" then
3088 v.ret(v.new_expr("glob_sys", ret.as(not null)))
3089 return true
3090 else if pname == "object_id" then
3091 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3092 return true
3093 else if pname == "is_same_type" then
3094 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
3095 return true
3096 else if pname == "is_same_instance" then
3097 v.ret(v.equal_test(arguments[0], arguments[1]))
3098 return true
3099 else if pname == "output_class_name" then
3100 var nat = v.class_name_string(arguments.first)
3101 v.add("printf(\"%s\\n\", {nat});")
3102 return true
3103 else if pname == "native_class_name" then
3104 var nat = v.class_name_string(arguments.first)
3105 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
3106 return true
3107 else if pname == "force_garbage_collection" then
3108 v.add("nit_gcollect();")
3109 return true
3110 else if pname == "native_argc" then
3111 v.ret(v.new_expr("glob_argc", ret.as(not null)))
3112 return true
3113 else if pname == "native_argv" then
3114 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
3115 return true
3116 end
3117 return false
3118 end
3119
3120 # Compile an extern method
3121 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3122 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3123 do
3124 var externname
3125 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3126 if at != null and at.n_args.length == 1 then
3127 externname = at.arg_as_string(v.compiler.modelbuilder)
3128 if externname == null then return false
3129 else
3130 return false
3131 end
3132 v.add_extern(mpropdef.mclassdef.mmodule)
3133 var res: nullable RuntimeVariable = null
3134 var ret = mpropdef.msignature.return_mtype
3135 if ret != null then
3136 ret = v.resolve_for(ret, arguments.first)
3137 res = v.new_var_extern(ret)
3138 end
3139 v.adapt_signature(mpropdef, arguments)
3140 v.unbox_signature_extern(mpropdef, arguments)
3141
3142 if res == null then
3143 v.add("{externname}({arguments.join(", ")});")
3144 else
3145 v.add("{res} = {externname}({arguments.join(", ")});")
3146 res = v.box_extern(res, ret.as(not null))
3147 v.ret(res)
3148 end
3149 return true
3150 end
3151
3152 # Compile an extern factory
3153 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3154 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3155 do
3156 var externname
3157 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3158 if at != null then
3159 externname = at.arg_as_string(v.compiler.modelbuilder)
3160 if externname == null then return false
3161 else
3162 return false
3163 end
3164 v.add_extern(mpropdef.mclassdef.mmodule)
3165 v.adapt_signature(mpropdef, arguments)
3166 v.unbox_signature_extern(mpropdef, arguments)
3167 var ret = arguments.first.mtype
3168 var res = v.new_var_extern(ret)
3169
3170 arguments.shift
3171
3172 v.add("{res} = {externname}({arguments.join(", ")});")
3173 res = v.box_extern(res, ret)
3174 v.ret(res)
3175 return true
3176 end
3177 end
3178
3179 redef class AAttrPropdef
3180 redef fun can_inline: Bool do return not is_lazy
3181
3182 redef fun compile_to_c(v, mpropdef, arguments)
3183 do
3184 if mpropdef == mreadpropdef then
3185 assert arguments.length == 1
3186 var recv = arguments.first
3187 var res
3188 if is_lazy then
3189 var set
3190 var ret = self.mtype
3191 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3192 var guard = self.mlazypropdef.mproperty
3193 if useiset then
3194 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3195 else
3196 set = v.read_attribute(guard, recv)
3197 end
3198 v.add("if(likely({set})) \{")
3199 res = v.read_attribute(self.mpropdef.mproperty, recv)
3200 v.add("\} else \{")
3201
3202 var value = evaluate_expr(v, recv)
3203
3204 v.assign(res, value)
3205 if not useiset then
3206 var true_v = v.bool_instance(true)
3207 v.write_attribute(guard, arguments.first, true_v)
3208 end
3209 v.add("\}")
3210 else
3211 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3212 end
3213 v.assign(v.frame.returnvar.as(not null), res)
3214 else if mpropdef == mwritepropdef then
3215 assert arguments.length == 2
3216 var recv = arguments.first
3217 var arg = arguments[1]
3218 if is_optional and v.maybe_null(arg) then
3219 var value = v.new_var(self.mpropdef.static_mtype.as(not null))
3220 v.add("if ({arg} == NULL) \{")
3221 v.assign(value, evaluate_expr(v, recv))
3222 v.add("\} else \{")
3223 v.assign(value, arg)
3224 v.add("\}")
3225 arg = value
3226 end
3227 v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
3228 if is_lazy then
3229 var ret = self.mtype
3230 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3231 if not useiset then
3232 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3233 end
3234 end
3235 else
3236 abort
3237 end
3238 end
3239
3240 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3241 do
3242 if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3243 end
3244
3245 # Evaluate, store and return the default value of the attribute
3246 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3247 do
3248 var oldnode = v.current_node
3249 v.current_node = self
3250 var old_frame = v.frame
3251 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3252 v.frame = frame
3253
3254 var value
3255 var mtype = self.mtype
3256 assert mtype != null
3257
3258 var nexpr = self.n_expr
3259 var nblock = self.n_block
3260 if nexpr != null then
3261 value = v.expr(nexpr, mtype)
3262 else if nblock != null then
3263 value = v.new_var(mtype)
3264 frame.returnvar = value
3265 frame.returnlabel = v.get_name("RET_LABEL")
3266 v.add("\{")
3267 v.stmt(nblock)
3268 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3269 v.add("\}")
3270 else
3271 abort
3272 end
3273
3274 v.write_attribute(self.mpropdef.mproperty, recv, value)
3275
3276 v.frame = old_frame
3277 v.current_node = oldnode
3278
3279 return value
3280 end
3281
3282 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3283 do
3284 var nexpr = self.n_expr
3285 if nexpr != null then return
3286
3287 var oldnode = v.current_node
3288 v.current_node = self
3289 var old_frame = v.frame
3290 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3291 v.frame = frame
3292 # Force read to check the initialization
3293 v.read_attribute(self.mpropdef.mproperty, recv)
3294 v.frame = old_frame
3295 v.current_node = oldnode
3296 end
3297 end
3298
3299 redef class AClassdef
3300 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3301 do
3302 if mpropdef.mproperty.is_root_init then
3303 assert arguments.length == 1
3304 if not mpropdef.is_intro then
3305 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3306 end
3307 return
3308 else
3309 abort
3310 end
3311 end
3312 end
3313
3314 redef class AExpr
3315 # Try to compile self as an expression
3316 # Do not call this method directly, use `v.expr` instead
3317 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3318 do
3319 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3320 var mtype = self.mtype
3321 if mtype == null then
3322 return null
3323 else
3324 var res = v.new_var(mtype)
3325 v.add("/* {res} = NOT YET {class_name} */")
3326 return res
3327 end
3328 end
3329
3330 # Try to compile self as a statement
3331 # Do not call this method directly, use `v.stmt` instead
3332 private fun stmt(v: AbstractCompilerVisitor)
3333 do
3334 expr(v)
3335 end
3336 end
3337
3338 redef class ABlockExpr
3339 redef fun stmt(v)
3340 do
3341 for e in self.n_expr do v.stmt(e)
3342 end
3343 redef fun expr(v)
3344 do
3345 var last = self.n_expr.last
3346 for e in self.n_expr do
3347 if e == last then break
3348 v.stmt(e)
3349 end
3350 return v.expr(last, null)
3351 end
3352 end
3353
3354 redef class AVardeclExpr
3355 redef fun stmt(v)
3356 do
3357 var variable = self.variable.as(not null)
3358 var ne = self.n_expr
3359 if ne != null then
3360 var i = v.expr(ne, variable.declared_type)
3361 v.assign(v.variable(variable), i)
3362 end
3363 end
3364 end
3365
3366 redef class AVarExpr
3367 redef fun expr(v)
3368 do
3369 var res = v.variable(self.variable.as(not null))
3370 var mtype = self.mtype.as(not null)
3371 return v.autoadapt(res, mtype)
3372 end
3373 end
3374
3375 redef class AVarAssignExpr
3376 redef fun expr(v)
3377 do
3378 var variable = self.variable.as(not null)
3379 var i = v.expr(self.n_value, variable.declared_type)
3380 v.assign(v.variable(variable), i)
3381 return i
3382 end
3383 end
3384
3385 redef class AVarReassignExpr
3386 redef fun stmt(v)
3387 do
3388 var variable = self.variable.as(not null)
3389 var vari = v.variable(variable)
3390 var value = v.expr(self.n_value, variable.declared_type)
3391 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3392 assert res != null
3393 v.assign(v.variable(variable), res)
3394 end
3395 end
3396
3397 redef class ASelfExpr
3398 redef fun expr(v) do return v.frame.arguments.first
3399 end
3400
3401 redef class AImplicitSelfExpr
3402 redef fun expr(v) do
3403 if not is_sys then return super
3404 return v.new_expr("glob_sys", mtype.as(not null))
3405 end
3406 end
3407
3408 redef class AEscapeExpr
3409 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3410 end
3411
3412 redef class AReturnExpr
3413 redef fun stmt(v)
3414 do
3415 var nexpr = self.n_expr
3416 if nexpr != null then
3417 var returnvar = v.frame.returnvar.as(not null)
3418 var i = v.expr(nexpr, returnvar.mtype)
3419 v.assign(returnvar, i)
3420 end
3421 v.add("goto {v.frame.returnlabel.as(not null)};")
3422 end
3423 end
3424
3425 redef class AAbortExpr
3426 redef fun stmt(v) do v.add_abort("Aborted")
3427 end
3428
3429 redef class AIfExpr
3430 redef fun stmt(v)
3431 do
3432 var cond = v.expr_bool(self.n_expr)
3433 v.add("if ({cond})\{")
3434 v.stmt(self.n_then)
3435 v.add("\} else \{")
3436 v.stmt(self.n_else)
3437 v.add("\}")
3438 end
3439
3440 redef fun expr(v)
3441 do
3442 var res = v.new_var(self.mtype.as(not null))
3443 var cond = v.expr_bool(self.n_expr)
3444 v.add("if ({cond})\{")
3445 v.assign(res, v.expr(self.n_then.as(not null), null))
3446 v.add("\} else \{")
3447 v.assign(res, v.expr(self.n_else.as(not null), null))
3448 v.add("\}")
3449 return res
3450 end
3451 end
3452
3453 redef class AIfexprExpr
3454 redef fun expr(v)
3455 do
3456 var res = v.new_var(self.mtype.as(not null))
3457 var cond = v.expr_bool(self.n_expr)
3458 v.add("if ({cond})\{")
3459 v.assign(res, v.expr(self.n_then, null))
3460 v.add("\} else \{")
3461 v.assign(res, v.expr(self.n_else, null))
3462 v.add("\}")
3463 return res
3464 end
3465 end
3466
3467 redef class ADoExpr
3468 redef fun stmt(v)
3469 do
3470 if self.n_catch != null then
3471 v.add("catchStack.cursor += 1;")
3472 v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
3473 v.stmt(self.n_block)
3474 v.add("catchStack.cursor -= 1;")
3475 v.add("\}else \{")
3476 v.add("catchStack.cursor -= 1;")
3477 v.stmt(self.n_catch)
3478 v.add("\}")
3479 else
3480 v.stmt(self.n_block)
3481 end
3482 v.add_escape_label(break_mark)
3483 end
3484 end
3485
3486 redef class AWhileExpr
3487 redef fun stmt(v)
3488 do
3489 v.add("for(;;) \{")
3490 var cond = v.expr_bool(self.n_expr)
3491 v.add("if (!{cond}) break;")
3492 v.stmt(self.n_block)
3493 v.add_escape_label(continue_mark)
3494 v.add("\}")
3495 v.add_escape_label(break_mark)
3496 end
3497 end
3498
3499 redef class ALoopExpr
3500 redef fun stmt(v)
3501 do
3502 v.add("for(;;) \{")
3503 v.stmt(self.n_block)
3504 v.add_escape_label(continue_mark)
3505 v.add("\}")
3506 v.add_escape_label(break_mark)
3507 end
3508 end
3509
3510 redef class AForExpr
3511 redef fun stmt(v)
3512 do
3513 for g in n_groups do
3514 var cl = v.expr(g.n_expr, null)
3515 var it_meth = g.method_iterator
3516 assert it_meth != null
3517 var it = v.compile_callsite(it_meth, [cl])
3518 assert it != null
3519 g.it = it
3520 end
3521 v.add("for(;;) \{")
3522 for g in n_groups do
3523 var it = g.it
3524 var isok_meth = g.method_is_ok
3525 assert isok_meth != null
3526 var ok = v.compile_callsite(isok_meth, [it])
3527 assert ok != null
3528 v.add("if(!{ok}) break;")
3529 if g.variables.length == 1 then
3530 var item_meth = g.method_item
3531 assert item_meth != null
3532 var i = v.compile_callsite(item_meth, [it])
3533 assert i != null
3534 v.assign(v.variable(g.variables.first), i)
3535 else if g.variables.length == 2 then
3536 var key_meth = g.method_key
3537 assert key_meth != null
3538 var i = v.compile_callsite(key_meth, [it])
3539 assert i != null
3540 v.assign(v.variable(g.variables[0]), i)
3541 var item_meth = g.method_item
3542 assert item_meth != null
3543 i = v.compile_callsite(item_meth, [it])
3544 assert i != null
3545 v.assign(v.variable(g.variables[1]), i)
3546 else
3547 abort
3548 end
3549 end
3550 v.stmt(self.n_block)
3551 v.add_escape_label(continue_mark)
3552 for g in n_groups do
3553 var next_meth = g.method_next
3554 assert next_meth != null
3555 v.compile_callsite(next_meth, [g.it])
3556 end
3557 v.add("\}")
3558 v.add_escape_label(break_mark)
3559
3560 for g in n_groups do
3561 var method_finish = g.method_finish
3562 if method_finish != null then
3563 # TODO: Find a way to call this also in long escape (e.g. return)
3564 v.compile_callsite(method_finish, [g.it])
3565 end
3566 end
3567 end
3568 end
3569
3570 redef class AForGroup
3571 # C variable representing the iterator
3572 private var it: RuntimeVariable is noinit
3573 end
3574
3575 redef class AAssertExpr
3576 redef fun stmt(v)
3577 do
3578 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3579
3580 var cond = v.expr_bool(self.n_expr)
3581 v.add("if (unlikely(!{cond})) \{")
3582 v.stmt(self.n_else)
3583 var nid = self.n_id
3584 if nid != null then
3585 v.add_abort("Assert '{nid.text}' failed")
3586 else
3587 v.add_abort("Assert failed")
3588 end
3589 v.add("\}")
3590 end
3591 end
3592
3593 redef class AOrExpr
3594 redef fun expr(v)
3595 do
3596 var res = v.new_var(self.mtype.as(not null))
3597 var i1 = v.expr_bool(self.n_expr)
3598 v.add("if ({i1}) \{")
3599 v.add("{res} = 1;")
3600 v.add("\} else \{")
3601 var i2 = v.expr_bool(self.n_expr2)
3602 v.add("{res} = {i2};")
3603 v.add("\}")
3604 return res
3605 end
3606 end
3607
3608 redef class AImpliesExpr
3609 redef fun expr(v)
3610 do
3611 var res = v.new_var(self.mtype.as(not null))
3612 var i1 = v.expr_bool(self.n_expr)
3613 v.add("if (!{i1}) \{")
3614 v.add("{res} = 1;")
3615 v.add("\} else \{")
3616 var i2 = v.expr_bool(self.n_expr2)
3617 v.add("{res} = {i2};")
3618 v.add("\}")
3619 return res
3620 end
3621 end
3622
3623 redef class AAndExpr
3624 redef fun expr(v)
3625 do
3626 var res = v.new_var(self.mtype.as(not null))
3627 var i1 = v.expr_bool(self.n_expr)
3628 v.add("if (!{i1}) \{")
3629 v.add("{res} = 0;")
3630 v.add("\} else \{")
3631 var i2 = v.expr_bool(self.n_expr2)
3632 v.add("{res} = {i2};")
3633 v.add("\}")
3634 return res
3635 end
3636 end
3637
3638 redef class ANotExpr
3639 redef fun expr(v)
3640 do
3641 var cond = v.expr_bool(self.n_expr)
3642 return v.new_expr("!{cond}", self.mtype.as(not null))
3643 end
3644 end
3645
3646 redef class AOrElseExpr
3647 redef fun expr(v)
3648 do
3649 var res = v.new_var(self.mtype.as(not null))
3650 var i1 = v.expr(self.n_expr, null)
3651
3652 if not v.maybe_null(i1) then return i1
3653
3654 v.add("if ({i1}!=NULL) \{")
3655 v.assign(res, i1)
3656 v.add("\} else \{")
3657 var i2 = v.expr(self.n_expr2, null)
3658 v.assign(res, i2)
3659 v.add("\}")
3660 return res
3661 end
3662 end
3663
3664 redef class AIntegerExpr
3665 redef fun expr(v) do
3666 if value isa Int then return v.int_instance(value.as(Int))
3667 if value isa Byte then return v.byte_instance(value.as(Byte))
3668 if value isa Int8 then return v.int8_instance(value.as(Int8))
3669 if value isa Int16 then return v.int16_instance(value.as(Int16))
3670 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3671 if value isa Int32 then return v.int32_instance(value.as(Int32))
3672 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3673 # Should never happen
3674 abort
3675 end
3676 end
3677
3678 redef class AFloatExpr
3679 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3680 end
3681
3682 redef class ACharExpr
3683 redef fun expr(v) do
3684 if is_ascii then return v.byte_instance(value.as(not null).ascii)
3685 if is_code_point then return v.int_instance(value.as(not null).code_point)
3686 return v.char_instance(self.value.as(not null))
3687 end
3688 end
3689
3690 redef class AArrayExpr
3691 redef fun expr(v)
3692 do
3693 var mtype = self.element_mtype.as(not null)
3694 var array = new Array[RuntimeVariable]
3695 var res = v.array_instance(array, mtype)
3696
3697 var old_comprehension = v.frame.comprehension
3698 v.frame.comprehension = res
3699 for nexpr in self.n_exprs do
3700 v.stmt(nexpr)
3701 end
3702 v.frame.comprehension = old_comprehension
3703
3704 return res
3705 end
3706 end
3707
3708 redef class AugmentedStringFormExpr
3709 # Factorize the making of a `Regex` object from a literal prefixed string
3710 protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
3711 var re = to_re
3712 assert re != null
3713 var res = v.compile_callsite(re, [rs])
3714 if res == null then
3715 print "Cannot call property `to_re` on {self}"
3716 abort
3717 end
3718 for i in suffix.chars do
3719 if i == 'i' then
3720 var ign = ignore_case
3721 assert ign != null
3722 v.compile_callsite(ign, [res, v.bool_instance(true)])
3723 continue
3724 end
3725 if i == 'm' then
3726 var nl = newline
3727 assert nl != null
3728 v.compile_callsite(nl, [res, v.bool_instance(true)])
3729 continue
3730 end
3731 if i == 'b' then
3732 var ext = extended
3733 assert ext != null
3734 v.compile_callsite(ext, [res, v.bool_instance(false)])
3735 continue
3736 end
3737 # Should not happen, this needs to be updated
3738 # along with the addition of new suffixes
3739 abort
3740 end
3741 return res
3742 end
3743 end
3744
3745 redef class AStringFormExpr
3746 redef fun expr(v) do return v.string_instance(value)
3747 end
3748
3749 redef class AStringExpr
3750 redef fun expr(v) do
3751 var s = v.string_instance(value)
3752 if is_string then return s
3753 if is_bytestring then
3754 var ns = v.c_string_instance(bytes.items, bytes.length)
3755 var ln = v.int_instance(bytes.length)
3756 var cs = to_bytes_with_copy
3757 assert cs != null
3758 var res = v.compile_callsite(cs, [ns, ln])
3759 assert res != null
3760 s = res
3761 else if is_re then
3762 var res = make_re(v, s)
3763 assert res != null
3764 s = res
3765 else
3766 print "Unimplemented prefix or suffix for {self}"
3767 abort
3768 end
3769 return s
3770 end
3771 end
3772
3773 redef class ASuperstringExpr
3774 redef fun expr(v)
3775 do
3776 var type_string = v.mmodule.string_type
3777
3778 # Collect elements of the superstring
3779 var array = new Array[AExpr]
3780 for ne in self.n_exprs do
3781 # Drop literal empty string.
3782 # They appears in things like "{a}" that is ["", a, ""]
3783 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3784 array.add(ne)
3785 end
3786
3787 # Store the allocated native array in a static variable
3788 # For reusing later
3789 var varonce = v.get_name("varonce")
3790 v.add("if (unlikely({varonce}==NULL)) \{")
3791
3792 # The native array that will contains the elements to_s-ized.
3793 # For fast concatenation.
3794 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3795
3796 v.add_decl("static {a.mtype.ctype} {varonce};")
3797
3798 # Pre-fill the array with the literal string parts.
3799 # So they do not need to be filled again when reused
3800 for i in [0..array.length[ do
3801 var ne = array[i]
3802 if not ne isa AStringFormExpr then continue
3803 var e = v.expr(ne, null)
3804 v.native_array_set(a, i, e)
3805 end
3806
3807 v.add("\} else \{")
3808 # Take the native-array from the store.
3809 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3810 # WARNING: not thread safe! (FIXME?)
3811 v.add("{a} = {varonce};")
3812 v.add("{varonce} = NULL;")
3813 v.add("\}")
3814
3815 # Stringify the elements and put them in the native array
3816 var to_s_method = v.get_property("to_s", v.object_type)
3817 for i in [0..array.length[ do
3818 var ne = array[i]
3819 if ne isa AStringFormExpr then continue
3820 var e = v.expr(ne, null)
3821 # Skip the `to_s` if the element is already a String
3822 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3823 e = v.send(to_s_method, [e]).as(not null)
3824 end
3825 v.native_array_set(a, i, e)
3826 end
3827
3828 # Fast join the C string to get the result
3829 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3830 assert res != null
3831
3832 if is_re then res = make_re(v, res)
3833
3834 # We finish to work with the native array,
3835 # so store it so that it can be reused
3836 v.add("{varonce} = {a};")
3837
3838 return res
3839 end
3840 end
3841
3842 redef class ACrangeExpr
3843 redef fun expr(v)
3844 do
3845 var i1 = v.expr(self.n_expr, null)
3846 var i2 = v.expr(self.n_expr2, null)
3847 var mtype = self.mtype.as(MClassType)
3848 var res = v.init_instance(mtype)
3849 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3850 return res
3851 end
3852 end
3853
3854 redef class AOrangeExpr
3855 redef fun expr(v)
3856 do
3857 var i1 = v.expr(self.n_expr, null)
3858 var i2 = v.expr(self.n_expr2, null)
3859 var mtype = self.mtype.as(MClassType)
3860 var res = v.init_instance(mtype)
3861 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3862 return res
3863 end
3864 end
3865
3866 redef class ATrueExpr
3867 redef fun expr(v) do return v.bool_instance(true)
3868 end
3869
3870 redef class AFalseExpr
3871 redef fun expr(v) do return v.bool_instance(false)
3872 end
3873
3874 redef class ANullExpr
3875 redef fun expr(v) do return v.null_instance
3876 end
3877
3878 redef class AIsaExpr
3879 redef fun expr(v)
3880 do
3881 var i = v.expr(self.n_expr, null)
3882 var cast_type = self.cast_type
3883 if cast_type == null then return null # no-no on broken node
3884 return v.type_test(i, cast_type, "isa")
3885 end
3886 end
3887
3888 redef class AAsCastExpr
3889 redef fun expr(v)
3890 do
3891 var i = v.expr(self.n_expr, null)
3892 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3893
3894 v.add_cast(i, self.mtype.as(not null), "as")
3895 return i
3896 end
3897 end
3898
3899 redef class AAsNotnullExpr
3900 redef fun expr(v)
3901 do
3902 var i = v.expr(self.n_expr, null)
3903 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3904
3905 if not v.maybe_null(i) then return i
3906
3907 v.add("if (unlikely({i} == NULL)) \{")
3908 v.add_abort("Cast failed")
3909 v.add("\}")
3910 return i
3911 end
3912 end
3913
3914 redef class AParExpr
3915 redef fun expr(v) do return v.expr(self.n_expr, null)
3916 end
3917
3918 redef class AOnceExpr
3919 redef fun expr(v)
3920 do
3921 var mtype = self.mtype.as(not null)
3922 var name = v.get_name("varonce")
3923 var guard = v.get_name(name + "_guard")
3924 v.add_decl("static {mtype.ctype} {name};")
3925 v.add_decl("static int {guard};")
3926 var res = v.new_var(mtype)
3927 v.add("if (likely({guard})) \{")
3928 v.add("{res} = {name};")
3929 v.add("\} else \{")
3930 var i = v.expr(self.n_expr, mtype)
3931 v.add("{res} = {i};")
3932 v.add("{name} = {res};")
3933 v.add("{guard} = 1;")
3934 v.add("\}")
3935 return res
3936 end
3937 end
3938
3939 redef class ASendExpr
3940 redef fun expr(v)
3941 do
3942 var recv = v.expr(self.n_expr, null)
3943 var callsite = self.callsite.as(not null)
3944 if callsite.is_broken then return null
3945 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3946 return v.compile_callsite(callsite, args)
3947 end
3948 end
3949
3950 redef class ASendReassignFormExpr
3951 redef fun stmt(v)
3952 do
3953 var recv = v.expr(self.n_expr, null)
3954 var callsite = self.callsite.as(not null)
3955 if callsite.is_broken then return
3956 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3957
3958 var value = v.expr(self.n_value, null)
3959
3960 var left = v.compile_callsite(callsite, args)
3961 assert left != null
3962
3963 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3964 assert res != null
3965
3966 args.add(res)
3967 v.compile_callsite(self.write_callsite.as(not null), args)
3968 end
3969 end
3970
3971 redef class ASuperExpr
3972 redef fun expr(v)
3973 do
3974 var frame = v.frame.as(not null)
3975 var recv = frame.arguments.first
3976
3977 var callsite = self.callsite
3978 if callsite != null then
3979 if callsite.is_broken then return null
3980 var args
3981
3982 if self.n_args.n_exprs.is_empty then
3983 # Add automatic arguments for the super init call
3984 args = [recv]
3985 for i in [0..callsite.msignature.arity[ do
3986 args.add(frame.arguments[i+1])
3987 end
3988 else
3989 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3990 end
3991
3992 # Super init call
3993 var res = v.compile_callsite(callsite, args)
3994 return res
3995 end
3996
3997 var mpropdef = self.mpropdef.as(not null)
3998
3999 var args
4000 if self.n_args.n_exprs.is_empty then
4001 args = frame.arguments
4002 else
4003 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
4004 end
4005
4006 # Standard call-next-method
4007 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
4008 end
4009 end
4010
4011 redef class ANewExpr
4012 redef fun expr(v)
4013 do
4014 var mtype = self.recvtype
4015 assert mtype != null
4016
4017 if mtype.mclass.name == "NativeArray" then
4018 assert self.n_args.n_exprs.length == 1
4019 var l = v.expr(self.n_args.n_exprs.first, null)
4020 assert mtype isa MGenericType
4021 var elttype = mtype.arguments.first
4022 return v.native_array_instance(elttype, l)
4023 end
4024
4025 var callsite = self.callsite
4026 if callsite == null then return v.init_instance_or_extern(mtype)
4027 if callsite.is_broken then return null
4028
4029 var recv
4030 # new factories are badly implemented.
4031 # They assume a stub temporary receiver exists.
4032 # This temporary receiver is required because it
4033 # currently holds the method and the formal types.
4034 #
4035 # However, this object could be reused if the formal types are the same.
4036 # Therefore, the following code will `once` it in these case
4037 if callsite.mproperty.is_new and not mtype.need_anchor then
4038 var name = v.get_name("varoncenew")
4039 var guard = v.get_name(name + "_guard")
4040 v.add_decl("static {mtype.ctype} {name};")
4041 v.add_decl("static int {guard};")
4042 recv = v.new_var(mtype)
4043 v.add("if (likely({guard})) \{")
4044 v.add("{recv} = {name};")
4045 v.add("\} else \{")
4046 var i = v.init_instance_or_extern(mtype)
4047 v.add("{recv} = {i};")
4048 v.add("{name} = {recv};")
4049 v.add("{guard} = 1;")
4050 v.add("\}")
4051 else
4052 recv = v.init_instance_or_extern(mtype)
4053 end
4054
4055 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
4056 var res2 = v.compile_callsite(callsite, args)
4057 if res2 != null then
4058 #self.debug("got {res2} from {mproperty}. drop {recv}")
4059 return res2
4060 end
4061 return recv
4062 end
4063 end
4064
4065 redef class AAttrExpr
4066 redef fun expr(v)
4067 do
4068 var recv = v.expr(self.n_expr, null)
4069 var mproperty = self.mproperty.as(not null)
4070 return v.read_attribute(mproperty, recv)
4071 end
4072 end
4073
4074 redef class AAttrAssignExpr
4075 redef fun expr(v)
4076 do
4077 var recv = v.expr(self.n_expr, null)
4078 var i = v.expr(self.n_value, null)
4079 var mproperty = self.mproperty.as(not null)
4080 v.write_attribute(mproperty, recv, i)
4081 return i
4082 end
4083 end
4084
4085 redef class AAttrReassignExpr
4086 redef fun stmt(v)
4087 do
4088 var recv = v.expr(self.n_expr, null)
4089 var value = v.expr(self.n_value, null)
4090 var mproperty = self.mproperty.as(not null)
4091 var attr = v.read_attribute(mproperty, recv)
4092 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
4093 assert res != null
4094 v.write_attribute(mproperty, recv, res)
4095 end
4096 end
4097
4098 redef class AIssetAttrExpr
4099 redef fun expr(v)
4100 do
4101 var recv = v.expr(self.n_expr, null)
4102 var mproperty = self.mproperty.as(not null)
4103 return v.isset_attribute(mproperty, recv)
4104 end
4105 end
4106
4107 redef class AVarargExpr
4108 redef fun expr(v)
4109 do
4110 return v.expr(self.n_expr, null)
4111 end
4112 end
4113
4114 redef class ANamedargExpr
4115 redef fun expr(v)
4116 do
4117 return v.expr(self.n_expr, null)
4118 end
4119 end
4120
4121 redef class ADebugTypeExpr
4122 redef fun stmt(v)
4123 do
4124 # do nothing
4125 end
4126 end
4127
4128 # Utils
4129
4130 redef class Array[E]
4131 # Return a new `Array` with the elements only contened in self and not in `o`
4132 fun -(o: Array[E]): Array[E] do
4133 var res = new Array[E]
4134 for e in self do if not o.has(e) then res.add(e)
4135 return res
4136 end
4137 end
4138
4139 redef class MModule
4140 # All `MProperty` associated to all `MClassDef` of `mclass`
4141 fun properties(mclass: MClass): Set[MProperty] do
4142 if not self.properties_cache.has_key(mclass) then
4143 var properties = new HashSet[MProperty]
4144 var parents = new Array[MClass]
4145 if self.flatten_mclass_hierarchy.has(mclass) then
4146 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
4147 end
4148 for parent in parents do
4149 properties.add_all(self.properties(parent))
4150 end
4151 for mclassdef in mclass.mclassdefs do
4152 if not self.in_importation <= mclassdef.mmodule then continue
4153 for mprop in mclassdef.intro_mproperties do
4154 properties.add(mprop)
4155 end
4156 end
4157 self.properties_cache[mclass] = properties
4158 end
4159 return properties_cache[mclass]
4160 end
4161 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
4162
4163 # Write FFI and nitni results to file
4164 fun finalize_ffi(c: AbstractCompiler) do end
4165
4166 # Give requided addinional system libraries (as given to LD_LIBS)
4167 # Note: can return null instead of an empty set
4168 fun collect_linker_libs: nullable Array[String] do return null
4169 end
4170
4171 # Create a tool context to handle options and paths
4172 var toolcontext = new ToolContext
4173
4174 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
4175
4176 # We do not add other options, so process them now!
4177 toolcontext.process_options(args)
4178
4179 # We need a model to collect stufs
4180 var model = new Model
4181 # An a model builder to parse files
4182 var modelbuilder = new ModelBuilder(model, toolcontext)
4183
4184 var arguments = toolcontext.option_context.rest
4185 if arguments.length > 1 and toolcontext.opt_output.value != null then
4186 print "Option Error: --output needs a single source file. Do you prefer --dir?"
4187 exit 1
4188 end
4189
4190 # Here we load an process all modules passed on the command line
4191 var mmodules = modelbuilder.parse(arguments)
4192
4193 if mmodules.is_empty then toolcontext.quit
4194
4195 modelbuilder.run_phases
4196
4197 for mmodule in mmodules do
4198 toolcontext.info("*** PROCESS {mmodule} ***", 1)
4199 var ms = [mmodule]
4200 toolcontext.run_global_phases(ms)
4201 end