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