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