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