97531000a8447a659754b5cd372464d444705bb8
[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 makefile.write("{o}: {f.filename}\n")
532 makefile.write("\t{f.makefile_rule_content}\n\n")
533 dep_rules.add(f.makefile_rule_name)
534
535 if f.compiles_to_o_file then ofiles.add(o)
536 if f.add_to_jar then java_files.add(f)
537 end
538
539 if not java_files.is_empty then
540 var jar_file = "{outpath}.jar"
541
542 var class_files_array = new Array[String]
543 for f in java_files do class_files_array.add(f.makefile_rule_name)
544 var class_files = class_files_array.join(" ")
545
546 makefile.write("{jar_file}: {class_files}\n")
547 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
548 dep_rules.add jar_file
549 end
550
551 # Link edition
552 var pkg = ""
553 if not pkgconfigs.is_empty then
554 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
555 end
556 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
557 # Clean
558 makefile.write("clean:\n\trm -f {ofiles.join(" ")} 2>/dev/null\n")
559 if outpath != real_outpath then
560 makefile.write("\trm -f -- {outpath.escape_to_sh} 2>/dev/null\n")
561 end
562 makefile.close
563 self.toolcontext.info("Generated makefile: {makepath}", 2)
564
565 makepath.file_copy_to "{compile_dir}/Makefile"
566 end
567
568 # The C code is generated, compile it to an executable
569 fun compile_c_code(compile_dir: String)
570 do
571 var makename = makefile_name
572
573 var makeflags = self.toolcontext.opt_make_flags.value
574 if makeflags == null then makeflags = ""
575
576 var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
577 self.toolcontext.info(command, 2)
578
579 var res
580 if self.toolcontext.verbose_level >= 3 then
581 res = sys.system("{command} 2>&1")
582 else if is_windows then
583 res = sys.system("{command} 2>&1 >nul")
584 else
585 res = sys.system("{command} 2>&1 >/dev/null")
586 end
587 if res != 0 then
588 toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
589 end
590 end
591 end
592
593 # Singleton that store the knowledge about the compilation process
594 abstract class AbstractCompiler
595 type VISITOR: AbstractCompilerVisitor
596
597 # Table corresponding c_names to nit names (methods)
598 var names = new HashMap[String, String]
599
600 # The main module of the program currently compiled
601 # Is assigned during the separate compilation
602 var mainmodule: MModule is writable
603
604 # The real main module of the program
605 var realmainmodule: MModule is noinit
606
607 # The modelbuilder used to know the model and the AST
608 var modelbuilder: ModelBuilder is protected writable
609
610 # The associated toolchain
611 #
612 # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
613 var toolchain: Toolchain is noinit
614
615 # Is hardening asked? (see --hardening)
616 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
617
618 # The targeted specific platform
619 var target_platform: Platform is noinit
620
621 init
622 do
623 self.realmainmodule = mainmodule
624 target_platform = mainmodule.target_platform or else new Platform
625 end
626
627 # Do the full code generation of the program `mainmodule`
628 # It is the main method usually called after the instantiation
629 fun do_compilation is abstract
630
631 # Force the creation of a new file
632 # The point is to avoid contamination between must-be-compiled-separately files
633 fun new_file(name: String): CodeFile
634 do
635 if modelbuilder.toolcontext.opt_group_c_files.value then
636 if self.files.is_empty then
637 var f = new CodeFile(mainmodule.c_name)
638 self.files.add(f)
639 end
640 return self.files.first
641 end
642 var f = new CodeFile(name)
643 self.files.add(f)
644 return f
645 end
646
647 # The list of all associated files
648 # Used to generate .c files
649 var files = new Array[CodeFile]
650
651 # Initialize a visitor specific for a compiler engine
652 fun new_visitor: VISITOR is abstract
653
654 # Where global declaration are stored (the main .h)
655 var header: CodeWriter is writable, noinit
656
657 # Additionnal linker script for `ld`.
658 # Mainly used to do specific link-time symbol resolution
659 var linker_script = new Array[String]
660
661 # Provide a declaration that can be requested (before or latter) by a visitor
662 fun provide_declaration(key: String, s: String)
663 do
664 if self.provided_declarations.has_key(key) then
665 assert self.provided_declarations[key] == s
666 end
667 self.provided_declarations[key] = s
668 end
669
670 private var provided_declarations = new HashMap[String, String]
671
672 private var requirers_of_declarations = new HashMap[String, ANode]
673
674 # Builds the .c and .h files to be used when generating a Stack Trace
675 # Binds the generated C function names to Nit function names
676 fun build_c_to_nit_bindings
677 do
678 var compile_dir = toolchain.compile_dir
679
680 var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
681 stream.write("#include <string.h>\n")
682 stream.write("#include <stdlib.h>\n")
683 stream.write("#include \"c_functions_hash.h\"\n")
684 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
685 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
686 stream.write("char* procname = malloc(len+1);")
687 stream.write("memcpy(procname, procproc, len);")
688 stream.write("procname[len] = '\\0';")
689 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
690 for i in names.keys do
691 stream.write("\{\"")
692 stream.write(i.escape_to_c)
693 stream.write("\",\"")
694 stream.write(names[i].escape_to_c)
695 stream.write("\"\},\n")
696 end
697 stream.write("\};\n")
698 stream.write("int i;")
699 stream.write("for(i = 0; i < {names.length}; i++)\{")
700 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
701 stream.write("free(procname);")
702 stream.write("return map[i].nit_name;")
703 stream.write("\}")
704 stream.write("\}")
705 stream.write("free(procname);")
706 stream.write("return NULL;")
707 stream.write("\}\n")
708 stream.close
709
710 stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
711 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
712 stream.close
713
714 extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
715 end
716
717 # Compile C headers
718 # This method call compile_header_strucs method that has to be refined
719 fun compile_header do
720 self.header.add_decl("#include <stdlib.h>")
721 self.header.add_decl("#include <stdio.h>")
722 self.header.add_decl("#include <string.h>")
723 # longjmp !
724 self.header.add_decl("#include <setjmp.h>\n")
725 self.header.add_decl("#include <sys/types.h>\n")
726 self.header.add_decl("#include <unistd.h>\n")
727 self.header.add_decl("#include <stdint.h>\n")
728 self.header.add_decl("#ifdef __linux__")
729 self.header.add_decl(" #include <endian.h>")
730 self.header.add_decl("#endif")
731 self.header.add_decl("#include <inttypes.h>\n")
732 self.header.add_decl("#include \"gc_chooser.h\"")
733 if modelbuilder.toolcontext.opt_trace.value then self.header.add_decl("#include \"traces.h\"")
734 self.header.add_decl("#ifdef __APPLE__")
735 self.header.add_decl(" #include <TargetConditionals.h>")
736 self.header.add_decl(" #include <syslog.h>")
737 self.header.add_decl(" #include <libkern/OSByteOrder.h>")
738 self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
739 self.header.add_decl("#endif")
740 self.header.add_decl("#ifdef _WIN32")
741 self.header.add_decl(" #define be32toh(val) _byteswap_ulong(val)")
742 self.header.add_decl("#endif")
743 self.header.add_decl("#ifdef ANDROID")
744 self.header.add_decl(" #ifndef be32toh")
745 self.header.add_decl(" #define be32toh(val) betoh32(val)")
746 self.header.add_decl(" #endif")
747 self.header.add_decl(" #include <android/log.h>")
748 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
749 self.header.add_decl("#elif TARGET_OS_IPHONE")
750 self.header.add_decl(" #define PRINT_ERROR(...) syslog(LOG_ERR, __VA_ARGS__)")
751 self.header.add_decl("#else")
752 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
753 self.header.add_decl("#endif")
754
755 compile_header_structs
756 compile_nitni_structs
757 compile_catch_stack
758
759 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
760 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
761 # Signal handler function prototype
762 self.header.add_decl("void fatal_exit(int);")
763 else
764 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
765 end
766
767 if gccd_disable.has("likely") or gccd_disable.has("all") then
768 self.header.add_decl("#define likely(x) (x)")
769 self.header.add_decl("#define unlikely(x) (x)")
770 else if gccd_disable.has("correct-likely") then
771 # invert the `likely` definition
772 # Used by masochists to bench the worst case
773 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
774 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
775 else
776 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
777 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
778 end
779
780 # Global variable used by intern methods
781 self.header.add_decl("extern int glob_argc;")
782 self.header.add_decl("extern char **glob_argv;")
783 self.header.add_decl("extern val *glob_sys;")
784 end
785
786 # Stack stocking environment for longjumps
787 protected fun compile_catch_stack do
788 self.header.add_decl """
789 struct catch_stack_t {
790 int cursor;
791 int currentSize;
792 jmp_buf *envs;
793 };
794 extern struct catch_stack_t *getCatchStack();
795 """
796 end
797
798 # Declaration of structures for live Nit types
799 protected fun compile_header_structs is abstract
800
801 # Declaration of structures for nitni undelying the FFI
802 protected fun compile_nitni_structs
803 do
804 self.header.add_decl """
805 /* Native reference to Nit objects */
806 /* This structure is used to represent every Nit type in extern methods and custom C code. */
807 struct nitni_ref {
808 struct nitni_ref *next,
809 *prev; /* adjacent global references in global list */
810 int count; /* number of time this global reference has been marked */
811 };
812
813 /* List of global references from C code to Nit objects */
814 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
815 struct nitni_global_ref_list_t {
816 struct nitni_ref *head, *tail;
817 };
818 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
819
820 /* Initializer of global reference list */
821 extern void nitni_global_ref_list_init();
822
823 /* Intern function to add a global reference to the list */
824 extern void nitni_global_ref_add( struct nitni_ref *ref );
825
826 /* Intern function to remove a global reference from the list */
827 extern void nitni_global_ref_remove( struct nitni_ref *ref );
828
829 /* Increase count on an existing global reference */
830 extern void nitni_global_ref_incr( struct nitni_ref *ref );
831
832 /* Decrease count on an existing global reference */
833 extern void nitni_global_ref_decr( struct nitni_ref *ref );
834 """
835 end
836
837 fun compile_finalizer_function
838 do
839 var finalizable_type = mainmodule.finalizable_type
840 if finalizable_type == null then return
841
842 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
843
844 if finalize_meth == null then
845 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
846 return
847 end
848
849 var v = self.new_visitor
850 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
851 var recv = v.new_expr("obj", finalizable_type)
852 v.send(finalize_meth, [recv])
853 v.add "\}"
854 end
855
856 # Hook to add specif piece of code before the the main C function.
857 #
858 # Is called by `compile_main_function`
859 fun compile_before_main(v: VISITOR)
860 do
861 end
862
863 # Hook to add specif piece of code at the begin on the main C function.
864 #
865 # Is called by `compile_main_function`
866 fun compile_begin_main(v: VISITOR)
867 do
868 end
869
870 # Generate the main C function.
871 #
872 # This function:
873 #
874 # * allocate the Sys object if it exists
875 # * call init if is exists
876 # * call main if it exists
877 fun compile_main_function
878 do
879 var v = self.new_visitor
880 v.add_decl("#include <signal.h>")
881 var platform = target_platform
882
883 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
884
885 if platform.supports_libunwind then
886 v.add_decl("#ifndef NO_STACKTRACE")
887 v.add_decl("#define UNW_LOCAL_ONLY")
888 v.add_decl("#include <libunwind.h>")
889 v.add_decl("#include \"c_functions_hash.h\"")
890 v.add_decl("#endif")
891 end
892 v.add_decl("int glob_argc;")
893 v.add_decl("char **glob_argv;")
894 v.add_decl("val *glob_sys;")
895
896 # Store catch stack in thread local storage
897 v.add_decl """
898 #if defined(TARGET_OS_IPHONE)
899 // Use pthread_key_create and others for iOS
900 #include <pthread.h>
901
902 static pthread_key_t catch_stack_key;
903 static pthread_once_t catch_stack_key_created = PTHREAD_ONCE_INIT;
904
905 static void create_catch_stack()
906 {
907 pthread_key_create(&catch_stack_key, NULL);
908 }
909
910 struct catch_stack_t *getCatchStack()
911 {
912 pthread_once(&catch_stack_key_created, &create_catch_stack);
913 struct catch_stack_t *data = pthread_getspecific(catch_stack_key);
914 if (data == NULL) {
915 data = malloc(sizeof(struct catch_stack_t));
916 data->cursor = -1;
917 data->currentSize = 0;
918 data->envs = NULL;
919 pthread_setspecific(catch_stack_key, data);
920 }
921 return data;
922 }
923 #else
924 // Use __thread when available
925 __thread struct catch_stack_t catchStack = {-1, 0, NULL};
926
927 struct catch_stack_t *getCatchStack()
928 {
929 return &catchStack;
930 }
931 #endif
932 """
933
934 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
935 for tag in count_type_test_tags do
936 v.add_decl("long count_type_test_resolved_{tag};")
937 v.add_decl("long count_type_test_unresolved_{tag};")
938 v.add_decl("long count_type_test_skipped_{tag};")
939 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
940 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
941 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
942 end
943 end
944
945 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
946 v.add_decl("long count_invoke_by_tables;")
947 v.add_decl("long count_invoke_by_direct;")
948 v.add_decl("long count_invoke_by_inline;")
949 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
950 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
951 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
952 end
953
954 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
955 v.add_decl("long count_attr_reads = 0;")
956 v.add_decl("long count_isset_checks = 0;")
957 v.compiler.header.add_decl("extern long count_attr_reads;")
958 v.compiler.header.add_decl("extern long count_isset_checks;")
959 end
960
961 v.add_decl("static void show_backtrace(void) \{")
962 if platform.supports_libunwind then
963 v.add_decl("#ifndef NO_STACKTRACE")
964 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
965 v.add_decl("unw_cursor_t cursor;")
966 v.add_decl("if(opt==NULL)\{")
967 v.add_decl("unw_context_t uc;")
968 v.add_decl("unw_word_t ip;")
969 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
970 v.add_decl("unw_getcontext(&uc);")
971 v.add_decl("unw_init_local(&cursor, &uc);")
972 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
973 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
974 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
975 v.add_decl("while (unw_step(&cursor) > 0) \{")
976 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
977 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
978 v.add_decl(" if (recv != NULL)\{")
979 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
980 v.add_decl(" \}else\{")
981 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
982 v.add_decl(" \}")
983 v.add_decl("\}")
984 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
985 v.add_decl("free(procname);")
986 v.add_decl("\}")
987 v.add_decl("#endif /* NO_STACKTRACE */")
988 end
989 v.add_decl("\}")
990
991 v.add_decl("void sig_handler(int signo)\{")
992 v.add_decl "#ifdef _WIN32"
993 v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);"
994 v.add_decl "#else"
995 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
996 v.add_decl "#endif"
997 v.add_decl("show_backtrace();")
998 # rethrows
999 v.add_decl("signal(signo, SIG_DFL);")
1000 v.add_decl "#ifndef _WIN32"
1001 v.add_decl("kill(getpid(), signo);")
1002 v.add_decl "#endif"
1003 v.add_decl("\}")
1004
1005 v.add_decl("void fatal_exit(int status) \{")
1006 v.add_decl("show_backtrace();")
1007 v.add_decl("exit(status);")
1008 v.add_decl("\}")
1009
1010 compile_before_main(v)
1011
1012 if no_main then
1013 v.add_decl("int nit_main(int argc, char** argv) \{")
1014 else
1015 v.add_decl("int main(int argc, char** argv) \{")
1016 end
1017
1018 compile_begin_main(v)
1019
1020 v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
1021 v.add("signal(SIGABRT, sig_handler);")
1022 v.add("signal(SIGFPE, sig_handler);")
1023 v.add("signal(SIGILL, sig_handler);")
1024 v.add("signal(SIGINT, sig_handler);")
1025 v.add("signal(SIGTERM, sig_handler);")
1026 v.add("signal(SIGSEGV, sig_handler);")
1027 v.add "#endif"
1028 v.add "#ifndef _WIN32"
1029 v.add("signal(SIGPIPE, SIG_IGN);")
1030 v.add "#endif"
1031
1032 v.add("glob_argc = argc; glob_argv = argv;")
1033 v.add("initialize_gc_option();")
1034
1035 v.add "initialize_nitni_global_refs();"
1036
1037 var main_type = mainmodule.sys_type
1038 if main_type != null then
1039 var mainmodule = v.compiler.mainmodule
1040 var glob_sys = v.init_instance(main_type)
1041 v.add("glob_sys = {glob_sys};")
1042 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
1043 if main_init != null then
1044 v.send(main_init, [glob_sys])
1045 end
1046 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
1047 mainmodule.try_get_primitive_method("main", main_type.mclass)
1048 if main_method != null then
1049 v.send(main_method, [glob_sys])
1050 end
1051 end
1052
1053 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1054 v.add_decl("long count_type_test_resolved_total = 0;")
1055 v.add_decl("long count_type_test_unresolved_total = 0;")
1056 v.add_decl("long count_type_test_skipped_total = 0;")
1057 v.add_decl("long count_type_test_total_total = 0;")
1058 for tag in count_type_test_tags do
1059 v.add_decl("long count_type_test_total_{tag};")
1060 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
1061 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
1062 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
1063 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
1064 v.add("count_type_test_total_total += count_type_test_total_{tag};")
1065 end
1066 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
1067 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
1068 var tags = count_type_test_tags.to_a
1069 tags.add("total")
1070 for tag in tags do
1071 v.add("printf(\"{tag}\");")
1072 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
1073 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
1074 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
1075 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
1076 end
1077 end
1078
1079 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
1080 v.add_decl("long count_invoke_total;")
1081 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
1082 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
1083 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
1084 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
1085 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
1086 end
1087
1088 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1089 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
1090 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
1091 end
1092
1093 v.add("return 0;")
1094 v.add("\}")
1095
1096 for m in mainmodule.in_importation.greaters do
1097 var f = "FILE_"+m.c_name
1098 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
1099 provide_declaration(f, "extern const char {f}[];")
1100 end
1101 end
1102
1103 # Copile all C functions related to the [incr|decr]_ref features of the FFI
1104 fun compile_nitni_global_ref_functions
1105 do
1106 var v = self.new_visitor
1107 v.add """
1108 struct nitni_global_ref_list_t *nitni_global_ref_list;
1109 void initialize_nitni_global_refs() {
1110 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
1111 nitni_global_ref_list->head = NULL;
1112 nitni_global_ref_list->tail = NULL;
1113 }
1114
1115 void nitni_global_ref_add( struct nitni_ref *ref ) {
1116 if ( nitni_global_ref_list->head == NULL ) {
1117 nitni_global_ref_list->head = ref;
1118 ref->prev = NULL;
1119 } else {
1120 nitni_global_ref_list->tail->next = ref;
1121 ref->prev = nitni_global_ref_list->tail;
1122 }
1123 nitni_global_ref_list->tail = ref;
1124
1125 ref->next = NULL;
1126 }
1127
1128 void nitni_global_ref_remove( struct nitni_ref *ref ) {
1129 if ( ref->prev == NULL ) {
1130 nitni_global_ref_list->head = ref->next;
1131 } else {
1132 ref->prev->next = ref->next;
1133 }
1134
1135 if ( ref->next == NULL ) {
1136 nitni_global_ref_list->tail = ref->prev;
1137 } else {
1138 ref->next->prev = ref->prev;
1139 }
1140 }
1141
1142 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
1143 if ( ref->count == 0 ) /* not registered */
1144 {
1145 /* add to list */
1146 nitni_global_ref_add( ref );
1147 }
1148
1149 ref->count ++;
1150 }
1151
1152 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
1153 if ( ref->count == 1 ) /* was last reference */
1154 {
1155 /* remove from list */
1156 nitni_global_ref_remove( ref );
1157 }
1158
1159 ref->count --;
1160 }
1161 """
1162 end
1163
1164 # List of additional files required to compile (FFI)
1165 var extern_bodies = new Array[ExternFile]
1166
1167 # List of source files to copy over to the compile dir
1168 var files_to_copy = new Array[String]
1169
1170 # This is used to avoid adding an extern file more than once
1171 private var seen_extern = new ArraySet[String]
1172
1173 # Generate code that initialize the attributes on a new instance
1174 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1175 do
1176 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1177 self.mainmodule.linearize_mclassdefs(cds)
1178 for cd in cds do
1179 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1180 npropdef.init_expr(v, recv)
1181 end
1182 end
1183 end
1184
1185 # Generate code that check if an attribute is correctly initialized
1186 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1187 do
1188 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1189 self.mainmodule.linearize_mclassdefs(cds)
1190 for cd in cds do
1191 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1192 npropdef.check_expr(v, recv)
1193 end
1194 end
1195 end
1196
1197 # stats
1198
1199 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1200 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1201 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1202 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1203
1204 protected fun init_count_type_test_tags: HashMap[String, Int]
1205 do
1206 var res = new HashMap[String, Int]
1207 for tag in count_type_test_tags do
1208 res[tag] = 0
1209 end
1210 return res
1211 end
1212
1213 # Display stats about compilation process
1214 #
1215 # Metrics used:
1216 #
1217 # * type tests against resolved types (`x isa Collection[Animal]`)
1218 # * type tests against unresolved types (`x isa Collection[E]`)
1219 # * type tests skipped
1220 # * type tests total
1221 fun display_stats
1222 do
1223 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1224 print "# static count_type_test"
1225 print "\tresolved:\tunresolved\tskipped\ttotal"
1226 var count_type_test_total = init_count_type_test_tags
1227 count_type_test_resolved["total"] = 0
1228 count_type_test_unresolved["total"] = 0
1229 count_type_test_skipped["total"] = 0
1230 count_type_test_total["total"] = 0
1231 for tag in count_type_test_tags do
1232 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1233 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1234 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1235 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1236 count_type_test_total["total"] += count_type_test_total[tag]
1237 end
1238 var count_type_test = count_type_test_total["total"]
1239 var tags = count_type_test_tags.to_a
1240 tags.add("total")
1241 for tag in tags do
1242 printn tag
1243 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1244 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1245 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1246 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1247 print ""
1248 end
1249 end
1250 end
1251
1252 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1253 end
1254
1255 # C code file generated from the `writers` and the `required_declarations`
1256 #
1257 # A file unit aims to be autonomous and is made or one or more `CodeWriter`s.
1258 class CodeFile
1259 # Basename of the file, will be appended by `.h` and `.c`
1260 var name: String
1261
1262 # `CodeWriter` used in sequence to fill the top of the body, then the bottom
1263 var writers = new Array[CodeWriter]
1264
1265 # Required declarations keys
1266 #
1267 # See: `provide_declaration`
1268 var required_declarations = new HashSet[String]
1269 end
1270
1271 # Store generated lines
1272 #
1273 # Instances are added to `file.writers` at construction.
1274 class CodeWriter
1275 # Parent `CodeFile`
1276 var file: CodeFile
1277
1278 # Main lines of code (written at the bottom of the body)
1279 var lines = new Array[String]
1280
1281 # Lines of code for declarations (written at the top of the body)
1282 var decl_lines = new Array[String]
1283
1284 # Add a line in the main lines of code (to `lines`)
1285 fun add(s: String) do self.lines.add(s)
1286
1287 # Add a declaration line (to `decl_lines`)
1288 #
1289 # Used for local and global declaration.
1290 fun add_decl(s: String) do self.decl_lines.add(s)
1291
1292 init
1293 do
1294 file.writers.add(self)
1295 end
1296 end
1297
1298 # A visitor on the AST of property definition that generate the C code.
1299 abstract class AbstractCompilerVisitor
1300
1301 type COMPILER: AbstractCompiler
1302
1303 # The associated compiler
1304 var compiler: COMPILER
1305
1306 # The current visited AST node
1307 var current_node: nullable ANode = null is writable
1308
1309 # The current `StaticFrame`
1310 var frame: nullable StaticFrame = null is writable
1311
1312 # Alias for self.compiler.mainmodule.object_type
1313 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1314
1315 # Alias for self.compiler.mainmodule.bool_type
1316 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1317
1318 var writer: CodeWriter is noinit
1319
1320 init
1321 do
1322 self.writer = new CodeWriter(compiler.files.last)
1323 end
1324
1325 # Force to get the primitive property named `name` in the instance `recv` or abort
1326 fun get_property(name: String, recv: MType): MMethod
1327 do
1328 assert recv isa MClassType
1329 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1330 end
1331
1332 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1333 do
1334 if callsite.is_broken then return null
1335 var initializers = callsite.mpropdef.initializers
1336 if not initializers.is_empty then
1337 var recv = arguments.first
1338
1339 var i = 1
1340 for p in initializers do
1341 if p isa MMethod then
1342 var args = [recv]
1343 for x in p.intro.msignature.mparameters do
1344 args.add arguments[i]
1345 i += 1
1346 end
1347 self.send(p, args)
1348 else if p isa MAttribute then
1349 self.write_attribute(p, recv, arguments[i])
1350 i += 1
1351 else abort
1352 end
1353 assert i == arguments.length
1354
1355 return self.send(callsite.mproperty, [recv])
1356 end
1357
1358 return self.send(callsite.mproperty, arguments)
1359 end
1360
1361 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1362
1363 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
1364
1365 # Return an element of a native array.
1366 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1367 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1368
1369 # Store an element in a native array.
1370 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1371 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1372
1373 # Allocate `size` bytes with the low_level `nit_alloc` C function
1374 #
1375 # This method can be redefined to inject statistic or tracing code.
1376 #
1377 # `tag` if any, is used to mark the class of the allocated object.
1378 fun nit_alloc(size: String, tag: nullable String): String
1379 do
1380 return "nit_alloc({size})"
1381 end
1382
1383 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1384 # This method is used to manage varargs in signatures and returns the real array
1385 # of runtime variables to use in the call.
1386 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1387 do
1388 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1389 var res = new Array[RuntimeVariable]
1390 res.add(recv)
1391
1392 if msignature.arity == 0 then return res
1393
1394 if map == null then
1395 assert args.length == msignature.arity
1396 for ne in args do
1397 res.add self.expr(ne, null)
1398 end
1399 return res
1400 end
1401
1402 # Eval in order of arguments, not parameters
1403 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1404 for ne in args do
1405 exprs.add self.expr(ne, null)
1406 end
1407
1408 # Fill `res` with the result of the evaluation according to the mapping
1409 for i in [0..msignature.arity[ do
1410 var param = msignature.mparameters[i]
1411 var j = map.map.get_or_null(i)
1412 if j == null then
1413 # default value
1414 res.add(null_instance)
1415 continue
1416 end
1417 if param.is_vararg and args[i].vararg_decl > 0 then
1418 var vararg = exprs.sub(j, args[i].vararg_decl)
1419 var elttype = param.mtype
1420 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1421 res.add(arg)
1422 continue
1423 end
1424 res.add exprs[j]
1425 end
1426 return res
1427 end
1428
1429 # Type handling
1430
1431 # Anchor a type to the main module and the current receiver
1432 fun anchor(mtype: MType): MType
1433 do
1434 if not mtype.need_anchor then return mtype
1435 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1436 end
1437
1438 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1439 do
1440 if not mtype.need_anchor then return mtype
1441 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1442 end
1443
1444 # Unsafely cast a value to a new type
1445 # ie the result share the same C variable but my have a different mcasttype
1446 # NOTE: if the adaptation is useless then `value` is returned as it.
1447 # ENSURE: `result.name == value.name`
1448 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1449 do
1450 mtype = self.anchor(mtype)
1451 var valmtype = value.mcasttype
1452
1453 # CPrimitive is the best you can do
1454 if valmtype.is_c_primitive then
1455 return value
1456 end
1457
1458 # Do nothing if useless autocast
1459 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1460 return value
1461 end
1462
1463 # Just as_not_null if the target is not nullable.
1464 #
1465 # eg `nullable PreciseType` adapted to `Object` gives precisetype.
1466 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1467 mtype = valmtype.mtype
1468 end
1469
1470 var res = new RuntimeVariable(value.name, value.mtype, mtype)
1471 return res
1472 end
1473
1474 # Generate a super call from a method definition
1475 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1476
1477 # Adapt the arguments of a method according to targetted `MMethodDef`
1478 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1479
1480 # Unbox all the arguments of a method when implemented `extern` or `intern`
1481 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1482
1483 # Box or unbox a value to another type iff a C type conversion is needed
1484 # ENSURE: `result.mtype.ctype == mtype.ctype`
1485 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1486
1487 # Box extern classes to be used in the generated code
1488 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1489
1490 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1491 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1492
1493 # Generate a polymorphic subtype test
1494 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1495
1496 # Generate the code required to dynamically check if 2 objects share the same runtime type
1497 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1498
1499 # Generate a Nit "is" for two runtime_variables
1500 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1501
1502 # Sends
1503
1504 # Generate a static call on a method definition
1505 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1506
1507 # Generate a polymorphic send for the method `m` and the arguments `args`
1508 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1509
1510 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1511 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1512 do
1513 assert t isa MClassType
1514 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1515 return self.call(propdef, t, args)
1516 end
1517
1518 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1519 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1520 do
1521 assert t isa MClassType
1522 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1523 return self.call(m, t, args)
1524 end
1525
1526 # Attributes handling
1527
1528 # Generate a polymorphic attribute is_set test
1529 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1530
1531 # Generate a polymorphic attribute read
1532 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1533
1534 # Generate a polymorphic attribute write
1535 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1536
1537 # Checks
1538
1539 # Can value be null? (according to current knowledge)
1540 fun maybe_null(value: RuntimeVariable): Bool
1541 do
1542 return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
1543 end
1544
1545 # Add a check and an abort for a null receiver if needed
1546 fun check_recv_notnull(recv: RuntimeVariable)
1547 do
1548 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1549
1550 if maybe_null(recv) then
1551 self.add("if (unlikely({recv} == NULL)) \{")
1552 self.add_abort("Receiver is null")
1553 self.add("\}")
1554 end
1555 end
1556
1557 # Names handling
1558
1559 private var names = new HashSet[String]
1560 private var last: Int = 0
1561
1562 # Return a new name based on `s` and unique in the visitor
1563 fun get_name(s: String): String
1564 do
1565 if not self.names.has(s) then
1566 self.names.add(s)
1567 return s
1568 end
1569 var i = self.last + 1
1570 loop
1571 var s2 = s + i.to_s
1572 if not self.names.has(s2) then
1573 self.last = i
1574 self.names.add(s2)
1575 return s2
1576 end
1577 i = i + 1
1578 end
1579 end
1580
1581 # Return an unique and stable identifier associated with an escapemark
1582 fun escapemark_name(e: nullable EscapeMark): String
1583 do
1584 assert e != null
1585 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1586 var name = e.name
1587 if name == null then name = "label"
1588 name = get_name(name)
1589 frame.escapemark_names[e] = name
1590 return name
1591 end
1592
1593 # Insert a C label for associated with an escapemark
1594 fun add_escape_label(e: nullable EscapeMark)
1595 do
1596 if e == null then return
1597 if e.escapes.is_empty then return
1598 add("BREAK_{escapemark_name(e)}: (void)0;")
1599 end
1600
1601 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1602 # NOTE: we do not return a `RuntimeVariable` "CString" as the class may not exist in the module/program
1603 fun class_name_string(value: RuntimeVariable): String is abstract
1604
1605 # Variables handling
1606
1607 protected var variables = new HashMap[Variable, RuntimeVariable]
1608
1609 # Return the local runtime_variable associated to a Nit local variable
1610 fun variable(variable: Variable): RuntimeVariable
1611 do
1612 if self.variables.has_key(variable) then
1613 return self.variables[variable]
1614 else
1615 var name = self.get_name("var_{variable.name}")
1616 var mtype = variable.declared_type.as(not null)
1617 mtype = self.anchor(mtype)
1618 var res = new RuntimeVariable(name, mtype, mtype)
1619 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1620 self.variables[variable] = res
1621 return res
1622 end
1623 end
1624
1625 # Return a new uninitialized local runtime_variable
1626 fun new_var(mtype: MType): RuntimeVariable
1627 do
1628 mtype = self.anchor(mtype)
1629 var name = self.get_name("var")
1630 var res = new RuntimeVariable(name, mtype, mtype)
1631 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1632 return res
1633 end
1634
1635 # The difference with `new_var` is the C static type of the local variable
1636 fun new_var_extern(mtype: MType): RuntimeVariable
1637 do
1638 mtype = self.anchor(mtype)
1639 var name = self.get_name("var")
1640 var res = new RuntimeVariable(name, mtype, mtype)
1641 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1642 return res
1643 end
1644
1645 # Return a new uninitialized named runtime_variable
1646 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1647 do
1648 mtype = self.anchor(mtype)
1649 var res = new RuntimeVariable(name, mtype, mtype)
1650 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1651 return res
1652 end
1653
1654 # Correctly assign a left and a right value
1655 # Boxing and unboxing is performed if required
1656 fun assign(left, right: RuntimeVariable)
1657 do
1658 right = self.autobox(right, left.mtype)
1659 self.add("{left} = {right};")
1660 end
1661
1662 # Generate instances
1663
1664 # Generate a alloc-instance + init-attributes
1665 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1666
1667 # Allocate and init attributes of an instance of a standard or extern class
1668 #
1669 # Does not support universals and the pseudo-internal `NativeArray` class.
1670 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1671 do
1672 var recv
1673 var ctype = mtype.ctype
1674 assert mtype.mclass.name != "NativeArray"
1675 if not mtype.is_c_primitive then
1676 recv = init_instance(mtype)
1677 else if ctype == "char*" then
1678 recv = new_expr("NULL/*special!*/", mtype)
1679 else
1680 recv = new_expr("({ctype})0/*special!*/", mtype)
1681 end
1682 return recv
1683 end
1684
1685 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1686 fun set_finalizer(recv: RuntimeVariable)
1687 do
1688 var mtype = recv.mtype
1689 var finalizable_type = compiler.mainmodule.finalizable_type
1690 if finalizable_type != null and not mtype.need_anchor and
1691 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1692 add "gc_register_finalizer({recv});"
1693 end
1694 end
1695
1696 # The currently processed module
1697 #
1698 # alias for `compiler.mainmodule`
1699 fun mmodule: MModule do return compiler.mainmodule
1700
1701 # Generate an integer value
1702 fun int_instance(value: Int): RuntimeVariable
1703 do
1704 var t = mmodule.int_type
1705 var res = new RuntimeVariable("{value.to_s}l", t, t)
1706 return res
1707 end
1708
1709 # Generate a byte value
1710 fun byte_instance(value: Byte): RuntimeVariable
1711 do
1712 var t = mmodule.byte_type
1713 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1714 return res
1715 end
1716
1717 # Generate an int8 value
1718 fun int8_instance(value: Int8): RuntimeVariable
1719 do
1720 var t = mmodule.int8_type
1721 var res = new RuntimeVariable("INT8_C({value.to_s})", t, t)
1722 return res
1723 end
1724
1725 # Generate an int16 value
1726 fun int16_instance(value: Int16): RuntimeVariable
1727 do
1728 var t = mmodule.int16_type
1729 var res = new RuntimeVariable("INT16_C({value.to_s})", t, t)
1730 return res
1731 end
1732
1733 # Generate a uint16 value
1734 fun uint16_instance(value: UInt16): RuntimeVariable
1735 do
1736 var t = mmodule.uint16_type
1737 var res = new RuntimeVariable("UINT16_C({value.to_s})", t, t)
1738 return res
1739 end
1740
1741 # Generate an int32 value
1742 fun int32_instance(value: Int32): RuntimeVariable
1743 do
1744 var t = mmodule.int32_type
1745 var res = new RuntimeVariable("INT32_C({value.to_s})", t, t)
1746 return res
1747 end
1748
1749 # Generate a uint32 value
1750 fun uint32_instance(value: UInt32): RuntimeVariable
1751 do
1752 var t = mmodule.uint32_type
1753 var res = new RuntimeVariable("UINT32_C({value.to_s})", t, t)
1754 return res
1755 end
1756
1757 # Generate a char value
1758 fun char_instance(value: Char): RuntimeVariable
1759 do
1760 var t = mmodule.char_type
1761
1762 if value.code_point < 128 then
1763 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1764 else
1765 return new RuntimeVariable("{value.code_point}", t, t)
1766 end
1767 end
1768
1769 # Generate a float value
1770 #
1771 # FIXME pass a Float, not a string
1772 fun float_instance(value: String): RuntimeVariable
1773 do
1774 var t = mmodule.float_type
1775 var res = new RuntimeVariable("{value}", t, t)
1776 return res
1777 end
1778
1779 # Generate an integer value
1780 fun bool_instance(value: Bool): RuntimeVariable
1781 do
1782 var s = if value then "1" else "0"
1783 var res = new RuntimeVariable(s, bool_type, bool_type)
1784 return res
1785 end
1786
1787 # Generate the `null` value
1788 fun null_instance: RuntimeVariable
1789 do
1790 var t = compiler.mainmodule.model.null_type
1791 var res = new RuntimeVariable("((val*)NULL)", t, t)
1792 return res
1793 end
1794
1795 # Generates a CString instance fully escaped in C-style \xHH fashion
1796 fun c_string_instance(ns: CString, len: Int): RuntimeVariable do
1797 var mtype = mmodule.c_string_type
1798 var nat = new_var(mtype)
1799 var byte_esc = new Buffer.with_cap(len * 4)
1800 for i in [0 .. len[ do
1801 byte_esc.append("\\x{ns[i].to_hex}")
1802 end
1803 self.add("{nat} = \"{byte_esc}\";")
1804 return nat
1805 end
1806
1807 # Generate a string value
1808 fun string_instance(string: String): RuntimeVariable
1809 do
1810 var mtype = mmodule.string_type
1811 var name = self.get_name("varonce")
1812 self.add_decl("static {mtype.ctype} {name};")
1813 var res = self.new_var(mtype)
1814 self.add("if (likely({name}!=NULL)) \{")
1815 self.add("{res} = {name};")
1816 self.add("\} else \{")
1817 var native_mtype = mmodule.c_string_type
1818 var nat = self.new_var(native_mtype)
1819 self.add("{nat} = \"{string.escape_to_c}\";")
1820 var byte_length = self.int_instance(string.byte_length)
1821 var unilen = self.int_instance(string.length)
1822 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)};")
1823 self.add("{name} = {res};")
1824 self.add("\}")
1825 return res
1826 end
1827
1828 fun value_instance(object: Object): RuntimeVariable
1829 do
1830 if object isa Int then
1831 return int_instance(object)
1832 else if object isa Bool then
1833 return bool_instance(object)
1834 else if object isa String then
1835 return string_instance(object)
1836 else
1837 abort
1838 end
1839 end
1840
1841 # Generate an array value
1842 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1843
1844 # Get an instance of a array for a vararg
1845 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1846
1847 # Code generation
1848
1849 # Add a line in the main part of the generated C
1850 fun add(s: String) do self.writer.lines.add(s)
1851
1852 # Add a line in the
1853 # (used for local or global declaration)
1854 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1855
1856 # Request the presence of a global declaration
1857 fun require_declaration(key: String)
1858 do
1859 var reqs = self.writer.file.required_declarations
1860 if reqs.has(key) then return
1861 reqs.add(key)
1862 var node = current_node
1863 if node != null then compiler.requirers_of_declarations[key] = node
1864 end
1865
1866 # Add a declaration in the local-header
1867 # The declaration is ensured to be present once
1868 fun declare_once(s: String)
1869 do
1870 self.compiler.provide_declaration(s, s)
1871 self.require_declaration(s)
1872 end
1873
1874 # Look for a needed .h and .c file for a given module
1875 # This is used for the legacy FFI
1876 fun add_extern(mmodule: MModule)
1877 do
1878 var file = mmodule.filepath
1879 file = file.strip_extension(".nit")
1880 var tryfile = file + ".nit.h"
1881 if tryfile.file_exists then
1882 self.declare_once("#include \"{tryfile.basename}\"")
1883 self.compiler.files_to_copy.add(tryfile)
1884 end
1885 tryfile = file + "_nit.h"
1886 if tryfile.file_exists then
1887 self.declare_once("#include \"{tryfile.basename}\"")
1888 self.compiler.files_to_copy.add(tryfile)
1889 end
1890
1891 if self.compiler.seen_extern.has(file) then return
1892 self.compiler.seen_extern.add(file)
1893 tryfile = file + ".nit.c"
1894 if not tryfile.file_exists then
1895 tryfile = file + "_nit.c"
1896 if not tryfile.file_exists then return
1897 end
1898 var f = new ExternCFile(tryfile.basename, "")
1899 self.compiler.extern_bodies.add(f)
1900 self.compiler.files_to_copy.add(tryfile)
1901 end
1902
1903 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1904 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1905 do
1906 var res = new_var(mtype)
1907 self.add("{res} = {cexpr};")
1908 return res
1909 end
1910
1911 # Generate generic abort
1912 # used by aborts, asserts, casts, etc.
1913 fun add_abort(message: String)
1914 do
1915 add_raw_throw
1916 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1917 add_raw_abort
1918 end
1919
1920 # Generate a long jump if there is a catch block.
1921 #
1922 # This method should be called before the error messages and before a `add_raw_abort`.
1923 fun add_raw_throw
1924 do
1925 self.add("\{")
1926 self.add("struct catch_stack_t *catchStack = getCatchStack();")
1927 self.add("if(catchStack->cursor >= 0)\{")
1928 self.add(" longjmp(catchStack->envs[catchStack->cursor], 1);")
1929 self.add("\}")
1930 self.add("\}")
1931 end
1932
1933 # Generate abort without a message.
1934 #
1935 # Used when one need a more complex message.
1936 # Do not forget to call `add_raw_abort` before the display of a custom user message.
1937 fun add_raw_abort
1938 do
1939 var current_node = self.current_node
1940 if current_node != null and current_node.location.file != null and
1941 current_node.location.file.mmodule != null then
1942 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1943 self.require_declaration(f)
1944 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1945 else
1946 self.add("PRINT_ERROR(\"\\n\");")
1947 end
1948 self.add("fatal_exit(1);")
1949 end
1950
1951 # Add a dynamic cast
1952 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1953 do
1954 var res = self.type_test(value, mtype, tag)
1955 self.add("if (unlikely(!{res})) \{")
1956 self.add_raw_throw
1957 var cn = self.class_name_string(value)
1958 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1959 self.add_raw_abort
1960 self.add("\}")
1961 end
1962
1963 # Generate a return with the value `s`
1964 fun ret(s: RuntimeVariable)
1965 do
1966 self.assign(self.frame.returnvar.as(not null), s)
1967 self.add("goto {self.frame.returnlabel.as(not null)};")
1968 end
1969
1970 # Compile a statement (if any)
1971 fun stmt(nexpr: nullable AExpr)
1972 do
1973 if nexpr == null then return
1974 if nexpr.is_broken then
1975 # Untyped expression.
1976 # Might mean dead code or invalid code
1977 # so aborts
1978 add_abort("FATAL: bad statement executed.")
1979 return
1980 end
1981
1982 var narray = nexpr.comprehension
1983 if narray != null then
1984 var recv = frame.comprehension.as(not null)
1985 var val = expr(nexpr, narray.element_mtype)
1986 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1987 return
1988 end
1989
1990 var old = self.current_node
1991 self.current_node = nexpr
1992 nexpr.stmt(self)
1993 self.current_node = old
1994 end
1995
1996 # Compile an expression an return its result
1997 # `mtype` is the expected return type, pass null if no specific type is expected.
1998 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1999 do
2000 var old = self.current_node
2001 self.current_node = nexpr
2002
2003 var res = null
2004 if nexpr.mtype != null then
2005 res = nexpr.expr(self)
2006 end
2007
2008 if res == null then
2009 # Untyped expression.
2010 # Might mean dead code or invalid code.
2011 # so aborts
2012 add_abort("FATAL: bad expression executed.")
2013 # and return a placebo result to please the C compiler
2014 if mtype == null then mtype = compiler.mainmodule.object_type
2015 res = new_var(mtype)
2016
2017 self.current_node = old
2018 return res
2019 end
2020
2021 if mtype != null then
2022 mtype = self.anchor(mtype)
2023 res = self.autobox(res, mtype)
2024 end
2025 res = autoadapt(res, nexpr.mtype.as(not null))
2026 var implicit_cast_to = nexpr.implicit_cast_to
2027 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
2028 add_cast(res, implicit_cast_to, "auto")
2029 res = autoadapt(res, implicit_cast_to)
2030 end
2031 self.current_node = old
2032 return res
2033 end
2034
2035 # Alias for `self.expr(nexpr, self.bool_type)`
2036 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
2037
2038 # Safely show a debug message on the current node and repeat the message in the C code as a comment
2039 fun debug(message: String)
2040 do
2041 var node = self.current_node
2042 if node == null then
2043 print "?: {message}"
2044 else
2045 node.debug(message)
2046 end
2047 self.add("/* DEBUG: {message} */")
2048 end
2049 end
2050
2051 # A C function associated to a Nit method
2052 # Because of customization, a given Nit method can be compiler more that once
2053 abstract class AbstractRuntimeFunction
2054
2055 type COMPILER: AbstractCompiler
2056 type VISITOR: AbstractCompilerVisitor
2057
2058 # The associated Nit method
2059 var mmethoddef: MMethodDef
2060
2061 # The mangled c name of the runtime_function
2062 # Subclasses should redefine `build_c_name` instead
2063 fun c_name: String
2064 do
2065 var res = self.c_name_cache
2066 if res != null then return res
2067 res = self.build_c_name
2068 self.c_name_cache = res
2069 return res
2070 end
2071
2072 # Non cached version of `c_name`
2073 protected fun build_c_name: String is abstract
2074
2075 protected var c_name_cache: nullable String = null is writable
2076
2077 # Implements a call of the runtime_function
2078 # May inline the body or generate a C function call
2079 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
2080
2081 # Generate the code for the `AbstractRuntimeFunction`
2082 # Warning: compile more than once compilation makes CC unhappy
2083 fun compile_to_c(compiler: COMPILER) is abstract
2084 end
2085
2086 # A runtime variable hold a runtime value in C.
2087 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
2088 #
2089 # 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.
2090 class RuntimeVariable
2091 # The name of the variable in the C code
2092 var name: String
2093
2094 # The static type of the variable (as declard in C)
2095 var mtype: MType
2096
2097 # The current casted type of the variable (as known in Nit)
2098 var mcasttype: MType is writable
2099
2100 # If the variable exaclty a mcasttype?
2101 # false (usual value) means that the variable is a mcasttype or a subtype.
2102 var is_exact: Bool = false is writable
2103
2104 init
2105 do
2106 assert not mtype.need_anchor
2107 assert not mcasttype.need_anchor
2108 end
2109
2110 redef fun to_s do return name
2111
2112 redef fun inspect
2113 do
2114 var exact_str
2115 if self.is_exact then
2116 exact_str = " exact"
2117 else
2118 exact_str = ""
2119 end
2120 var type_str
2121 if self.mtype == self.mcasttype then
2122 type_str = "{mtype}{exact_str}"
2123 else
2124 type_str = "{mtype}({mcasttype}{exact_str})"
2125 end
2126 return "<{name}:{type_str}>"
2127 end
2128 end
2129
2130 # The static context of a visited property in a `AbstractCompilerVisitor`
2131 class StaticFrame
2132
2133 type VISITOR: AbstractCompilerVisitor
2134
2135 # The associated visitor
2136 var visitor: VISITOR
2137
2138 # The executed property.
2139 # A Method in case of a call, an attribute in case of a default initialization.
2140 var mpropdef: MPropDef
2141
2142 # The static type of the receiver
2143 var receiver: MClassType
2144
2145 # Arguments of the method (the first is the receiver)
2146 var arguments: Array[RuntimeVariable]
2147
2148 # The runtime_variable associated to the return (in a function)
2149 var returnvar: nullable RuntimeVariable = null is writable
2150
2151 # The label at the end of the property
2152 var returnlabel: nullable String = null is writable
2153
2154 # Labels associated to a each escapemarks.
2155 # Because of inlinings, escape-marks must be associated to their context (the frame)
2156 private var escapemark_names = new HashMap[EscapeMark, String]
2157
2158 # The array comprehension currently filled, if any
2159 private var comprehension: nullable RuntimeVariable = null
2160 end
2161
2162 redef class MType
2163 # Return the C type associated to a given Nit static type
2164 fun ctype: String do return "val*"
2165
2166 # C type outside of the compiler code and in boxes
2167 fun ctype_extern: String do return "val*"
2168
2169 # Short name of the `ctype` to use in unions
2170 fun ctypename: String do return "val"
2171
2172 # Is the associated C type a primitive one?
2173 #
2174 # ENSURE `result == (ctype != "val*")`
2175 fun is_c_primitive: Bool do return false
2176 end
2177
2178 redef class MClassType
2179
2180 redef var ctype is lazy do
2181 if mclass.name == "Int" then
2182 return "long"
2183 else if mclass.name == "Bool" then
2184 return "short int"
2185 else if mclass.name == "Char" then
2186 return "uint32_t"
2187 else if mclass.name == "Float" then
2188 return "double"
2189 else if mclass.name == "Int8" then
2190 return "int8_t"
2191 else if mclass.name == "Byte" then
2192 return "unsigned char"
2193 else if mclass.name == "Int16" then
2194 return "int16_t"
2195 else if mclass.name == "UInt16" then
2196 return "uint16_t"
2197 else if mclass.name == "Int32" then
2198 return "int32_t"
2199 else if mclass.name == "UInt32" then
2200 return "uint32_t"
2201 else if mclass.name == "CString" then
2202 return "char*"
2203 else if mclass.name == "NativeArray" then
2204 return "val*"
2205 else
2206 return "val*"
2207 end
2208 end
2209
2210 redef var is_c_primitive is lazy do return ctype != "val*"
2211
2212 redef fun ctype_extern: String
2213 do
2214 if mclass.kind == extern_kind then
2215 return "void*"
2216 else
2217 return ctype
2218 end
2219 end
2220
2221 redef fun ctypename: String
2222 do
2223 if mclass.name == "Int" then
2224 return "l"
2225 else if mclass.name == "Bool" then
2226 return "s"
2227 else if mclass.name == "Char" then
2228 return "c"
2229 else if mclass.name == "Float" then
2230 return "d"
2231 else if mclass.name == "Int8" then
2232 return "i8"
2233 else if mclass.name == "Byte" then
2234 return "b"
2235 else if mclass.name == "Int16" then
2236 return "i16"
2237 else if mclass.name == "UInt16" then
2238 return "u16"
2239 else if mclass.name == "Int32" then
2240 return "i32"
2241 else if mclass.name == "UInt32" then
2242 return "u32"
2243 else if mclass.name == "CString" then
2244 return "str"
2245 else if mclass.name == "NativeArray" then
2246 #return "{self.arguments.first.ctype}*"
2247 return "val"
2248 else
2249 return "val"
2250 end
2251 end
2252 end
2253
2254 redef class MPropDef
2255 type VISITOR: AbstractCompilerVisitor
2256 end
2257
2258 redef class MMethodDef
2259 # Can the body be inlined?
2260 fun can_inline(v: VISITOR): Bool
2261 do
2262 if is_abstract then return true
2263 if constant_value != null then return true
2264 var modelbuilder = v.compiler.modelbuilder
2265 var node = modelbuilder.mpropdef2node(self)
2266 if node isa APropdef then
2267 return node.can_inline
2268 else if node isa AClassdef then
2269 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2270 return true
2271 else if node == null then
2272 return true
2273 else
2274 abort
2275 end
2276 end
2277
2278 # Inline the body in another visitor
2279 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2280 do
2281 var modelbuilder = v.compiler.modelbuilder
2282 var val = constant_value
2283 var node = modelbuilder.mpropdef2node(self)
2284
2285 if is_abstract then
2286 v.add_raw_throw
2287 var cn = v.class_name_string(arguments.first)
2288 v.current_node = node
2289 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2290 v.add_raw_abort
2291 return null
2292 end
2293
2294 if node isa APropdef then
2295 var oldnode = v.current_node
2296 v.current_node = node
2297 self.compile_parameter_check(v, arguments)
2298 node.compile_to_c(v, self, arguments)
2299 v.current_node = oldnode
2300 else if node isa AClassdef then
2301 var oldnode = v.current_node
2302 v.current_node = node
2303 self.compile_parameter_check(v, arguments)
2304 node.compile_to_c(v, self, arguments)
2305 v.current_node = oldnode
2306 else if val != null then
2307 v.ret(v.value_instance(val))
2308 else
2309 abort
2310 end
2311 return null
2312 end
2313
2314 # Generate type checks in the C code to check covariant parameters
2315 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2316 do
2317 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2318
2319 var msignature = self.msignature.as(not null)
2320
2321 for i in [0..msignature.arity[ do
2322 var mp = msignature.mparameters[i]
2323 # skip test for vararg since the array is instantiated with the correct polymorphic type
2324 if mp.is_vararg then continue
2325
2326 # skip if the cast is not required
2327 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2328 if not origmtype.need_anchor then continue
2329
2330 # get the parameter type
2331 var mtype = mp.mtype
2332
2333 # generate the cast
2334 # note that v decides if and how to implements the cast
2335 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2336 v.add_cast(arguments[i+1], mtype, "covariance")
2337 end
2338 end
2339 end
2340
2341 # Node visit
2342
2343 redef class APropdef
2344 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2345 do
2346 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2347 debug("Not yet implemented")
2348 end
2349
2350 fun can_inline: Bool do return true
2351 end
2352
2353 redef class AMethPropdef
2354 redef fun compile_to_c(v, mpropdef, arguments)
2355 do
2356 # Call the implicit super-init
2357 var auto_super_inits = self.auto_super_inits
2358 if auto_super_inits != null then
2359 var args = [arguments.first]
2360 for auto_super_init in auto_super_inits do
2361 assert auto_super_init.mproperty != mpropdef.mproperty
2362 args.clear
2363 for i in [0..auto_super_init.msignature.arity+1[ do
2364 args.add(arguments[i])
2365 end
2366 assert auto_super_init.mproperty != mpropdef.mproperty
2367 v.compile_callsite(auto_super_init, args)
2368 end
2369 end
2370 if auto_super_call then
2371 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2372 end
2373
2374 # Try special compilation
2375 if mpropdef.is_intern then
2376 if compile_intern_to_c(v, mpropdef, arguments) then return
2377 end
2378 if mpropdef.is_extern then
2379 if mpropdef.mproperty.is_init then
2380 if compile_externinit_to_c(v, mpropdef, arguments) then return
2381 else
2382 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2383 end
2384 end
2385
2386 # Compile block if any
2387 var n_block = n_block
2388 if n_block != null then
2389 for i in [0..mpropdef.msignature.arity[ do
2390 var variable = self.n_signature.n_params[i].variable.as(not null)
2391 v.assign(v.variable(variable), arguments[i+1])
2392 end
2393 v.stmt(n_block)
2394 return
2395 end
2396
2397 # We have a problem
2398 v.add_raw_throw
2399 var cn = v.class_name_string(arguments.first)
2400 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2401 v.add_raw_abort
2402 end
2403
2404 redef fun can_inline
2405 do
2406 if self.auto_super_inits != null then return false
2407 var nblock = self.n_block
2408 if nblock == null then return true
2409 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2410 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2411 return false
2412 end
2413
2414 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2415 do
2416 var pname = mpropdef.mproperty.name
2417 var cname = mpropdef.mclassdef.mclass.name
2418 var ret = mpropdef.msignature.return_mtype
2419 if ret != null then
2420 ret = v.resolve_for(ret, arguments.first)
2421 end
2422 if pname != "==" and pname != "!=" then
2423 v.adapt_signature(mpropdef, arguments)
2424 v.unbox_signature_extern(mpropdef, arguments)
2425 end
2426 if cname == "Int" then
2427 if pname == "output" then
2428 v.add("printf(\"%ld\\n\", {arguments.first});")
2429 return true
2430 else if pname == "object_id" then
2431 v.ret(arguments.first)
2432 return true
2433 else if pname == "+" then
2434 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2435 return true
2436 else if pname == "-" then
2437 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2438 return true
2439 else if pname == "unary -" then
2440 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2441 return true
2442 else if pname == "unary +" then
2443 v.ret(arguments[0])
2444 return true
2445 else if pname == "*" then
2446 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2447 return true
2448 else if pname == "/" then
2449 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2450 return true
2451 else if pname == "%" then
2452 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2453 return true
2454 else if pname == "==" then
2455 v.ret(v.equal_test(arguments[0], arguments[1]))
2456 return true
2457 else if pname == "!=" then
2458 var res = v.equal_test(arguments[0], arguments[1])
2459 v.ret(v.new_expr("!{res}", ret.as(not null)))
2460 return true
2461 else if pname == "<" then
2462 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2463 return true
2464 else if pname == ">" then
2465 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2466 return true
2467 else if pname == "<=" then
2468 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2469 return true
2470 else if pname == ">=" then
2471 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2472 return true
2473 else if pname == "to_i8" then
2474 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2475 return true
2476 else if pname == "to_i16" then
2477 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2478 return true
2479 else if pname == "to_u16" then
2480 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2481 return true
2482 else if pname == "to_i32" then
2483 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2484 return true
2485 else if pname == "to_u32" then
2486 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2487 return true
2488 else if pname == "to_f" then
2489 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2490 return true
2491 else if pname == "to_b" then
2492 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2493 return true
2494 else if pname == "code_point" then
2495 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2496 return true
2497 else if pname == "&" then
2498 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2499 return true
2500 else if pname == "|" then
2501 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2502 return true
2503 else if pname == ">>" then
2504 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2505 return true
2506 else if pname == "<<" then
2507 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2508 return true
2509 end
2510 else if cname == "Char" then
2511 if pname == "object_id" then
2512 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2513 return true
2514 else if pname == "successor" then
2515 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2516 return true
2517 else if pname == "predecessor" then
2518 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2519 return true
2520 else if pname == "==" then
2521 v.ret(v.equal_test(arguments[0], arguments[1]))
2522 return true
2523 else if pname == "!=" then
2524 var res = v.equal_test(arguments[0], arguments[1])
2525 v.ret(v.new_expr("!{res}", ret.as(not null)))
2526 return true
2527 else if pname == "<" then
2528 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2529 return true
2530 else if pname == ">" then
2531 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2532 return true
2533 else if pname == "<=" then
2534 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2535 return true
2536 else if pname == ">=" then
2537 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2538 return true
2539 else if pname == "to_i" then
2540 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2541 return true
2542 else if pname == "code_point" then
2543 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2544 return true
2545 end
2546 else if cname == "Byte" then
2547 if pname == "output" then
2548 v.add("printf(\"%x\\n\", {arguments.first});")
2549 return true
2550 else if pname == "object_id" then
2551 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2552 return true
2553 else if pname == "+" then
2554 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2555 return true
2556 else if pname == "-" then
2557 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2558 return true
2559 else if pname == "unary -" then
2560 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2561 return true
2562 else if pname == "unary +" then
2563 v.ret(arguments[0])
2564 return true
2565 else if pname == "*" then
2566 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2567 return true
2568 else if pname == "/" then
2569 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2570 return true
2571 else if pname == "%" then
2572 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2573 return true
2574 else if pname == "==" then
2575 v.ret(v.equal_test(arguments[0], arguments[1]))
2576 return true
2577 else if pname == "!=" then
2578 var res = v.equal_test(arguments[0], arguments[1])
2579 v.ret(v.new_expr("!{res}", ret.as(not null)))
2580 return true
2581 else if pname == "<" then
2582 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2583 return true
2584 else if pname == ">" then
2585 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2586 return true
2587 else if pname == "<=" then
2588 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2589 return true
2590 else if pname == ">=" then
2591 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2592 return true
2593 else if pname == ">>" then
2594 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2595 return true
2596 else if pname == "<<" then
2597 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2598 return true
2599 else if pname == "&" then
2600 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2601 return true
2602 else if pname == "to_i" then
2603 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2604 return true
2605 else if pname == "to_f" then
2606 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2607 return true
2608 else if pname == "to_i8" then
2609 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2610 return true
2611 else if pname == "to_i16" then
2612 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2613 return true
2614 else if pname == "to_u16" then
2615 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2616 return true
2617 else if pname == "to_i32" then
2618 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2619 return true
2620 else if pname == "to_u32" then
2621 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2622 return true
2623 else if pname == "ascii" then
2624 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2625 return true
2626 end
2627 else if cname == "Bool" then
2628 if pname == "output" then
2629 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2630 return true
2631 else if pname == "object_id" then
2632 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2633 return true
2634 else if pname == "==" then
2635 v.ret(v.equal_test(arguments[0], arguments[1]))
2636 return true
2637 else if pname == "!=" then
2638 var res = v.equal_test(arguments[0], arguments[1])
2639 v.ret(v.new_expr("!{res}", ret.as(not null)))
2640 return true
2641 end
2642 else if cname == "Float" then
2643 if pname == "output" then
2644 v.add("printf(\"%f\\n\", {arguments.first});")
2645 return true
2646 else if pname == "object_id" then
2647 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2648 return true
2649 else if pname == "+" then
2650 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2651 return true
2652 else if pname == "-" then
2653 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2654 return true
2655 else if pname == "unary -" then
2656 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2657 return true
2658 else if pname == "unary +" then
2659 v.ret(arguments[0])
2660 return true
2661 else if pname == "succ" then
2662 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2663 return true
2664 else if pname == "prec" then
2665 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2666 return true
2667 else if pname == "*" then
2668 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2669 return true
2670 else if pname == "/" then
2671 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2672 return true
2673 else if pname == "==" then
2674 v.ret(v.equal_test(arguments[0], arguments[1]))
2675 return true
2676 else if pname == "!=" then
2677 var res = v.equal_test(arguments[0], arguments[1])
2678 v.ret(v.new_expr("!{res}", ret.as(not null)))
2679 return true
2680 else if pname == "<" then
2681 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2682 return true
2683 else if pname == ">" then
2684 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2685 return true
2686 else if pname == "<=" then
2687 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2688 return true
2689 else if pname == ">=" then
2690 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2691 return true
2692 else if pname == "to_i" then
2693 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2694 return true
2695 else if pname == "to_b" then
2696 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2697 return true
2698 else if pname == "to_i8" then
2699 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2700 return true
2701 else if pname == "to_i16" then
2702 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2703 return true
2704 else if pname == "to_u16" then
2705 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2706 return true
2707 else if pname == "to_i32" then
2708 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2709 return true
2710 else if pname == "to_u32" then
2711 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2712 return true
2713 end
2714 else if cname == "CString" then
2715 if pname == "[]" then
2716 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2717 return true
2718 else if pname == "[]=" then
2719 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2720 return true
2721 else if pname == "copy_to" then
2722 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2723 return true
2724 else if pname == "atoi" then
2725 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2726 return true
2727 else if pname == "fast_cstring" then
2728 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2729 return true
2730 else if pname == "==" then
2731 v.ret(v.equal_test(arguments[0], arguments[1]))
2732 return true
2733 else if pname == "!=" then
2734 var res = v.equal_test(arguments[0], arguments[1])
2735 v.ret(v.new_expr("!{res}", ret.as(not null)))
2736 return true
2737 else if pname == "new" then
2738 var alloc = v.nit_alloc(arguments[1].to_s, "CString")
2739 v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
2740 return true
2741 else if pname == "fetch_4_chars" then
2742 v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
2743 return true
2744 else if pname == "fetch_4_hchars" then
2745 v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
2746 return true
2747 end
2748 else if cname == "NativeArray" then
2749 return v.native_array_def(pname, ret, arguments)
2750 else if cname == "Int8" then
2751 if pname == "output" then
2752 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2753 return true
2754 else if pname == "object_id" then
2755 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2756 return true
2757 else if pname == "+" then
2758 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2759 return true
2760 else if pname == "-" then
2761 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2762 return true
2763 else if pname == "unary -" then
2764 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2765 return true
2766 else if pname == "unary +" then
2767 v.ret(arguments[0])
2768 return true
2769 else if pname == "*" then
2770 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2771 return true
2772 else if pname == "/" then
2773 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2774 return true
2775 else if pname == "%" then
2776 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2777 return true
2778 else if pname == "<<" then
2779 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2780 return true
2781 else if pname == ">>" then
2782 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2783 return true
2784 else if pname == "==" then
2785 v.ret(v.equal_test(arguments[0], arguments[1]))
2786 return true
2787 else if pname == "!=" then
2788 var res = v.equal_test(arguments[0], arguments[1])
2789 v.ret(v.new_expr("!{res}", ret.as(not null)))
2790 return true
2791 else if pname == "<" then
2792 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2793 return true
2794 else if pname == ">" then
2795 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2796 return true
2797 else if pname == "<=" then
2798 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2799 return true
2800 else if pname == ">=" then
2801 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2802 return true
2803 else if pname == "to_i" then
2804 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2805 return true
2806 else if pname == "to_b" then
2807 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2808 return true
2809 else if pname == "to_i16" then
2810 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2811 return true
2812 else if pname == "to_u16" then
2813 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2814 return true
2815 else if pname == "to_i32" then
2816 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2817 return true
2818 else if pname == "to_u32" then
2819 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2820 return true
2821 else if pname == "to_f" then
2822 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2823 return true
2824 else if pname == "&" then
2825 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2826 return true
2827 else if pname == "|" then
2828 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2829 return true
2830 else if pname == "^" then
2831 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2832 return true
2833 else if pname == "unary ~" then
2834 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2835 return true
2836 end
2837 else if cname == "Int16" then
2838 if pname == "output" then
2839 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2840 return true
2841 else if pname == "object_id" then
2842 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2843 return true
2844 else if pname == "+" then
2845 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2846 return true
2847 else if pname == "-" then
2848 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2849 return true
2850 else if pname == "unary -" then
2851 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2852 return true
2853 else if pname == "unary +" then
2854 v.ret(arguments[0])
2855 return true
2856 else if pname == "*" then
2857 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2858 return true
2859 else if pname == "/" then
2860 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2861 return true
2862 else if pname == "%" then
2863 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2864 return true
2865 else if pname == "<<" then
2866 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2867 return true
2868 else if pname == ">>" then
2869 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2870 return true
2871 else if pname == "==" then
2872 v.ret(v.equal_test(arguments[0], arguments[1]))
2873 return true
2874 else if pname == "!=" then
2875 var res = v.equal_test(arguments[0], arguments[1])
2876 v.ret(v.new_expr("!{res}", ret.as(not null)))
2877 return true
2878 else if pname == "<" then
2879 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2880 return true
2881 else if pname == ">" then
2882 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2883 return true
2884 else if pname == "<=" then
2885 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2886 return true
2887 else if pname == ">=" then
2888 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2889 return true
2890 else if pname == "to_i" then
2891 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2892 return true
2893 else if pname == "to_b" then
2894 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2895 return true
2896 else if pname == "to_i8" then
2897 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2898 return true
2899 else if pname == "to_u16" then
2900 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2901 return true
2902 else if pname == "to_i32" then
2903 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2904 return true
2905 else if pname == "to_u32" then
2906 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2907 return true
2908 else if pname == "to_f" then
2909 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2910 return true
2911 else if pname == "&" then
2912 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2913 return true
2914 else if pname == "|" then
2915 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2916 return true
2917 else if pname == "^" then
2918 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2919 return true
2920 else if pname == "unary ~" then
2921 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2922 return true
2923 end
2924 else if cname == "UInt16" then
2925 if pname == "output" then
2926 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2927 return true
2928 else if pname == "object_id" then
2929 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2930 return true
2931 else if pname == "+" then
2932 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2933 return true
2934 else if pname == "-" then
2935 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2936 return true
2937 else if pname == "unary -" then
2938 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2939 return true
2940 else if pname == "unary +" then
2941 v.ret(arguments[0])
2942 return true
2943 else if pname == "*" then
2944 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2945 return true
2946 else if pname == "/" then
2947 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2948 return true
2949 else if pname == "%" then
2950 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2951 return true
2952 else if pname == "<<" then
2953 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2954 return true
2955 else if pname == ">>" then
2956 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2957 return true
2958 else if pname == "==" then
2959 v.ret(v.equal_test(arguments[0], arguments[1]))
2960 return true
2961 else if pname == "!=" then
2962 var res = v.equal_test(arguments[0], arguments[1])
2963 v.ret(v.new_expr("!{res}", ret.as(not null)))
2964 return true
2965 else if pname == "<" then
2966 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2967 return true
2968 else if pname == ">" then
2969 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2970 return true
2971 else if pname == "<=" then
2972 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2973 return true
2974 else if pname == ">=" then
2975 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2976 return true
2977 else if pname == "to_i" then
2978 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2979 return true
2980 else if pname == "to_b" then
2981 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2982 return true
2983 else if pname == "to_i8" then
2984 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2985 return true
2986 else if pname == "to_i16" then
2987 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2988 return true
2989 else if pname == "to_i32" then
2990 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2991 return true
2992 else if pname == "to_u32" then
2993 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2994 return true
2995 else if pname == "to_f" then
2996 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2997 return true
2998 else if pname == "&" then
2999 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3000 return true
3001 else if pname == "|" then
3002 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3003 return true
3004 else if pname == "^" then
3005 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3006 return true
3007 else if pname == "unary ~" then
3008 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3009 return true
3010 end
3011 else if cname == "Int32" then
3012 if pname == "output" then
3013 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
3014 return true
3015 else if pname == "object_id" then
3016 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3017 return true
3018 else if pname == "+" then
3019 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3020 return true
3021 else if pname == "-" then
3022 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3023 return true
3024 else if pname == "unary -" then
3025 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3026 return true
3027 else if pname == "unary +" then
3028 v.ret(arguments[0])
3029 return true
3030 else if pname == "*" then
3031 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3032 return true
3033 else if pname == "/" then
3034 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3035 return true
3036 else if pname == "%" then
3037 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3038 return true
3039 else if pname == "<<" then
3040 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3041 return true
3042 else if pname == ">>" then
3043 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3044 return true
3045 else if pname == "==" then
3046 v.ret(v.equal_test(arguments[0], arguments[1]))
3047 return true
3048 else if pname == "!=" then
3049 var res = v.equal_test(arguments[0], arguments[1])
3050 v.ret(v.new_expr("!{res}", ret.as(not null)))
3051 return true
3052 else if pname == "<" then
3053 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3054 return true
3055 else if pname == ">" then
3056 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3057 return true
3058 else if pname == "<=" then
3059 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3060 return true
3061 else if pname == ">=" then
3062 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3063 return true
3064 else if pname == "to_i" then
3065 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3066 return true
3067 else if pname == "to_b" then
3068 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3069 return true
3070 else if pname == "to_i8" then
3071 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3072 return true
3073 else if pname == "to_i16" then
3074 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3075 return true
3076 else if pname == "to_u16" then
3077 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3078 return true
3079 else if pname == "to_u32" then
3080 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
3081 return true
3082 else if pname == "to_f" then
3083 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3084 return true
3085 else if pname == "&" then
3086 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3087 return true
3088 else if pname == "|" then
3089 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3090 return true
3091 else if pname == "^" then
3092 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3093 return true
3094 else if pname == "unary ~" then
3095 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3096 return true
3097 end
3098 else if cname == "UInt32" then
3099 if pname == "output" then
3100 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
3101 return true
3102 else if pname == "object_id" then
3103 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3104 return true
3105 else if pname == "+" then
3106 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3107 return true
3108 else if pname == "-" then
3109 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3110 return true
3111 else if pname == "unary -" then
3112 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3113 return true
3114 else if pname == "unary +" then
3115 v.ret(arguments[0])
3116 return true
3117 else if pname == "*" then
3118 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3119 return true
3120 else if pname == "/" then
3121 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3122 return true
3123 else if pname == "%" then
3124 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3125 return true
3126 else if pname == "<<" then
3127 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3128 return true
3129 else if pname == ">>" then
3130 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3131 return true
3132 else if pname == "==" then
3133 v.ret(v.equal_test(arguments[0], arguments[1]))
3134 return true
3135 else if pname == "!=" then
3136 var res = v.equal_test(arguments[0], arguments[1])
3137 v.ret(v.new_expr("!{res}", ret.as(not null)))
3138 return true
3139 else if pname == "<" then
3140 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3141 return true
3142 else if pname == ">" then
3143 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3144 return true
3145 else if pname == "<=" then
3146 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3147 return true
3148 else if pname == ">=" then
3149 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3150 return true
3151 else if pname == "to_i" then
3152 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3153 return true
3154 else if pname == "to_b" then
3155 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3156 return true
3157 else if pname == "to_i8" then
3158 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3159 return true
3160 else if pname == "to_i16" then
3161 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3162 return true
3163 else if pname == "to_u16" then
3164 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3165 return true
3166 else if pname == "to_i32" then
3167 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3168 return true
3169 else if pname == "to_f" then
3170 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3171 return true
3172 else if pname == "&" then
3173 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3174 return true
3175 else if pname == "|" then
3176 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3177 return true
3178 else if pname == "^" then
3179 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3180 return true
3181 else if pname == "unary ~" then
3182 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3183 return true
3184 end
3185 end
3186 if pname == "exit" then
3187 v.add("exit((int){arguments[1]});")
3188 return true
3189 else if pname == "sys" then
3190 v.ret(v.new_expr("glob_sys", ret.as(not null)))
3191 return true
3192 else if pname == "object_id" then
3193 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3194 return true
3195 else if pname == "is_same_type" then
3196 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
3197 return true
3198 else if pname == "is_same_instance" then
3199 v.ret(v.equal_test(arguments[0], arguments[1]))
3200 return true
3201 else if pname == "output_class_name" then
3202 var nat = v.class_name_string(arguments.first)
3203 v.add("printf(\"%s\\n\", {nat});")
3204 return true
3205 else if pname == "native_class_name" then
3206 var nat = v.class_name_string(arguments.first)
3207 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
3208 return true
3209 else if pname == "force_garbage_collection" then
3210 v.add("nit_gcollect();")
3211 return true
3212 else if pname == "native_argc" then
3213 v.ret(v.new_expr("glob_argc", ret.as(not null)))
3214 return true
3215 else if pname == "native_argv" then
3216 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
3217 return true
3218 end
3219 return false
3220 end
3221
3222 # Compile an extern method
3223 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3224 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3225 do
3226 var externname
3227 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3228 if at != null and at.n_args.length == 1 then
3229 externname = at.arg_as_string(v.compiler.modelbuilder)
3230 if externname == null then return false
3231 else
3232 return false
3233 end
3234 v.add_extern(mpropdef.mclassdef.mmodule)
3235 var res: nullable RuntimeVariable = null
3236 var ret = mpropdef.msignature.return_mtype
3237 if ret != null then
3238 ret = v.resolve_for(ret, arguments.first)
3239 res = v.new_var_extern(ret)
3240 end
3241 v.adapt_signature(mpropdef, arguments)
3242 v.unbox_signature_extern(mpropdef, arguments)
3243
3244 if res == null then
3245 v.add("{externname}({arguments.join(", ")});")
3246 else
3247 v.add("{res} = {externname}({arguments.join(", ")});")
3248 res = v.box_extern(res, ret.as(not null))
3249 v.ret(res)
3250 end
3251 return true
3252 end
3253
3254 # Compile an extern factory
3255 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3256 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3257 do
3258 var externname
3259 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3260 if at != null then
3261 externname = at.arg_as_string(v.compiler.modelbuilder)
3262 if externname == null then return false
3263 else
3264 return false
3265 end
3266 v.add_extern(mpropdef.mclassdef.mmodule)
3267 v.adapt_signature(mpropdef, arguments)
3268 v.unbox_signature_extern(mpropdef, arguments)
3269 var ret = arguments.first.mtype
3270 var res = v.new_var_extern(ret)
3271
3272 arguments.shift
3273
3274 v.add("{res} = {externname}({arguments.join(", ")});")
3275 res = v.box_extern(res, ret)
3276 v.ret(res)
3277 return true
3278 end
3279 end
3280
3281 redef class AAttrPropdef
3282 redef fun can_inline: Bool do return not is_lazy
3283
3284 redef fun compile_to_c(v, mpropdef, arguments)
3285 do
3286 if mpropdef == mreadpropdef then
3287 assert arguments.length == 1
3288 var recv = arguments.first
3289 var res
3290 if is_lazy then
3291 var set
3292 var ret = self.mtype
3293 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3294 var guard = self.mlazypropdef.mproperty
3295 if useiset then
3296 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3297 else
3298 set = v.read_attribute(guard, recv)
3299 end
3300 v.add("if(likely({set})) \{")
3301 res = v.read_attribute(self.mpropdef.mproperty, recv)
3302 v.add("\} else \{")
3303
3304 var value = evaluate_expr(v, recv)
3305
3306 v.assign(res, value)
3307 if not useiset then
3308 var true_v = v.bool_instance(true)
3309 v.write_attribute(guard, arguments.first, true_v)
3310 end
3311 v.add("\}")
3312 else
3313 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3314 end
3315 v.assign(v.frame.returnvar.as(not null), res)
3316 else if mpropdef == mwritepropdef then
3317 assert arguments.length == 2
3318 var recv = arguments.first
3319 var arg = arguments[1]
3320 if is_optional and v.maybe_null(arg) then
3321 var value = v.new_var(self.mpropdef.static_mtype.as(not null))
3322 v.add("if ({arg} == NULL) \{")
3323 v.assign(value, evaluate_expr(v, recv))
3324 v.add("\} else \{")
3325 v.assign(value, arg)
3326 v.add("\}")
3327 arg = value
3328 end
3329 v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
3330 if is_lazy then
3331 var ret = self.mtype
3332 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3333 if not useiset then
3334 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3335 end
3336 end
3337 else
3338 abort
3339 end
3340 end
3341
3342 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3343 do
3344 if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3345 end
3346
3347 # Evaluate, store and return the default value of the attribute
3348 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3349 do
3350 var oldnode = v.current_node
3351 v.current_node = self
3352 var old_frame = v.frame
3353 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3354 v.frame = frame
3355
3356 var value
3357 var mtype = self.mtype
3358 assert mtype != null
3359
3360 var nexpr = self.n_expr
3361 var nblock = self.n_block
3362 if nexpr != null then
3363 value = v.expr(nexpr, mtype)
3364 else if nblock != null then
3365 value = v.new_var(mtype)
3366 frame.returnvar = value
3367 frame.returnlabel = v.get_name("RET_LABEL")
3368 v.add("\{")
3369 v.stmt(nblock)
3370 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3371 v.add("\}")
3372 else
3373 abort
3374 end
3375
3376 v.write_attribute(self.mpropdef.mproperty, recv, value)
3377
3378 v.frame = old_frame
3379 v.current_node = oldnode
3380
3381 return value
3382 end
3383
3384 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3385 do
3386 var nexpr = self.n_expr
3387 if nexpr != null then return
3388
3389 var oldnode = v.current_node
3390 v.current_node = self
3391 var old_frame = v.frame
3392 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3393 v.frame = frame
3394 # Force read to check the initialization
3395 v.read_attribute(self.mpropdef.mproperty, recv)
3396 v.frame = old_frame
3397 v.current_node = oldnode
3398 end
3399 end
3400
3401 redef class AClassdef
3402 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3403 do
3404 if mpropdef.mproperty.is_root_init then
3405 assert arguments.length == 1
3406 if not mpropdef.is_intro then
3407 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3408 end
3409 return
3410 else
3411 abort
3412 end
3413 end
3414 end
3415
3416 redef class AExpr
3417 # Try to compile self as an expression
3418 # Do not call this method directly, use `v.expr` instead
3419 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3420 do
3421 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3422 var mtype = self.mtype
3423 if mtype == null then
3424 return null
3425 else
3426 var res = v.new_var(mtype)
3427 v.add("/* {res} = NOT YET {class_name} */")
3428 return res
3429 end
3430 end
3431
3432 # Try to compile self as a statement
3433 # Do not call this method directly, use `v.stmt` instead
3434 private fun stmt(v: AbstractCompilerVisitor)
3435 do
3436 expr(v)
3437 end
3438 end
3439
3440 redef class ABlockExpr
3441 redef fun stmt(v)
3442 do
3443 for e in self.n_expr do v.stmt(e)
3444 end
3445 redef fun expr(v)
3446 do
3447 var last = self.n_expr.last
3448 for e in self.n_expr do
3449 if e == last then break
3450 v.stmt(e)
3451 end
3452 return v.expr(last, null)
3453 end
3454 end
3455
3456 redef class AVardeclExpr
3457 redef fun stmt(v)
3458 do
3459 var variable = self.variable.as(not null)
3460 var ne = self.n_expr
3461 if ne != null then
3462 var i = v.expr(ne, variable.declared_type)
3463 v.assign(v.variable(variable), i)
3464 end
3465 end
3466 end
3467
3468 redef class AVarExpr
3469 redef fun expr(v)
3470 do
3471 var res = v.variable(self.variable.as(not null))
3472 var mtype = self.mtype.as(not null)
3473 return v.autoadapt(res, mtype)
3474 end
3475 end
3476
3477 redef class AVarAssignExpr
3478 redef fun expr(v)
3479 do
3480 var variable = self.variable.as(not null)
3481 var i = v.expr(self.n_value, variable.declared_type)
3482 v.assign(v.variable(variable), i)
3483 return i
3484 end
3485 end
3486
3487 redef class AVarReassignExpr
3488 redef fun stmt(v)
3489 do
3490 var variable = self.variable.as(not null)
3491 var vari = v.variable(variable)
3492 var value = v.expr(self.n_value, variable.declared_type)
3493 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3494 assert res != null
3495 v.assign(v.variable(variable), res)
3496 end
3497 end
3498
3499 redef class ASelfExpr
3500 redef fun expr(v) do return v.frame.arguments.first
3501 end
3502
3503 redef class AImplicitSelfExpr
3504 redef fun expr(v) do
3505 if not is_sys then return super
3506 return v.new_expr("glob_sys", mtype.as(not null))
3507 end
3508 end
3509
3510 redef class AEscapeExpr
3511 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3512 end
3513
3514 redef class AReturnExpr
3515 redef fun stmt(v)
3516 do
3517 var nexpr = self.n_expr
3518 if nexpr != null then
3519 var returnvar = v.frame.returnvar.as(not null)
3520 var i = v.expr(nexpr, returnvar.mtype)
3521 v.assign(returnvar, i)
3522 end
3523 v.add("goto {v.frame.returnlabel.as(not null)};")
3524 end
3525 end
3526
3527 redef class AAbortExpr
3528 redef fun stmt(v) do v.add_abort("Aborted")
3529 end
3530
3531 redef class AIfExpr
3532 redef fun stmt(v)
3533 do
3534 var cond = v.expr_bool(self.n_expr)
3535 v.add("if ({cond})\{")
3536 v.stmt(self.n_then)
3537 v.add("\} else \{")
3538 v.stmt(self.n_else)
3539 v.add("\}")
3540 end
3541
3542 redef fun expr(v)
3543 do
3544 var res = v.new_var(self.mtype.as(not null))
3545 var cond = v.expr_bool(self.n_expr)
3546 v.add("if ({cond})\{")
3547 v.assign(res, v.expr(self.n_then.as(not null), null))
3548 v.add("\} else \{")
3549 v.assign(res, v.expr(self.n_else.as(not null), null))
3550 v.add("\}")
3551 return res
3552 end
3553 end
3554
3555 redef class AIfexprExpr
3556 redef fun expr(v)
3557 do
3558 var res = v.new_var(self.mtype.as(not null))
3559 var cond = v.expr_bool(self.n_expr)
3560 v.add("if ({cond})\{")
3561 v.assign(res, v.expr(self.n_then, null))
3562 v.add("\} else \{")
3563 v.assign(res, v.expr(self.n_else, null))
3564 v.add("\}")
3565 return res
3566 end
3567 end
3568
3569 redef class ADoExpr
3570 redef fun stmt(v)
3571 do
3572 if self.n_catch != null then
3573 v.add("\{")
3574 v.add("struct catch_stack_t *catchStack = getCatchStack();")
3575 v.add("if(catchStack->currentSize == 0) \{")
3576 v.add(" catchStack->cursor = -1;")
3577 v.add(" catchStack->currentSize = 100;")
3578 v.add(" catchStack->envs = malloc(sizeof(jmp_buf)*100);")
3579 v.add("\} else if(catchStack->cursor == catchStack->currentSize - 1) \{")
3580 v.add(" catchStack->currentSize *= 2;")
3581 v.add(" catchStack->envs = realloc(catchStack->envs, sizeof(jmp_buf)*catchStack->currentSize);")
3582 v.add("\}")
3583 v.add("catchStack->cursor += 1;")
3584 v.add("if(!setjmp(catchStack->envs[catchStack->cursor]))\{")
3585 v.stmt(self.n_block)
3586 v.add("catchStack->cursor -= 1;")
3587 v.add("\}else \{")
3588 v.add("catchStack->cursor -= 1;")
3589 v.stmt(self.n_catch)
3590 v.add("\}")
3591 v.add("\}")
3592 else
3593 v.stmt(self.n_block)
3594 end
3595 v.add_escape_label(break_mark)
3596 end
3597 end
3598
3599 redef class AWhileExpr
3600 redef fun stmt(v)
3601 do
3602 v.add("for(;;) \{")
3603 var cond = v.expr_bool(self.n_expr)
3604 v.add("if (!{cond}) break;")
3605 v.stmt(self.n_block)
3606 v.add_escape_label(continue_mark)
3607 v.add("\}")
3608 v.add_escape_label(break_mark)
3609 end
3610 end
3611
3612 redef class ALoopExpr
3613 redef fun stmt(v)
3614 do
3615 v.add("for(;;) \{")
3616 v.stmt(self.n_block)
3617 v.add_escape_label(continue_mark)
3618 v.add("\}")
3619 v.add_escape_label(break_mark)
3620 end
3621 end
3622
3623 redef class AForExpr
3624 redef fun stmt(v)
3625 do
3626 for g in n_groups do
3627 var cl = v.expr(g.n_expr, null)
3628 var it_meth = g.method_iterator
3629 assert it_meth != null
3630 var it = v.compile_callsite(it_meth, [cl])
3631 assert it != null
3632 g.it = it
3633 end
3634 v.add("for(;;) \{")
3635 for g in n_groups do
3636 var it = g.it
3637 var isok_meth = g.method_is_ok
3638 assert isok_meth != null
3639 var ok = v.compile_callsite(isok_meth, [it])
3640 assert ok != null
3641 v.add("if(!{ok}) break;")
3642 if g.variables.length == 1 then
3643 var item_meth = g.method_item
3644 assert item_meth != null
3645 var i = v.compile_callsite(item_meth, [it])
3646 assert i != null
3647 v.assign(v.variable(g.variables.first), i)
3648 else if g.variables.length == 2 then
3649 var key_meth = g.method_key
3650 assert key_meth != null
3651 var i = v.compile_callsite(key_meth, [it])
3652 assert i != null
3653 v.assign(v.variable(g.variables[0]), i)
3654 var item_meth = g.method_item
3655 assert item_meth != null
3656 i = v.compile_callsite(item_meth, [it])
3657 assert i != null
3658 v.assign(v.variable(g.variables[1]), i)
3659 else
3660 abort
3661 end
3662 end
3663 v.stmt(self.n_block)
3664 v.add_escape_label(continue_mark)
3665 for g in n_groups do
3666 var next_meth = g.method_next
3667 assert next_meth != null
3668 v.compile_callsite(next_meth, [g.it])
3669 end
3670 v.add("\}")
3671 v.add_escape_label(break_mark)
3672
3673 for g in n_groups do
3674 var method_finish = g.method_finish
3675 if method_finish != null then
3676 # TODO: Find a way to call this also in long escape (e.g. return)
3677 v.compile_callsite(method_finish, [g.it])
3678 end
3679 end
3680 end
3681 end
3682
3683 redef class AForGroup
3684 # C variable representing the iterator
3685 private var it: RuntimeVariable is noinit
3686 end
3687
3688 redef class AAssertExpr
3689 redef fun stmt(v)
3690 do
3691 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3692
3693 var cond = v.expr_bool(self.n_expr)
3694 v.add("if (unlikely(!{cond})) \{")
3695 v.stmt(self.n_else)
3696
3697 explain_assert v
3698
3699 var nid = self.n_id
3700 if nid != null then
3701 v.add_abort("Assert '{nid.text}' failed")
3702 else
3703 v.add_abort("Assert failed")
3704 end
3705 v.add("\}")
3706 end
3707
3708 # Explain assert if it fails
3709 private fun explain_assert(v: AbstractCompilerVisitor)
3710 do
3711 var explain_assert_str = explain_assert_str
3712 if explain_assert_str == null then return
3713
3714 var nas = v.compiler.modelbuilder.model.get_mclasses_by_name("NativeArray")
3715 if nas == null then return
3716
3717 nas = v.compiler.modelbuilder.model.get_mclasses_by_name("Array")
3718 if nas == null or nas.is_empty then return
3719
3720 var expr = explain_assert_str.expr(v)
3721 if expr == null then return
3722
3723 var cstr = v.send(v.get_property("to_cstring", expr.mtype), [expr])
3724 if cstr == null then return
3725
3726 v.add "PRINT_ERROR(\"Runtime assert: %s\\n\", {cstr});"
3727 end
3728 end
3729
3730 redef class AOrExpr
3731 redef fun expr(v)
3732 do
3733 var res = v.new_var(self.mtype.as(not null))
3734 var i1 = v.expr_bool(self.n_expr)
3735 v.add("if ({i1}) \{")
3736 v.add("{res} = 1;")
3737 v.add("\} else \{")
3738 var i2 = v.expr_bool(self.n_expr2)
3739 v.add("{res} = {i2};")
3740 v.add("\}")
3741 return res
3742 end
3743 end
3744
3745 redef class AImpliesExpr
3746 redef fun expr(v)
3747 do
3748 var res = v.new_var(self.mtype.as(not null))
3749 var i1 = v.expr_bool(self.n_expr)
3750 v.add("if (!{i1}) \{")
3751 v.add("{res} = 1;")
3752 v.add("\} else \{")
3753 var i2 = v.expr_bool(self.n_expr2)
3754 v.add("{res} = {i2};")
3755 v.add("\}")
3756 return res
3757 end
3758 end
3759
3760 redef class AAndExpr
3761 redef fun expr(v)
3762 do
3763 var res = v.new_var(self.mtype.as(not null))
3764 var i1 = v.expr_bool(self.n_expr)
3765 v.add("if (!{i1}) \{")
3766 v.add("{res} = 0;")
3767 v.add("\} else \{")
3768 var i2 = v.expr_bool(self.n_expr2)
3769 v.add("{res} = {i2};")
3770 v.add("\}")
3771 return res
3772 end
3773 end
3774
3775 redef class ANotExpr
3776 redef fun expr(v)
3777 do
3778 var cond = v.expr_bool(self.n_expr)
3779 return v.new_expr("!{cond}", self.mtype.as(not null))
3780 end
3781 end
3782
3783 redef class AOrElseExpr
3784 redef fun expr(v)
3785 do
3786 var res = v.new_var(self.mtype.as(not null))
3787 var i1 = v.expr(self.n_expr, null)
3788
3789 if not v.maybe_null(i1) then return i1
3790
3791 v.add("if ({i1}!=NULL) \{")
3792 v.assign(res, i1)
3793 v.add("\} else \{")
3794 var i2 = v.expr(self.n_expr2, null)
3795 v.assign(res, i2)
3796 v.add("\}")
3797 return res
3798 end
3799 end
3800
3801 redef class AIntegerExpr
3802 redef fun expr(v) do
3803 if value isa Int then return v.int_instance(value.as(Int))
3804 if value isa Byte then return v.byte_instance(value.as(Byte))
3805 if value isa Int8 then return v.int8_instance(value.as(Int8))
3806 if value isa Int16 then return v.int16_instance(value.as(Int16))
3807 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3808 if value isa Int32 then return v.int32_instance(value.as(Int32))
3809 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3810 # Should never happen
3811 abort
3812 end
3813 end
3814
3815 redef class AFloatExpr
3816 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3817 end
3818
3819 redef class ACharExpr
3820 redef fun expr(v) do
3821 if is_code_point then
3822 return v.int_instance(value.as(not null).code_point)
3823 end
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