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