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