Merge: Make the `do catch` great again
[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 int currentSize;
771 jmp_buf *envs;
772 };
773 extern __thread struct catch_stack_t catchStack;
774 """
775 end
776
777 # Declaration of structures for live Nit types
778 protected fun compile_header_structs is abstract
779
780 # Declaration of structures for nitni undelying the FFI
781 protected fun compile_nitni_structs
782 do
783 self.header.add_decl """
784 /* Native reference to Nit objects */
785 /* This structure is used to represent every Nit type in extern methods and custom C code. */
786 struct nitni_ref {
787 struct nitni_ref *next,
788 *prev; /* adjacent global references in global list */
789 int count; /* number of time this global reference has been marked */
790 };
791
792 /* List of global references from C code to Nit objects */
793 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
794 struct nitni_global_ref_list_t {
795 struct nitni_ref *head, *tail;
796 };
797 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
798
799 /* Initializer of global reference list */
800 extern void nitni_global_ref_list_init();
801
802 /* Intern function to add a global reference to the list */
803 extern void nitni_global_ref_add( struct nitni_ref *ref );
804
805 /* Intern function to remove a global reference from the list */
806 extern void nitni_global_ref_remove( struct nitni_ref *ref );
807
808 /* Increase count on an existing global reference */
809 extern void nitni_global_ref_incr( struct nitni_ref *ref );
810
811 /* Decrease count on an existing global reference */
812 extern void nitni_global_ref_decr( struct nitni_ref *ref );
813 """
814 end
815
816 fun compile_finalizer_function
817 do
818 var finalizable_type = mainmodule.finalizable_type
819 if finalizable_type == null then return
820
821 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
822
823 if finalize_meth == null then
824 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
825 return
826 end
827
828 var v = self.new_visitor
829 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
830 var recv = v.new_expr("obj", finalizable_type)
831 v.send(finalize_meth, [recv])
832 v.add "\}"
833 end
834
835 # Hook to add specif piece of code before the the main C function.
836 #
837 # Is called by `compile_main_function`
838 fun compile_before_main(v: VISITOR)
839 do
840 end
841
842 # Hook to add specif piece of code at the begin on the main C function.
843 #
844 # Is called by `compile_main_function`
845 fun compile_begin_main(v: VISITOR)
846 do
847 end
848
849 # Generate the main C function.
850 #
851 # This function:
852 #
853 # * allocate the Sys object if it exists
854 # * call init if is exists
855 # * call main if it exists
856 fun compile_main_function
857 do
858 var v = self.new_visitor
859 v.add_decl("#include <signal.h>")
860 var platform = target_platform
861
862 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
863
864 if platform.supports_libunwind then
865 v.add_decl("#ifndef NO_STACKTRACE")
866 v.add_decl("#define UNW_LOCAL_ONLY")
867 v.add_decl("#include <libunwind.h>")
868 v.add_decl("#include \"c_functions_hash.h\"")
869 v.add_decl("#endif")
870 end
871 v.add_decl("int glob_argc;")
872 v.add_decl("char **glob_argv;")
873 v.add_decl("val *glob_sys;")
874 v.add_decl("__thread struct catch_stack_t catchStack = \{-1, 0, NULL\};")
875
876 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
877 for tag in count_type_test_tags do
878 v.add_decl("long count_type_test_resolved_{tag};")
879 v.add_decl("long count_type_test_unresolved_{tag};")
880 v.add_decl("long count_type_test_skipped_{tag};")
881 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
882 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
883 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
884 end
885 end
886
887 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
888 v.add_decl("long count_invoke_by_tables;")
889 v.add_decl("long count_invoke_by_direct;")
890 v.add_decl("long count_invoke_by_inline;")
891 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
892 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
893 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
894 end
895
896 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
897 v.add_decl("long count_attr_reads = 0;")
898 v.add_decl("long count_isset_checks = 0;")
899 v.compiler.header.add_decl("extern long count_attr_reads;")
900 v.compiler.header.add_decl("extern long count_isset_checks;")
901 end
902
903 v.add_decl("static void show_backtrace(void) \{")
904 if platform.supports_libunwind then
905 v.add_decl("#ifndef NO_STACKTRACE")
906 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
907 v.add_decl("unw_cursor_t cursor;")
908 v.add_decl("if(opt==NULL)\{")
909 v.add_decl("unw_context_t uc;")
910 v.add_decl("unw_word_t ip;")
911 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
912 v.add_decl("unw_getcontext(&uc);")
913 v.add_decl("unw_init_local(&cursor, &uc);")
914 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
915 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
916 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
917 v.add_decl("while (unw_step(&cursor) > 0) \{")
918 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
919 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
920 v.add_decl(" if (recv != NULL)\{")
921 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
922 v.add_decl(" \}else\{")
923 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
924 v.add_decl(" \}")
925 v.add_decl("\}")
926 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
927 v.add_decl("free(procname);")
928 v.add_decl("\}")
929 v.add_decl("#endif /* NO_STACKTRACE */")
930 end
931 v.add_decl("\}")
932
933 v.add_decl("void sig_handler(int signo)\{")
934 v.add_decl "#ifdef _WIN32"
935 v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);"
936 v.add_decl "#else"
937 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
938 v.add_decl "#endif"
939 v.add_decl("show_backtrace();")
940 # rethrows
941 v.add_decl("signal(signo, SIG_DFL);")
942 v.add_decl "#ifndef _WIN32"
943 v.add_decl("kill(getpid(), signo);")
944 v.add_decl "#endif"
945 v.add_decl("\}")
946
947 v.add_decl("void fatal_exit(int status) \{")
948 v.add_decl("show_backtrace();")
949 v.add_decl("exit(status);")
950 v.add_decl("\}")
951
952 compile_before_main(v)
953
954 if no_main then
955 v.add_decl("int nit_main(int argc, char** argv) \{")
956 else
957 v.add_decl("int main(int argc, char** argv) \{")
958 end
959
960 compile_begin_main(v)
961
962 v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
963 v.add("signal(SIGABRT, sig_handler);")
964 v.add("signal(SIGFPE, sig_handler);")
965 v.add("signal(SIGILL, sig_handler);")
966 v.add("signal(SIGINT, sig_handler);")
967 v.add("signal(SIGTERM, sig_handler);")
968 v.add("signal(SIGSEGV, sig_handler);")
969 v.add "#endif"
970 v.add "#ifndef _WIN32"
971 v.add("signal(SIGPIPE, SIG_IGN);")
972 v.add "#endif"
973
974 v.add("glob_argc = argc; glob_argv = argv;")
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("if(catchStack.currentSize == 0) \{")
3508 v.add(" catchStack.cursor = -1;")
3509 v.add(" catchStack.currentSize = 100;")
3510 v.add(" catchStack.envs = malloc(sizeof(jmp_buf)*100);")
3511 v.add("\} else if(catchStack.cursor == catchStack.currentSize - 1) \{")
3512 v.add(" catchStack.currentSize *= 2;")
3513 v.add(" catchStack.envs = realloc(catchStack.envs, sizeof(jmp_buf)*catchStack.currentSize);")
3514 v.add("\}")
3515 v.add("catchStack.cursor += 1;")
3516 v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
3517 v.stmt(self.n_block)
3518 v.add("catchStack.cursor -= 1;")
3519 v.add("\}else \{")
3520 v.add("catchStack.cursor -= 1;")
3521 v.stmt(self.n_catch)
3522 v.add("\}")
3523 else
3524 v.stmt(self.n_block)
3525 end
3526 v.add_escape_label(break_mark)
3527 end
3528 end
3529
3530 redef class AWhileExpr
3531 redef fun stmt(v)
3532 do
3533 v.add("for(;;) \{")
3534 var cond = v.expr_bool(self.n_expr)
3535 v.add("if (!{cond}) break;")
3536 v.stmt(self.n_block)
3537 v.add_escape_label(continue_mark)
3538 v.add("\}")
3539 v.add_escape_label(break_mark)
3540 end
3541 end
3542
3543 redef class ALoopExpr
3544 redef fun stmt(v)
3545 do
3546 v.add("for(;;) \{")
3547 v.stmt(self.n_block)
3548 v.add_escape_label(continue_mark)
3549 v.add("\}")
3550 v.add_escape_label(break_mark)
3551 end
3552 end
3553
3554 redef class AForExpr
3555 redef fun stmt(v)
3556 do
3557 for g in n_groups do
3558 var cl = v.expr(g.n_expr, null)
3559 var it_meth = g.method_iterator
3560 assert it_meth != null
3561 var it = v.compile_callsite(it_meth, [cl])
3562 assert it != null
3563 g.it = it
3564 end
3565 v.add("for(;;) \{")
3566 for g in n_groups do
3567 var it = g.it
3568 var isok_meth = g.method_is_ok
3569 assert isok_meth != null
3570 var ok = v.compile_callsite(isok_meth, [it])
3571 assert ok != null
3572 v.add("if(!{ok}) break;")
3573 if g.variables.length == 1 then
3574 var item_meth = g.method_item
3575 assert item_meth != null
3576 var i = v.compile_callsite(item_meth, [it])
3577 assert i != null
3578 v.assign(v.variable(g.variables.first), i)
3579 else if g.variables.length == 2 then
3580 var key_meth = g.method_key
3581 assert key_meth != null
3582 var i = v.compile_callsite(key_meth, [it])
3583 assert i != null
3584 v.assign(v.variable(g.variables[0]), i)
3585 var item_meth = g.method_item
3586 assert item_meth != null
3587 i = v.compile_callsite(item_meth, [it])
3588 assert i != null
3589 v.assign(v.variable(g.variables[1]), i)
3590 else
3591 abort
3592 end
3593 end
3594 v.stmt(self.n_block)
3595 v.add_escape_label(continue_mark)
3596 for g in n_groups do
3597 var next_meth = g.method_next
3598 assert next_meth != null
3599 v.compile_callsite(next_meth, [g.it])
3600 end
3601 v.add("\}")
3602 v.add_escape_label(break_mark)
3603
3604 for g in n_groups do
3605 var method_finish = g.method_finish
3606 if method_finish != null then
3607 # TODO: Find a way to call this also in long escape (e.g. return)
3608 v.compile_callsite(method_finish, [g.it])
3609 end
3610 end
3611 end
3612 end
3613
3614 redef class AForGroup
3615 # C variable representing the iterator
3616 private var it: RuntimeVariable is noinit
3617 end
3618
3619 redef class AAssertExpr
3620 redef fun stmt(v)
3621 do
3622 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3623
3624 var cond = v.expr_bool(self.n_expr)
3625 v.add("if (unlikely(!{cond})) \{")
3626 v.stmt(self.n_else)
3627
3628 explain_assert v
3629
3630 var nid = self.n_id
3631 if nid != null then
3632 v.add_abort("Assert '{nid.text}' failed")
3633 else
3634 v.add_abort("Assert failed")
3635 end
3636 v.add("\}")
3637 end
3638
3639 # Explain assert if it fails
3640 private fun explain_assert(v: AbstractCompilerVisitor)
3641 do
3642 var explain_assert_str = explain_assert_str
3643 if explain_assert_str == null then return
3644
3645 var nas = v.compiler.modelbuilder.model.get_mclasses_by_name("NativeArray")
3646 if nas == null then return
3647
3648 nas = v.compiler.modelbuilder.model.get_mclasses_by_name("Array")
3649 if nas == null or nas.is_empty then return
3650
3651 var expr = explain_assert_str.expr(v)
3652 if expr == null then return
3653
3654 var cstr = v.send(v.get_property("to_cstring", expr.mtype), [expr])
3655 if cstr == null then return
3656
3657 v.add "PRINT_ERROR(\"Runtime assert: %s\\n\", {cstr});"
3658 end
3659 end
3660
3661 redef class AOrExpr
3662 redef fun expr(v)
3663 do
3664 var res = v.new_var(self.mtype.as(not null))
3665 var i1 = v.expr_bool(self.n_expr)
3666 v.add("if ({i1}) \{")
3667 v.add("{res} = 1;")
3668 v.add("\} else \{")
3669 var i2 = v.expr_bool(self.n_expr2)
3670 v.add("{res} = {i2};")
3671 v.add("\}")
3672 return res
3673 end
3674 end
3675
3676 redef class AImpliesExpr
3677 redef fun expr(v)
3678 do
3679 var res = v.new_var(self.mtype.as(not null))
3680 var i1 = v.expr_bool(self.n_expr)
3681 v.add("if (!{i1}) \{")
3682 v.add("{res} = 1;")
3683 v.add("\} else \{")
3684 var i2 = v.expr_bool(self.n_expr2)
3685 v.add("{res} = {i2};")
3686 v.add("\}")
3687 return res
3688 end
3689 end
3690
3691 redef class AAndExpr
3692 redef fun expr(v)
3693 do
3694 var res = v.new_var(self.mtype.as(not null))
3695 var i1 = v.expr_bool(self.n_expr)
3696 v.add("if (!{i1}) \{")
3697 v.add("{res} = 0;")
3698 v.add("\} else \{")
3699 var i2 = v.expr_bool(self.n_expr2)
3700 v.add("{res} = {i2};")
3701 v.add("\}")
3702 return res
3703 end
3704 end
3705
3706 redef class ANotExpr
3707 redef fun expr(v)
3708 do
3709 var cond = v.expr_bool(self.n_expr)
3710 return v.new_expr("!{cond}", self.mtype.as(not null))
3711 end
3712 end
3713
3714 redef class AOrElseExpr
3715 redef fun expr(v)
3716 do
3717 var res = v.new_var(self.mtype.as(not null))
3718 var i1 = v.expr(self.n_expr, null)
3719
3720 if not v.maybe_null(i1) then return i1
3721
3722 v.add("if ({i1}!=NULL) \{")
3723 v.assign(res, i1)
3724 v.add("\} else \{")
3725 var i2 = v.expr(self.n_expr2, null)
3726 v.assign(res, i2)
3727 v.add("\}")
3728 return res
3729 end
3730 end
3731
3732 redef class AIntegerExpr
3733 redef fun expr(v) do
3734 if value isa Int then return v.int_instance(value.as(Int))
3735 if value isa Byte then return v.byte_instance(value.as(Byte))
3736 if value isa Int8 then return v.int8_instance(value.as(Int8))
3737 if value isa Int16 then return v.int16_instance(value.as(Int16))
3738 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3739 if value isa Int32 then return v.int32_instance(value.as(Int32))
3740 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3741 # Should never happen
3742 abort
3743 end
3744 end
3745
3746 redef class AFloatExpr
3747 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3748 end
3749
3750 redef class ACharExpr
3751 redef fun expr(v) do
3752 if is_ascii then return v.byte_instance(value.as(not null).ascii)
3753 if is_code_point then return v.int_instance(value.as(not null).code_point)
3754 return v.char_instance(self.value.as(not null))
3755 end
3756 end
3757
3758 redef class AArrayExpr
3759 redef fun expr(v)
3760 do
3761 var mtype = self.element_mtype.as(not null)
3762 var array = new Array[RuntimeVariable]
3763 var res = v.array_instance(array, mtype)
3764
3765 var old_comprehension = v.frame.comprehension
3766 v.frame.comprehension = res
3767 for nexpr in self.n_exprs do
3768 v.stmt(nexpr)
3769 end
3770 v.frame.comprehension = old_comprehension
3771
3772 return res
3773 end
3774 end
3775
3776 redef class AugmentedStringFormExpr
3777 # Factorize the making of a `Regex` object from a literal prefixed string
3778 protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
3779 var re = to_re
3780 assert re != null
3781 var res = v.compile_callsite(re, [rs])
3782 if res == null then
3783 print "Cannot call property `to_re` on {self}"
3784 abort
3785 end
3786 for i in suffix.chars do
3787 if i == 'i' then
3788 var ign = ignore_case
3789 assert ign != null
3790 v.compile_callsite(ign, [res, v.bool_instance(true)])
3791 continue
3792 end
3793 if i == 'm' then
3794 var nl = newline
3795 assert nl != null
3796 v.compile_callsite(nl, [res, v.bool_instance(true)])
3797 continue
3798 end
3799 if i == 'b' then
3800 var ext = extended
3801 assert ext != null
3802 v.compile_callsite(ext, [res, v.bool_instance(false)])
3803 continue
3804 end
3805 # Should not happen, this needs to be updated
3806 # along with the addition of new suffixes
3807 abort
3808 end
3809 return res
3810 end
3811 end
3812
3813 redef class AStringFormExpr
3814 redef fun expr(v) do return v.string_instance(value)
3815 end
3816
3817 redef class AStringExpr
3818 redef fun expr(v) do
3819 var s = v.string_instance(value)
3820 if is_string then return s
3821 if is_bytestring then
3822 var ns = v.c_string_instance(bytes.items, bytes.length)
3823 var ln = v.int_instance(bytes.length)
3824 var cs = to_bytes_with_copy
3825 assert cs != null
3826 var res = v.compile_callsite(cs, [ns, ln])
3827 assert res != null
3828 s = res
3829 else if is_re then
3830 var res = make_re(v, s)
3831 assert res != null
3832 s = res
3833 else
3834 print "Unimplemented prefix or suffix for {self}"
3835 abort
3836 end
3837 return s
3838 end
3839 end
3840
3841 redef class ASuperstringExpr
3842 redef fun expr(v)
3843 do
3844 var type_string = v.mmodule.string_type
3845
3846 # Collect elements of the superstring
3847 var array = new Array[AExpr]
3848 for ne in self.n_exprs do
3849 # Drop literal empty string.
3850 # They appears in things like "{a}" that is ["", a, ""]
3851 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3852 array.add(ne)
3853 end
3854
3855 # Store the allocated native array in a static variable
3856 # For reusing later
3857 var varonce = v.get_name("varonce")
3858 v.add("if (unlikely({varonce}==NULL)) \{")
3859
3860 # The native array that will contains the elements to_s-ized.
3861 # For fast concatenation.
3862 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3863
3864 v.add_decl("static {a.mtype.ctype} {varonce};")
3865
3866 # Pre-fill the array with the literal string parts.
3867 # So they do not need to be filled again when reused
3868 for i in [0..array.length[ do
3869 var ne = array[i]
3870 if not ne isa AStringFormExpr then continue
3871 var e = v.expr(ne, null)
3872 v.native_array_set(a, i, e)
3873 end
3874
3875 v.add("\} else \{")
3876 # Take the native-array from the store.
3877 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3878 # WARNING: not thread safe! (FIXME?)
3879 v.add("{a} = {varonce};")
3880 v.add("{varonce} = NULL;")
3881 v.add("\}")
3882
3883 # Stringify the elements and put them in the native array
3884 var to_s_method = v.get_property("to_s", v.object_type)
3885 for i in [0..array.length[ do
3886 var ne = array[i]
3887 if ne isa AStringFormExpr then continue
3888 var e = v.expr(ne, null)
3889 # Skip the `to_s` if the element is already a String
3890 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3891 e = v.send(to_s_method, [e]).as(not null)
3892 end
3893 v.native_array_set(a, i, e)
3894 end
3895
3896 # Fast join the C string to get the result
3897 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3898 assert res != null
3899
3900 if is_re then res = make_re(v, res)
3901
3902 # We finish to work with the native array,
3903 # so store it so that it can be reused
3904 v.add("{varonce} = {a};")
3905
3906 return res
3907 end
3908 end
3909
3910 redef class ACrangeExpr
3911 redef fun expr(v)
3912 do
3913 var i1 = v.expr(self.n_expr, null)
3914 var i2 = v.expr(self.n_expr2, null)
3915 var mtype = self.mtype.as(MClassType)
3916 var res = v.init_instance(mtype)
3917 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3918 return res
3919 end
3920 end
3921
3922 redef class AOrangeExpr
3923 redef fun expr(v)
3924 do
3925 var i1 = v.expr(self.n_expr, null)
3926 var i2 = v.expr(self.n_expr2, null)
3927 var mtype = self.mtype.as(MClassType)
3928 var res = v.init_instance(mtype)
3929 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3930 return res
3931 end
3932 end
3933
3934 redef class ATrueExpr
3935 redef fun expr(v) do return v.bool_instance(true)
3936 end
3937
3938 redef class AFalseExpr
3939 redef fun expr(v) do return v.bool_instance(false)
3940 end
3941
3942 redef class ANullExpr
3943 redef fun expr(v) do return v.null_instance
3944 end
3945
3946 redef class AIsaExpr
3947 redef fun expr(v)
3948 do
3949 var i = v.expr(self.n_expr, null)
3950 var cast_type = self.cast_type
3951 if cast_type == null then return null # no-no on broken node
3952 return v.type_test(i, cast_type, "isa")
3953 end
3954 end
3955
3956 redef class AAsCastExpr
3957 redef fun expr(v)
3958 do
3959 var i = v.expr(self.n_expr, null)
3960 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3961
3962 v.add_cast(i, self.mtype.as(not null), "as")
3963 return i
3964 end
3965 end
3966
3967 redef class AAsNotnullExpr
3968 redef fun expr(v)
3969 do
3970 var i = v.expr(self.n_expr, null)
3971 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3972
3973 if not v.maybe_null(i) then return i
3974
3975 v.add("if (unlikely({i} == NULL)) \{")
3976 v.add_abort("Cast failed")
3977 v.add("\}")
3978 return i
3979 end
3980 end
3981
3982 redef class AParExpr
3983 redef fun expr(v) do return v.expr(self.n_expr, null)
3984 end
3985
3986 redef class AOnceExpr
3987 redef fun expr(v)
3988 do
3989 var mtype = self.mtype.as(not null)
3990 var name = v.get_name("varonce")
3991 var guard = v.get_name(name + "_guard")
3992 v.add_decl("static {mtype.ctype} {name};")
3993 v.add_decl("static int {guard};")
3994 var res = v.new_var(mtype)
3995 v.add("if (likely({guard})) \{")
3996 v.add("{res} = {name};")
3997 v.add("\} else \{")
3998 var i = v.expr(self.n_expr, mtype)
3999 v.add("{res} = {i};")
4000 v.add("{name} = {res};")
4001 v.add("{guard} = 1;")
4002 v.add("\}")
4003 return res
4004 end
4005 end
4006
4007 redef class ASendExpr
4008 redef fun expr(v)
4009 do
4010 var recv = v.expr(self.n_expr, null)
4011 var callsite = self.callsite.as(not null)
4012 if callsite.is_broken then return null
4013 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
4014 return v.compile_callsite(callsite, args)
4015 end
4016 end
4017
4018 redef class ASendReassignFormExpr
4019 redef fun stmt(v)
4020 do
4021 var recv = v.expr(self.n_expr, null)
4022 var callsite = self.callsite.as(not null)
4023 if callsite.is_broken then return
4024 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
4025
4026 var value = v.expr(self.n_value, null)
4027
4028 var left = v.compile_callsite(callsite, args)
4029 assert left != null
4030
4031 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
4032 assert res != null
4033
4034 args.add(res)
4035 v.compile_callsite(self.write_callsite.as(not null), args)
4036 end
4037 end
4038
4039 redef class ASuperExpr
4040 redef fun expr(v)
4041 do
4042 var frame = v.frame.as(not null)
4043 var recv = frame.arguments.first
4044
4045 var callsite = self.callsite
4046 if callsite != null then
4047 if callsite.is_broken then return null
4048 var args
4049
4050 if self.n_args.n_exprs.is_empty then
4051 # Add automatic arguments for the super init call
4052 args = [recv]
4053 for i in [0..callsite.msignature.arity[ do
4054 args.add(frame.arguments[i+1])
4055 end
4056 else
4057 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
4058 end
4059
4060 # Super init call
4061 var res = v.compile_callsite(callsite, args)
4062 return res
4063 end
4064
4065 var mpropdef = self.mpropdef.as(not null)
4066
4067 var args
4068 if self.n_args.n_exprs.is_empty then
4069 args = frame.arguments
4070 else
4071 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
4072 end
4073
4074 # Standard call-next-method
4075 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
4076 end
4077 end
4078
4079 redef class ANewExpr
4080 redef fun expr(v)
4081 do
4082 var mtype = self.recvtype
4083 assert mtype != null
4084
4085 if mtype.mclass.name == "NativeArray" then
4086 assert self.n_args.n_exprs.length == 1
4087 var l = v.expr(self.n_args.n_exprs.first, null)
4088 assert mtype isa MGenericType
4089 var elttype = mtype.arguments.first
4090 return v.native_array_instance(elttype, l)
4091 end
4092
4093 var callsite = self.callsite
4094 if callsite == null then return v.init_instance_or_extern(mtype)
4095 if callsite.is_broken then return null
4096
4097 var recv
4098 # new factories are badly implemented.
4099 # They assume a stub temporary receiver exists.
4100 # This temporary receiver is required because it
4101 # currently holds the method and the formal types.
4102 #
4103 # However, this object could be reused if the formal types are the same.
4104 # Therefore, the following code will `once` it in these case
4105 if callsite.mproperty.is_new and not mtype.need_anchor then
4106 var name = v.get_name("varoncenew")
4107 var guard = v.get_name(name + "_guard")
4108 v.add_decl("static {mtype.ctype} {name};")
4109 v.add_decl("static int {guard};")
4110 recv = v.new_var(mtype)
4111 v.add("if (likely({guard})) \{")
4112 v.add("{recv} = {name};")
4113 v.add("\} else \{")
4114 var i = v.init_instance_or_extern(mtype)
4115 v.add("{recv} = {i};")
4116 v.add("{name} = {recv};")
4117 v.add("{guard} = 1;")
4118 v.add("\}")
4119 else
4120 recv = v.init_instance_or_extern(mtype)
4121 end
4122
4123 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
4124 var res2 = v.compile_callsite(callsite, args)
4125 if res2 != null then
4126 #self.debug("got {res2} from {mproperty}. drop {recv}")
4127 return res2
4128 end
4129 return recv
4130 end
4131 end
4132
4133 redef class AAttrExpr
4134 redef fun expr(v)
4135 do
4136 var recv = v.expr(self.n_expr, null)
4137 var mproperty = self.mproperty.as(not null)
4138 return v.read_attribute(mproperty, recv)
4139 end
4140 end
4141
4142 redef class AAttrAssignExpr
4143 redef fun expr(v)
4144 do
4145 var recv = v.expr(self.n_expr, null)
4146 var i = v.expr(self.n_value, null)
4147 var mproperty = self.mproperty.as(not null)
4148 v.write_attribute(mproperty, recv, i)
4149 return i
4150 end
4151 end
4152
4153 redef class AAttrReassignExpr
4154 redef fun stmt(v)
4155 do
4156 var recv = v.expr(self.n_expr, null)
4157 var value = v.expr(self.n_value, null)
4158 var mproperty = self.mproperty.as(not null)
4159 var attr = v.read_attribute(mproperty, recv)
4160 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
4161 assert res != null
4162 v.write_attribute(mproperty, recv, res)
4163 end
4164 end
4165
4166 redef class AIssetAttrExpr
4167 redef fun expr(v)
4168 do
4169 var recv = v.expr(self.n_expr, null)
4170 var mproperty = self.mproperty.as(not null)
4171 return v.isset_attribute(mproperty, recv)
4172 end
4173 end
4174
4175 redef class AVarargExpr
4176 redef fun expr(v)
4177 do
4178 return v.expr(self.n_expr, null)
4179 end
4180 end
4181
4182 redef class ANamedargExpr
4183 redef fun expr(v)
4184 do
4185 return v.expr(self.n_expr, null)
4186 end
4187 end
4188
4189 redef class ADebugTypeExpr
4190 redef fun stmt(v)
4191 do
4192 # do nothing
4193 end
4194 end
4195
4196 # Utils
4197
4198 redef class Array[E]
4199 # Return a new `Array` with the elements only contened in self and not in `o`
4200 fun -(o: Array[E]): Array[E] do
4201 var res = new Array[E]
4202 for e in self do if not o.has(e) then res.add(e)
4203 return res
4204 end
4205 end
4206
4207 redef class MModule
4208 # All `MProperty` associated to all `MClassDef` of `mclass`
4209 fun properties(mclass: MClass): Set[MProperty] do
4210 if not self.properties_cache.has_key(mclass) then
4211 var properties = new HashSet[MProperty]
4212 var parents = new Array[MClass]
4213 if self.flatten_mclass_hierarchy.has(mclass) then
4214 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
4215 end
4216 for parent in parents do
4217 properties.add_all(self.properties(parent))
4218 end
4219 for mclassdef in mclass.mclassdefs do
4220 if not self.in_importation <= mclassdef.mmodule then continue
4221 for mprop in mclassdef.intro_mproperties do
4222 properties.add(mprop)
4223 end
4224 end
4225 self.properties_cache[mclass] = properties
4226 end
4227 return properties_cache[mclass]
4228 end
4229 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
4230
4231 # Write FFI and nitni results to file
4232 fun finalize_ffi(c: AbstractCompiler) do end
4233
4234 # Give requided addinional system libraries (as given to LD_LIBS)
4235 # Note: can return null instead of an empty set
4236 fun collect_linker_libs: nullable Array[String] do return null
4237 end
4238
4239 # Create a tool context to handle options and paths
4240 var toolcontext = new ToolContext
4241
4242 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
4243
4244 # We do not add other options, so process them now!
4245 toolcontext.process_options(args)
4246
4247 # We need a model to collect stufs
4248 var model = new Model
4249 # An a model builder to parse files
4250 var modelbuilder = new ModelBuilder(model, toolcontext)
4251
4252 var arguments = toolcontext.option_context.rest
4253 if toolcontext.opt_run.value then
4254 # When --run, only the first is the program, the rest is the run arguments
4255 arguments = [toolcontext.option_context.rest.shift]
4256 end
4257 if arguments.length > 1 and toolcontext.opt_output.value != null then
4258 print "Option Error: --output needs a single source file. Do you prefer --dir?"
4259 exit 1
4260 end
4261
4262 # Here we load an process all modules passed on the command line
4263 var mmodules = modelbuilder.parse(arguments)
4264
4265 if mmodules.is_empty then toolcontext.quit
4266
4267 modelbuilder.run_phases
4268
4269 for mmodule in mmodules do
4270 toolcontext.info("*** PROCESS {mmodule} ***", 1)
4271 var ms = [mmodule]
4272 toolcontext.run_global_phases(ms)
4273 end