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