b512cddee9fbf69a7f75eef8f41071b2cd84362e
[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 # Generate the code for the body without return statement at the end and
2219 # no curly brace.
2220 # Step 7
2221 protected fun body_to_c(v: VISITOR)
2222 do
2223 mmethoddef.compile_inside_to_c(v, v.frame.arguments)
2224 end
2225
2226 # Hook called at the end of `compile_to_c` function. This function
2227 # is useful if you need to register any function compiled to c.
2228 # Step 8 (optional).
2229 protected fun end_compile_to_c(v: VISITOR)
2230 do
2231 # Nothing to do by default
2232 end
2233
2234 # Generate the code
2235 fun compile_to_c(compiler: COMPILER)
2236 do
2237 var v = compiler.new_visitor
2238 var selfvar = resolve_receiver(v)
2239 var arguments = [selfvar]
2240 var frame = build_frame(v, arguments)
2241 v.frame = frame
2242
2243 resolve_return_mtype(v)
2244 fill_parameters(v)
2245
2246 # WARNING: the signature must be declared before creating
2247 # any returnlabel and returnvar (`StaticFrame`). Otherwise,
2248 # you could end up with variable outside the function.
2249 var sig = signature_to_c(v)
2250 declare_signature(v, sig)
2251
2252 frame.returnlabel = v.get_name("RET_LABEL")
2253 if has_return then
2254 var ret_mtype = return_mtype
2255 assert ret_mtype != null
2256 frame.returnvar = v.new_var(ret_mtype)
2257 end
2258
2259 body_to_c(v)
2260 v.add("{frame.returnlabel.as(not null)}:;")
2261 if has_return then
2262 v.add("return {frame.returnvar.as(not null)};")
2263 end
2264 v.add "\}"
2265 end_compile_to_c(v)
2266 end
2267 end
2268
2269 # Base class for all thunk-like function. A thunk is a function whose purpose
2270 # is to call another function. Like a class wrapper or decorator, a thunk is used
2271 # to computer things (conversions, log, etc) before or after a function call. It's
2272 # an intermediary between the caller and the callee.
2273 #
2274 # The most basic use of a thunk is to unbox its argument before invoking the real callee.
2275 # Virtual functions are a use case of thunk function:
2276 #
2277 # ~~~~nitish
2278 # redef class Object
2279 # fun toto(x: Int): Int do return 1 + x
2280 # end
2281 # redef class Int
2282 # redef fun toto(x) do return x + self
2283 # end
2284 #
2285 # ```generated C code
2286 # long Object__toto(val* self, long x) { return 1 + x }
2287 # long Int__toto(long self, long x) { return x + self }
2288 # long THUNK_Int__toto(val* self, long x) {
2289 # long self2 = (long)(self)>>2 // Unboxing from Object to Int
2290 # return Int_toto(self2, x)
2291 # }
2292 #
2293 # ```
2294 # ~~~~
2295 #
2296 # A thunk has its OWN SIGNATURE and is considered like any other `AbstractRuntimeFunction`.
2297 # Thus, one might need to be careful when overriding parent's method. Since most usage of
2298 # thunks in Nit is to unbox and box things between a caller and a callee, the default
2299 # implementation is doing just that. In other words, a thunk takes a signature and a method
2300 # and tries to cast its signature to the underlying method's signature.
2301 #
2302 # A virtual function has the same signature as its `mmethoddef` field, except for
2303 # its receiver is of type `Object`.
2304 # In the same vibe, a call reference has all of its argument boxed as `Object`.
2305 abstract class ThunkFunction
2306 super AbstractRuntimeFunction
2307
2308 # Determines if the callsite should be polymorphic or static.
2309 var polymorph_call_flag = false is writable
2310
2311 # The type expected by the callee. Used to resolve `mmethoddef`'s formal
2312 # parameters and virtual type. This type must NOT need anchor.
2313 fun target_recv: MType is abstract
2314
2315 redef fun body_to_c(v)
2316 do
2317 assert not target_recv.need_anchor
2318 var frame = v.frame
2319 assert frame != null
2320 var selfvar = frame.selfvar
2321 var arguments = frame.arguments
2322 var arguments2 = new Array[RuntimeVariable]
2323 arguments2.push(v.autobox(selfvar, target_recv))
2324 var resolved_sig = msignature.resolve_for(target_recv, target_recv.as(MClassType), v.mmodule, true)
2325 for i in [0..resolved_sig.arity[ do
2326 var param = resolved_sig.mparameters[i]
2327 var mtype = param.mtype
2328 if param.is_vararg then
2329 mtype = v.mmodule.array_type(mtype)
2330 end
2331 var temp = v.autobox(arguments[i+1], mtype)
2332 arguments2.push(temp)
2333 end
2334 v.add("/* {mmethoddef}, {recv_mtype.ctype} */")
2335 var subret: nullable RuntimeVariable = null
2336 if polymorph_call_flag then
2337 subret = v.send(mmethoddef.mproperty, arguments2)
2338 else
2339 subret = v.call(mmethoddef, arguments2[0].mcasttype.as(MClassType), arguments2)
2340 end
2341 if has_return then
2342 assert subret != null
2343 var subret2 = v.autobox(subret, return_mtype.as(not null))
2344 v.assign(frame.returnvar.as(not null), subret2)
2345 end
2346
2347 end
2348
2349 end
2350
2351 # A runtime variable hold a runtime value in C.
2352 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
2353 #
2354 # 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.
2355 class RuntimeVariable
2356 # The name of the variable in the C code
2357 var name: String
2358
2359 # The static type of the variable (as declard in C)
2360 var mtype: MType
2361
2362 # The current casted type of the variable (as known in Nit)
2363 var mcasttype: MType is writable
2364
2365 # If the variable exaclty a mcasttype?
2366 # false (usual value) means that the variable is a mcasttype or a subtype.
2367 var is_exact: Bool = false is writable
2368
2369 init
2370 do
2371 assert not mtype.need_anchor
2372 assert not mcasttype.need_anchor
2373 end
2374
2375 redef fun to_s do return name
2376
2377 redef fun inspect
2378 do
2379 var exact_str
2380 if self.is_exact then
2381 exact_str = " exact"
2382 else
2383 exact_str = ""
2384 end
2385 var type_str
2386 if self.mtype == self.mcasttype then
2387 type_str = "{mtype}{exact_str}"
2388 else
2389 type_str = "{mtype}({mcasttype}{exact_str})"
2390 end
2391 return "<{name}:{type_str}>"
2392 end
2393 end
2394
2395 # The static context of a visited property in a `AbstractCompilerVisitor`
2396 class StaticFrame
2397
2398 type VISITOR: AbstractCompilerVisitor
2399
2400 # The associated visitor
2401 var visitor: VISITOR
2402
2403 # The executed property.
2404 # A Method in case of a call, an attribute in case of a default initialization.
2405 var mpropdef: MPropDef
2406
2407 # The static type of the receiver
2408 var receiver: MClassType
2409
2410 # Arguments of the method (the first is the receiver)
2411 var arguments: Array[RuntimeVariable]
2412
2413 # The runtime_variable associated to the return (in a function)
2414 var returnvar: nullable RuntimeVariable = null is writable
2415
2416 # The label at the end of the property
2417 var returnlabel: nullable String = null is writable
2418
2419 # Labels associated to a each escapemarks.
2420 # Because of inlinings, escape-marks must be associated to their context (the frame)
2421 private var escapemark_names = new HashMap[EscapeMark, String]
2422
2423 # The array comprehension currently filled, if any
2424 private var comprehension: nullable RuntimeVariable = null
2425
2426 # Returns the first argument (the receiver) of a frame.
2427 # REQUIRE: arguments.length >= 1
2428 fun selfvar: RuntimeVariable
2429 do
2430 assert arguments.length >= 1
2431 return arguments.first
2432 end
2433 end
2434
2435 redef class MType
2436 # Return the C type associated to a given Nit static type
2437 fun ctype: String do return "val*"
2438
2439 # C type outside of the compiler code and in boxes
2440 fun ctype_extern: String do return "val*"
2441
2442 # Short name of the `ctype` to use in unions
2443 fun ctypename: String do return "val"
2444
2445 # Is the associated C type a primitive one?
2446 #
2447 # ENSURE `result == (ctype != "val*")`
2448 fun is_c_primitive: Bool do return false
2449 end
2450
2451 redef class MClassType
2452
2453 redef var ctype is lazy do
2454 if mclass.name == "Int" then
2455 return "long"
2456 else if mclass.name == "Bool" then
2457 return "short int"
2458 else if mclass.name == "Char" then
2459 return "uint32_t"
2460 else if mclass.name == "Float" then
2461 return "double"
2462 else if mclass.name == "Int8" then
2463 return "int8_t"
2464 else if mclass.name == "Byte" then
2465 return "unsigned char"
2466 else if mclass.name == "Int16" then
2467 return "int16_t"
2468 else if mclass.name == "UInt16" then
2469 return "uint16_t"
2470 else if mclass.name == "Int32" then
2471 return "int32_t"
2472 else if mclass.name == "UInt32" then
2473 return "uint32_t"
2474 else if mclass.name == "CString" then
2475 return "char*"
2476 else if mclass.name == "NativeArray" then
2477 return "val*"
2478 else
2479 return "val*"
2480 end
2481 end
2482
2483 redef var is_c_primitive is lazy do return ctype != "val*"
2484
2485 redef fun ctype_extern: String
2486 do
2487 if mclass.kind == extern_kind then
2488 return "void*"
2489 else
2490 return ctype
2491 end
2492 end
2493
2494 redef fun ctypename: String
2495 do
2496 if mclass.name == "Int" then
2497 return "l"
2498 else if mclass.name == "Bool" then
2499 return "s"
2500 else if mclass.name == "Char" then
2501 return "c"
2502 else if mclass.name == "Float" then
2503 return "d"
2504 else if mclass.name == "Int8" then
2505 return "i8"
2506 else if mclass.name == "Byte" then
2507 return "b"
2508 else if mclass.name == "Int16" then
2509 return "i16"
2510 else if mclass.name == "UInt16" then
2511 return "u16"
2512 else if mclass.name == "Int32" then
2513 return "i32"
2514 else if mclass.name == "UInt32" then
2515 return "u32"
2516 else if mclass.name == "CString" then
2517 return "str"
2518 else if mclass.name == "NativeArray" then
2519 #return "{self.arguments.first.ctype}*"
2520 return "val"
2521 else
2522 return "val"
2523 end
2524 end
2525 end
2526
2527 redef class MPropDef
2528 type VISITOR: AbstractCompilerVisitor
2529 end
2530
2531 redef class MMethodDef
2532 # Can the body be inlined?
2533 fun can_inline(v: VISITOR): Bool
2534 do
2535 if is_abstract then return true
2536 if constant_value != null then return true
2537 var modelbuilder = v.compiler.modelbuilder
2538 var node = modelbuilder.mpropdef2node(self)
2539 if node isa APropdef then
2540 return node.can_inline
2541 else if node isa AClassdef then
2542 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2543 return true
2544 else if node == null then
2545 return true
2546 else
2547 abort
2548 end
2549 end
2550
2551 # Inline the body in another visitor
2552 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2553 do
2554 var modelbuilder = v.compiler.modelbuilder
2555 var val = constant_value
2556 var node = modelbuilder.mpropdef2node(self)
2557
2558 if is_abstract then
2559 v.add_raw_throw
2560 var cn = v.class_name_string(arguments.first)
2561 v.current_node = node
2562 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2563 v.add_raw_abort
2564 return null
2565 end
2566
2567 if node isa APropdef then
2568 var oldnode = v.current_node
2569 v.current_node = node
2570 self.compile_parameter_check(v, arguments)
2571 node.compile_to_c(v, self, arguments)
2572 v.current_node = oldnode
2573 else if node isa AClassdef then
2574 var oldnode = v.current_node
2575 v.current_node = node
2576 self.compile_parameter_check(v, arguments)
2577 node.compile_to_c(v, self, arguments)
2578 v.current_node = oldnode
2579 else if val != null then
2580 v.ret(v.value_instance(val))
2581 else
2582 abort
2583 end
2584 return null
2585 end
2586
2587 # Generate type checks in the C code to check covariant parameters
2588 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2589 do
2590 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2591
2592 var msignature = self.msignature.as(not null)
2593
2594 for i in [0..msignature.arity[ do
2595 var mp = msignature.mparameters[i]
2596 # skip test for vararg since the array is instantiated with the correct polymorphic type
2597 if mp.is_vararg then continue
2598
2599 # skip if the cast is not required
2600 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2601 if not origmtype.need_anchor then continue
2602
2603 # get the parameter type
2604 var mtype = mp.mtype
2605
2606 # generate the cast
2607 # note that v decides if and how to implements the cast
2608 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2609 v.add_cast(arguments[i+1], mtype, "covariance")
2610 end
2611 end
2612 end
2613
2614 # Node visit
2615
2616 redef class APropdef
2617 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2618 do
2619 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2620 debug("Not yet implemented")
2621 end
2622
2623 fun can_inline: Bool do return true
2624 end
2625
2626 redef class AMethPropdef
2627 redef fun compile_to_c(v, mpropdef, arguments)
2628 do
2629 # Call the implicit super-init
2630 var auto_super_inits = self.auto_super_inits
2631 if auto_super_inits != null then
2632 var args = [arguments.first]
2633 for auto_super_init in auto_super_inits do
2634 assert auto_super_init.mproperty != mpropdef.mproperty
2635 args.clear
2636 for i in [0..auto_super_init.msignature.arity+1[ do
2637 args.add(arguments[i])
2638 end
2639 assert auto_super_init.mproperty != mpropdef.mproperty
2640 v.compile_callsite(auto_super_init, args)
2641 end
2642 end
2643 if auto_super_call then
2644 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2645 end
2646
2647 # Try special compilation
2648 if mpropdef.is_intern then
2649 if compile_intern_to_c(v, mpropdef, arguments) then return
2650 end
2651 if mpropdef.is_extern then
2652 if mpropdef.mproperty.is_init then
2653 if compile_externinit_to_c(v, mpropdef, arguments) then return
2654 else
2655 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2656 end
2657 end
2658
2659 # Compile block if any
2660 var n_block = n_block
2661 if n_block != null then
2662 for i in [0..mpropdef.msignature.arity[ do
2663 var variable = self.n_signature.n_params[i].variable.as(not null)
2664 v.assign(v.variable(variable), arguments[i+1])
2665 end
2666 v.stmt(n_block)
2667 return
2668 end
2669
2670 # We have a problem
2671 v.add_raw_throw
2672 var cn = v.class_name_string(arguments.first)
2673 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2674 v.add_raw_abort
2675 end
2676
2677 redef fun can_inline
2678 do
2679 if self.auto_super_inits != null then return false
2680 var nblock = self.n_block
2681 if nblock == null then return true
2682 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2683 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2684 return false
2685 end
2686
2687 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2688 do
2689 var pname = mpropdef.mproperty.name
2690 var cname = mpropdef.mclassdef.mclass.name
2691 var ret = mpropdef.msignature.return_mtype
2692 if ret != null then
2693 ret = v.resolve_for(ret, arguments.first)
2694 end
2695 if pname != "==" and pname != "!=" then
2696 v.adapt_signature(mpropdef, arguments)
2697 v.unbox_signature_extern(mpropdef, arguments)
2698 end
2699 if cname == "Int" then
2700 if pname == "output" then
2701 v.add("printf(\"%ld\\n\", {arguments.first});")
2702 return true
2703 else if pname == "object_id" then
2704 v.ret(arguments.first)
2705 return true
2706 else if pname == "+" then
2707 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2708 return true
2709 else if pname == "-" then
2710 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2711 return true
2712 else if pname == "unary -" then
2713 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2714 return true
2715 else if pname == "unary +" then
2716 v.ret(arguments[0])
2717 return true
2718 else if pname == "*" then
2719 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2720 return true
2721 else if pname == "/" then
2722 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2723 return true
2724 else if pname == "%" then
2725 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2726 return true
2727 else if pname == "==" then
2728 v.ret(v.equal_test(arguments[0], arguments[1]))
2729 return true
2730 else if pname == "!=" then
2731 var res = v.equal_test(arguments[0], arguments[1])
2732 v.ret(v.new_expr("!{res}", ret.as(not null)))
2733 return true
2734 else if pname == "<" then
2735 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2736 return true
2737 else if pname == ">" then
2738 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2739 return true
2740 else if pname == "<=" then
2741 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2742 return true
2743 else if pname == ">=" then
2744 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2745 return true
2746 else if pname == "to_i8" then
2747 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2748 return true
2749 else if pname == "to_i16" then
2750 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2751 return true
2752 else if pname == "to_u16" then
2753 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2754 return true
2755 else if pname == "to_i32" then
2756 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2757 return true
2758 else if pname == "to_u32" then
2759 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2760 return true
2761 else if pname == "to_f" then
2762 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2763 return true
2764 else if pname == "to_b" then
2765 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2766 return true
2767 else if pname == "code_point" then
2768 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2769 return true
2770 else if pname == "&" then
2771 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2772 return true
2773 else if pname == "|" then
2774 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2775 return true
2776 else if pname == ">>" then
2777 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2778 return true
2779 else if pname == "<<" then
2780 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2781 return true
2782 end
2783 else if cname == "Char" then
2784 if pname == "object_id" then
2785 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2786 return true
2787 else if pname == "successor" then
2788 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2789 return true
2790 else if pname == "predecessor" then
2791 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2792 return true
2793 else if pname == "==" then
2794 v.ret(v.equal_test(arguments[0], arguments[1]))
2795 return true
2796 else if pname == "!=" then
2797 var res = v.equal_test(arguments[0], arguments[1])
2798 v.ret(v.new_expr("!{res}", ret.as(not null)))
2799 return true
2800 else if pname == "<" then
2801 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2802 return true
2803 else if pname == ">" then
2804 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2805 return true
2806 else if pname == "<=" then
2807 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2808 return true
2809 else if pname == ">=" then
2810 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2811 return true
2812 else if pname == "to_i" then
2813 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2814 return true
2815 else if pname == "code_point" then
2816 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2817 return true
2818 end
2819 else if cname == "Byte" then
2820 if pname == "output" then
2821 v.add("printf(\"%x\\n\", {arguments.first});")
2822 return true
2823 else if pname == "object_id" then
2824 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2825 return true
2826 else if pname == "+" then
2827 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2828 return true
2829 else if pname == "-" then
2830 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2831 return true
2832 else if pname == "unary -" then
2833 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2834 return true
2835 else if pname == "unary +" then
2836 v.ret(arguments[0])
2837 return true
2838 else if pname == "*" then
2839 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2840 return true
2841 else if pname == "/" then
2842 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2843 return true
2844 else if pname == "%" then
2845 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2846 return true
2847 else if pname == "==" then
2848 v.ret(v.equal_test(arguments[0], arguments[1]))
2849 return true
2850 else if pname == "!=" then
2851 var res = v.equal_test(arguments[0], arguments[1])
2852 v.ret(v.new_expr("!{res}", ret.as(not null)))
2853 return true
2854 else if pname == "<" then
2855 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2856 return true
2857 else if pname == ">" then
2858 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2859 return true
2860 else if pname == "<=" then
2861 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2862 return true
2863 else if pname == ">=" then
2864 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2865 return true
2866 else if pname == ">>" then
2867 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2868 return true
2869 else if pname == "<<" then
2870 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2871 return true
2872 else if pname == "&" then
2873 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2874 return true
2875 else if pname == "to_i" then
2876 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2877 return true
2878 else if pname == "to_f" then
2879 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2880 return true
2881 else if pname == "to_i8" then
2882 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2883 return true
2884 else if pname == "to_i16" then
2885 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2886 return true
2887 else if pname == "to_u16" then
2888 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2889 return true
2890 else if pname == "to_i32" then
2891 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2892 return true
2893 else if pname == "to_u32" then
2894 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2895 return true
2896 else if pname == "ascii" then
2897 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2898 return true
2899 end
2900 else if cname == "Bool" then
2901 if pname == "output" then
2902 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2903 return true
2904 else if pname == "object_id" then
2905 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2906 return true
2907 else if pname == "==" then
2908 v.ret(v.equal_test(arguments[0], arguments[1]))
2909 return true
2910 else if pname == "!=" then
2911 var res = v.equal_test(arguments[0], arguments[1])
2912 v.ret(v.new_expr("!{res}", ret.as(not null)))
2913 return true
2914 end
2915 else if cname == "Float" then
2916 if pname == "output" then
2917 v.add("printf(\"%f\\n\", {arguments.first});")
2918 return true
2919 else if pname == "object_id" then
2920 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2921 return true
2922 else if pname == "+" then
2923 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2924 return true
2925 else if pname == "-" then
2926 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2927 return true
2928 else if pname == "unary -" then
2929 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2930 return true
2931 else if pname == "unary +" then
2932 v.ret(arguments[0])
2933 return true
2934 else if pname == "succ" then
2935 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2936 return true
2937 else if pname == "prec" then
2938 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2939 return true
2940 else if pname == "*" then
2941 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2942 return true
2943 else if pname == "/" then
2944 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2945 return true
2946 else if pname == "==" then
2947 v.ret(v.equal_test(arguments[0], arguments[1]))
2948 return true
2949 else if pname == "!=" then
2950 var res = v.equal_test(arguments[0], arguments[1])
2951 v.ret(v.new_expr("!{res}", ret.as(not null)))
2952 return true
2953 else if pname == "<" then
2954 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2955 return true
2956 else if pname == ">" then
2957 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2958 return true
2959 else if pname == "<=" then
2960 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2961 return true
2962 else if pname == ">=" then
2963 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2964 return true
2965 else if pname == "to_i" then
2966 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2967 return true
2968 else if pname == "to_b" then
2969 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2970 return true
2971 else if pname == "to_i8" then
2972 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2973 return true
2974 else if pname == "to_i16" then
2975 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2976 return true
2977 else if pname == "to_u16" then
2978 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2979 return true
2980 else if pname == "to_i32" then
2981 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2982 return true
2983 else if pname == "to_u32" then
2984 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2985 return true
2986 end
2987 else if cname == "CString" then
2988 if pname == "[]" then
2989 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2990 return true
2991 else if pname == "[]=" then
2992 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2993 return true
2994 else if pname == "copy_to" then
2995 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2996 return true
2997 else if pname == "atoi" then
2998 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2999 return true
3000 else if pname == "fast_cstring" then
3001 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3002 return true
3003 else if pname == "==" then
3004 v.ret(v.equal_test(arguments[0], arguments[1]))
3005 return true
3006 else if pname == "!=" then
3007 var res = v.equal_test(arguments[0], arguments[1])
3008 v.ret(v.new_expr("!{res}", ret.as(not null)))
3009 return true
3010 else if pname == "new" then
3011 var alloc = v.nit_alloc(arguments[1].to_s, "CString")
3012 v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
3013 return true
3014 else if pname == "fetch_4_chars" then
3015 v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
3016 return true
3017 else if pname == "fetch_4_hchars" then
3018 v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
3019 return true
3020 end
3021 else if cname == "NativeArray" then
3022 return v.native_array_def(pname, ret, arguments)
3023 else if cname == "Int8" then
3024 if pname == "output" then
3025 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
3026 return true
3027 else if pname == "object_id" then
3028 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3029 return true
3030 else if pname == "+" then
3031 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3032 return true
3033 else if pname == "-" then
3034 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3035 return true
3036 else if pname == "unary -" then
3037 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3038 return true
3039 else if pname == "unary +" then
3040 v.ret(arguments[0])
3041 return true
3042 else if pname == "*" then
3043 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3044 return true
3045 else if pname == "/" then
3046 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3047 return true
3048 else if pname == "%" then
3049 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3050 return true
3051 else if pname == "<<" then
3052 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3053 return true
3054 else if pname == ">>" then
3055 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3056 return true
3057 else if pname == "==" then
3058 v.ret(v.equal_test(arguments[0], arguments[1]))
3059 return true
3060 else if pname == "!=" then
3061 var res = v.equal_test(arguments[0], arguments[1])
3062 v.ret(v.new_expr("!{res}", ret.as(not null)))
3063 return true
3064 else if pname == "<" then
3065 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3066 return true
3067 else if pname == ">" then
3068 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3069 return true
3070 else if pname == "<=" then
3071 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3072 return true
3073 else if pname == ">=" then
3074 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3075 return true
3076 else if pname == "to_i" then
3077 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3078 return true
3079 else if pname == "to_b" then
3080 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3081 return true
3082 else if pname == "to_i16" then
3083 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3084 return true
3085 else if pname == "to_u16" then
3086 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3087 return true
3088 else if pname == "to_i32" then
3089 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3090 return true
3091 else if pname == "to_u32" then
3092 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
3093 return true
3094 else if pname == "to_f" then
3095 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3096 return true
3097 else if pname == "&" then
3098 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3099 return true
3100 else if pname == "|" then
3101 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3102 return true
3103 else if pname == "^" then
3104 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3105 return true
3106 else if pname == "unary ~" then
3107 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3108 return true
3109 end
3110 else if cname == "Int16" then
3111 if pname == "output" then
3112 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
3113 return true
3114 else if pname == "object_id" then
3115 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3116 return true
3117 else if pname == "+" then
3118 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3119 return true
3120 else if pname == "-" then
3121 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3122 return true
3123 else if pname == "unary -" then
3124 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3125 return true
3126 else if pname == "unary +" then
3127 v.ret(arguments[0])
3128 return true
3129 else if pname == "*" then
3130 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3131 return true
3132 else if pname == "/" then
3133 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3134 return true
3135 else if pname == "%" then
3136 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3137 return true
3138 else if pname == "<<" then
3139 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3140 return true
3141 else if pname == ">>" then
3142 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3143 return true
3144 else if pname == "==" then
3145 v.ret(v.equal_test(arguments[0], arguments[1]))
3146 return true
3147 else if pname == "!=" then
3148 var res = v.equal_test(arguments[0], arguments[1])
3149 v.ret(v.new_expr("!{res}", ret.as(not null)))
3150 return true
3151 else if pname == "<" then
3152 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3153 return true
3154 else if pname == ">" then
3155 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3156 return true
3157 else if pname == "<=" then
3158 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3159 return true
3160 else if pname == ">=" then
3161 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3162 return true
3163 else if pname == "to_i" then
3164 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3165 return true
3166 else if pname == "to_b" then
3167 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3168 return true
3169 else if pname == "to_i8" then
3170 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3171 return true
3172 else if pname == "to_u16" then
3173 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3174 return true
3175 else if pname == "to_i32" then
3176 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3177 return true
3178 else if pname == "to_u32" then
3179 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
3180 return true
3181 else if pname == "to_f" then
3182 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3183 return true
3184 else if pname == "&" then
3185 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3186 return true
3187 else if pname == "|" then
3188 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3189 return true
3190 else if pname == "^" then
3191 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3192 return true
3193 else if pname == "unary ~" then
3194 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3195 return true
3196 end
3197 else if cname == "UInt16" then
3198 if pname == "output" then
3199 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
3200 return true
3201 else if pname == "object_id" then
3202 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3203 return true
3204 else if pname == "+" then
3205 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3206 return true
3207 else if pname == "-" then
3208 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3209 return true
3210 else if pname == "unary -" then
3211 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3212 return true
3213 else if pname == "unary +" then
3214 v.ret(arguments[0])
3215 return true
3216 else if pname == "*" then
3217 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3218 return true
3219 else if pname == "/" then
3220 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3221 return true
3222 else if pname == "%" then
3223 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3224 return true
3225 else if pname == "<<" then
3226 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3227 return true
3228 else if pname == ">>" then
3229 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3230 return true
3231 else if pname == "==" then
3232 v.ret(v.equal_test(arguments[0], arguments[1]))
3233 return true
3234 else if pname == "!=" then
3235 var res = v.equal_test(arguments[0], arguments[1])
3236 v.ret(v.new_expr("!{res}", ret.as(not null)))
3237 return true
3238 else if pname == "<" then
3239 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3240 return true
3241 else if pname == ">" then
3242 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3243 return true
3244 else if pname == "<=" then
3245 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3246 return true
3247 else if pname == ">=" then
3248 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3249 return true
3250 else if pname == "to_i" then
3251 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3252 return true
3253 else if pname == "to_b" then
3254 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3255 return true
3256 else if pname == "to_i8" then
3257 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3258 return true
3259 else if pname == "to_i16" then
3260 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3261 return true
3262 else if pname == "to_i32" then
3263 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3264 return true
3265 else if pname == "to_u32" then
3266 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
3267 return true
3268 else if pname == "to_f" then
3269 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3270 return true
3271 else if pname == "&" then
3272 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3273 return true
3274 else if pname == "|" then
3275 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3276 return true
3277 else if pname == "^" then
3278 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3279 return true
3280 else if pname == "unary ~" then
3281 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3282 return true
3283 end
3284 else if cname == "Int32" then
3285 if pname == "output" then
3286 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
3287 return true
3288 else if pname == "object_id" then
3289 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3290 return true
3291 else if pname == "+" then
3292 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3293 return true
3294 else if pname == "-" then
3295 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3296 return true
3297 else if pname == "unary -" then
3298 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3299 return true
3300 else if pname == "unary +" then
3301 v.ret(arguments[0])
3302 return true
3303 else if pname == "*" then
3304 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3305 return true
3306 else if pname == "/" then
3307 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3308 return true
3309 else if pname == "%" then
3310 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3311 return true
3312 else if pname == "<<" then
3313 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3314 return true
3315 else if pname == ">>" then
3316 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3317 return true
3318 else if pname == "==" then
3319 v.ret(v.equal_test(arguments[0], arguments[1]))
3320 return true
3321 else if pname == "!=" then
3322 var res = v.equal_test(arguments[0], arguments[1])
3323 v.ret(v.new_expr("!{res}", ret.as(not null)))
3324 return true
3325 else if pname == "<" then
3326 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3327 return true
3328 else if pname == ">" then
3329 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3330 return true
3331 else if pname == "<=" then
3332 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3333 return true
3334 else if pname == ">=" then
3335 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3336 return true
3337 else if pname == "to_i" then
3338 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3339 return true
3340 else if pname == "to_b" then
3341 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3342 return true
3343 else if pname == "to_i8" then
3344 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3345 return true
3346 else if pname == "to_i16" then
3347 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3348 return true
3349 else if pname == "to_u16" then
3350 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3351 return true
3352 else if pname == "to_u32" then
3353 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
3354 return true
3355 else if pname == "to_f" then
3356 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3357 return true
3358 else if pname == "&" then
3359 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3360 return true
3361 else if pname == "|" then
3362 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3363 return true
3364 else if pname == "^" then
3365 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3366 return true
3367 else if pname == "unary ~" then
3368 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3369 return true
3370 end
3371 else if cname == "UInt32" then
3372 if pname == "output" then
3373 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
3374 return true
3375 else if pname == "object_id" then
3376 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3377 return true
3378 else if pname == "+" then
3379 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
3380 return true
3381 else if pname == "-" then
3382 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
3383 return true
3384 else if pname == "unary -" then
3385 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
3386 return true
3387 else if pname == "unary +" then
3388 v.ret(arguments[0])
3389 return true
3390 else if pname == "*" then
3391 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
3392 return true
3393 else if pname == "/" then
3394 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
3395 return true
3396 else if pname == "%" then
3397 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
3398 return true
3399 else if pname == "<<" then
3400 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
3401 return true
3402 else if pname == ">>" then
3403 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
3404 return true
3405 else if pname == "==" then
3406 v.ret(v.equal_test(arguments[0], arguments[1]))
3407 return true
3408 else if pname == "!=" then
3409 var res = v.equal_test(arguments[0], arguments[1])
3410 v.ret(v.new_expr("!{res}", ret.as(not null)))
3411 return true
3412 else if pname == "<" then
3413 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
3414 return true
3415 else if pname == ">" then
3416 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
3417 return true
3418 else if pname == "<=" then
3419 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
3420 return true
3421 else if pname == ">=" then
3422 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
3423 return true
3424 else if pname == "to_i" then
3425 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
3426 return true
3427 else if pname == "to_b" then
3428 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
3429 return true
3430 else if pname == "to_i8" then
3431 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
3432 return true
3433 else if pname == "to_i16" then
3434 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
3435 return true
3436 else if pname == "to_u16" then
3437 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
3438 return true
3439 else if pname == "to_i32" then
3440 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
3441 return true
3442 else if pname == "to_f" then
3443 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
3444 return true
3445 else if pname == "&" then
3446 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
3447 return true
3448 else if pname == "|" then
3449 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
3450 return true
3451 else if pname == "^" then
3452 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
3453 return true
3454 else if pname == "unary ~" then
3455 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
3456 return true
3457 end
3458 end
3459 if pname == "exit" then
3460 v.add("exit((int){arguments[1]});")
3461 return true
3462 else if pname == "sys" then
3463 v.ret(v.new_expr("glob_sys", ret.as(not null)))
3464 return true
3465 else if pname == "object_id" then
3466 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
3467 return true
3468 else if pname == "is_same_type" then
3469 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
3470 return true
3471 else if pname == "is_same_instance" then
3472 v.ret(v.equal_test(arguments[0], arguments[1]))
3473 return true
3474 else if pname == "output_class_name" then
3475 var nat = v.class_name_string(arguments.first)
3476 v.add("printf(\"%s\\n\", {nat});")
3477 return true
3478 else if pname == "native_class_name" then
3479 var nat = v.class_name_string(arguments.first)
3480 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
3481 return true
3482 else if pname == "force_garbage_collection" then
3483 v.add("nit_gcollect();")
3484 return true
3485 else if pname == "native_argc" then
3486 v.ret(v.new_expr("glob_argc", ret.as(not null)))
3487 return true
3488 else if pname == "native_argv" then
3489 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
3490 return true
3491 end
3492 return false
3493 end
3494
3495 # Compile an extern method
3496 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3497 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3498 do
3499 var externname
3500 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3501 if at != null and at.n_args.length == 1 then
3502 externname = at.arg_as_string(v.compiler.modelbuilder)
3503 if externname == null then return false
3504 else
3505 return false
3506 end
3507 v.add_extern(mpropdef.mclassdef.mmodule)
3508 var res: nullable RuntimeVariable = null
3509 var ret = mpropdef.msignature.return_mtype
3510 if ret != null then
3511 ret = v.resolve_for(ret, arguments.first)
3512 res = v.new_var_extern(ret)
3513 end
3514 v.adapt_signature(mpropdef, arguments)
3515 v.unbox_signature_extern(mpropdef, arguments)
3516
3517 if res == null then
3518 v.add("{externname}({arguments.join(", ")});")
3519 else
3520 v.add("{res} = {externname}({arguments.join(", ")});")
3521 res = v.box_extern(res, ret.as(not null))
3522 v.ret(res)
3523 end
3524 return true
3525 end
3526
3527 # Compile an extern factory
3528 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3529 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3530 do
3531 var externname
3532 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3533 if at != null then
3534 externname = at.arg_as_string(v.compiler.modelbuilder)
3535 if externname == null then return false
3536 else
3537 return false
3538 end
3539 v.add_extern(mpropdef.mclassdef.mmodule)
3540 v.adapt_signature(mpropdef, arguments)
3541 v.unbox_signature_extern(mpropdef, arguments)
3542 var ret = arguments.first.mtype
3543 var res = v.new_var_extern(ret)
3544
3545 arguments.shift
3546
3547 v.add("{res} = {externname}({arguments.join(", ")});")
3548 res = v.box_extern(res, ret)
3549 v.ret(res)
3550 return true
3551 end
3552 end
3553
3554 redef class AAttrPropdef
3555 redef fun can_inline: Bool do return not is_lazy
3556
3557 redef fun compile_to_c(v, mpropdef, arguments)
3558 do
3559 if mpropdef == mreadpropdef then
3560 assert arguments.length == 1
3561 var recv = arguments.first
3562 var res
3563 if is_lazy then
3564 var set
3565 var ret = self.mtype
3566 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3567 var guard = self.mlazypropdef.mproperty
3568 if useiset then
3569 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3570 else
3571 set = v.read_attribute(guard, recv)
3572 end
3573 v.add("if(likely({set})) \{")
3574 res = v.read_attribute(self.mpropdef.mproperty, recv)
3575 v.add("\} else \{")
3576
3577 var value = evaluate_expr(v, recv)
3578
3579 v.assign(res, value)
3580 if not useiset then
3581 var true_v = v.bool_instance(true)
3582 v.write_attribute(guard, arguments.first, true_v)
3583 end
3584 v.add("\}")
3585 else
3586 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3587 end
3588 v.assign(v.frame.returnvar.as(not null), res)
3589 else if mpropdef == mwritepropdef then
3590 assert arguments.length == 2
3591 var recv = arguments.first
3592 var arg = arguments[1]
3593 if is_optional and v.maybe_null(arg) then
3594 var value = v.new_var(self.mpropdef.static_mtype.as(not null))
3595 v.add("if ({arg} == NULL) \{")
3596 v.assign(value, evaluate_expr(v, recv))
3597 v.add("\} else \{")
3598 v.assign(value, arg)
3599 v.add("\}")
3600 arg = value
3601 end
3602 v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
3603 if is_lazy then
3604 var ret = self.mtype
3605 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3606 if not useiset then
3607 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3608 end
3609 end
3610 else
3611 abort
3612 end
3613 end
3614
3615 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3616 do
3617 if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3618 end
3619
3620 # Evaluate, store and return the default value of the attribute
3621 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3622 do
3623 var oldnode = v.current_node
3624 v.current_node = self
3625 var old_frame = v.frame
3626 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3627 v.frame = frame
3628
3629 var value
3630 var mtype = self.mtype
3631 assert mtype != null
3632
3633 var nexpr = self.n_expr
3634 var nblock = self.n_block
3635 if nexpr != null then
3636 value = v.expr(nexpr, mtype)
3637 else if nblock != null then
3638 value = v.new_var(mtype)
3639 frame.returnvar = value
3640 frame.returnlabel = v.get_name("RET_LABEL")
3641 v.add("\{")
3642 v.stmt(nblock)
3643 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3644 v.add("\}")
3645 else
3646 abort
3647 end
3648
3649 v.write_attribute(self.mpropdef.mproperty, recv, value)
3650
3651 v.frame = old_frame
3652 v.current_node = oldnode
3653
3654 return value
3655 end
3656
3657 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3658 do
3659 var nexpr = self.n_expr
3660 if nexpr != null then return
3661
3662 var oldnode = v.current_node
3663 v.current_node = self
3664 var old_frame = v.frame
3665 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3666 v.frame = frame
3667 # Force read to check the initialization
3668 v.read_attribute(self.mpropdef.mproperty, recv)
3669 v.frame = old_frame
3670 v.current_node = oldnode
3671 end
3672 end
3673
3674 redef class AClassdef
3675 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3676 do
3677 if mpropdef.mproperty.is_root_init then
3678 assert arguments.length == 1
3679 if not mpropdef.is_intro then
3680 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3681 end
3682 return
3683 else
3684 abort
3685 end
3686 end
3687 end
3688
3689 redef class AExpr
3690 # Try to compile self as an expression
3691 # Do not call this method directly, use `v.expr` instead
3692 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3693 do
3694 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3695 var mtype = self.mtype
3696 if mtype == null then
3697 return null
3698 else
3699 var res = v.new_var(mtype)
3700 v.add("/* {res} = NOT YET {class_name} */")
3701 return res
3702 end
3703 end
3704
3705 # Try to compile self as a statement
3706 # Do not call this method directly, use `v.stmt` instead
3707 private fun stmt(v: AbstractCompilerVisitor)
3708 do
3709 expr(v)
3710 end
3711 end
3712
3713 redef class ABlockExpr
3714 redef fun stmt(v)
3715 do
3716 for e in self.n_expr do v.stmt(e)
3717 end
3718 redef fun expr(v)
3719 do
3720 var last = self.n_expr.last
3721 for e in self.n_expr do
3722 if e == last then break
3723 v.stmt(e)
3724 end
3725 return v.expr(last, null)
3726 end
3727 end
3728
3729 redef class AVardeclExpr
3730 redef fun stmt(v)
3731 do
3732 var variable = self.variable.as(not null)
3733 var ne = self.n_expr
3734 if ne != null then
3735 var i = v.expr(ne, variable.declared_type)
3736 v.assign(v.variable(variable), i)
3737 end
3738 end
3739 end
3740
3741 redef class AVarExpr
3742 redef fun expr(v)
3743 do
3744 var res = v.variable(self.variable.as(not null))
3745 var mtype = self.mtype.as(not null)
3746 return v.autoadapt(res, mtype)
3747 end
3748 end
3749
3750 redef class AVarAssignExpr
3751 redef fun expr(v)
3752 do
3753 var variable = self.variable.as(not null)
3754 var i = v.expr(self.n_value, variable.declared_type)
3755 v.assign(v.variable(variable), i)
3756 return i
3757 end
3758 end
3759
3760 redef class AVarReassignExpr
3761 redef fun stmt(v)
3762 do
3763 var variable = self.variable.as(not null)
3764 var vari = v.variable(variable)
3765 var value = v.expr(self.n_value, variable.declared_type)
3766 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3767 assert res != null
3768 v.assign(v.variable(variable), res)
3769 end
3770 end
3771
3772 redef class ASelfExpr
3773 redef fun expr(v) do return v.frame.arguments.first
3774 end
3775
3776 redef class AImplicitSelfExpr
3777 redef fun expr(v) do
3778 if not is_sys then return super
3779 return v.new_expr("glob_sys", mtype.as(not null))
3780 end
3781 end
3782
3783 redef class AEscapeExpr
3784 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3785 end
3786
3787 redef class AReturnExpr
3788 redef fun stmt(v)
3789 do
3790 var nexpr = self.n_expr
3791 if nexpr != null then
3792 var returnvar = v.frame.returnvar.as(not null)
3793 var i = v.expr(nexpr, returnvar.mtype)
3794 v.assign(returnvar, i)
3795 end
3796 v.add("goto {v.frame.returnlabel.as(not null)};")
3797 end
3798 end
3799
3800 redef class AAbortExpr
3801 redef fun stmt(v) do v.add_abort("Aborted")
3802 end
3803
3804 redef class AIfExpr
3805 redef fun stmt(v)
3806 do
3807 var cond = v.expr_bool(self.n_expr)
3808 v.add("if ({cond})\{")
3809 v.stmt(self.n_then)
3810 v.add("\} else \{")
3811 v.stmt(self.n_else)
3812 v.add("\}")
3813 end
3814
3815 redef fun expr(v)
3816 do
3817 var res = v.new_var(self.mtype.as(not null))
3818 var cond = v.expr_bool(self.n_expr)
3819 v.add("if ({cond})\{")
3820 v.assign(res, v.expr(self.n_then.as(not null), null))
3821 v.add("\} else \{")
3822 v.assign(res, v.expr(self.n_else.as(not null), null))
3823 v.add("\}")
3824 return res
3825 end
3826 end
3827
3828 redef class AIfexprExpr
3829 redef fun expr(v)
3830 do
3831 var res = v.new_var(self.mtype.as(not null))
3832 var cond = v.expr_bool(self.n_expr)
3833 v.add("if ({cond})\{")
3834 v.assign(res, v.expr(self.n_then, null))
3835 v.add("\} else \{")
3836 v.assign(res, v.expr(self.n_else, null))
3837 v.add("\}")
3838 return res
3839 end
3840 end
3841
3842 redef class ADoExpr
3843 redef fun stmt(v)
3844 do
3845 if self.n_catch != null then
3846 v.add("\{")
3847 v.add("struct catch_stack_t *catchStack = getCatchStack();")
3848 v.add("if(catchStack->currentSize == 0) \{")
3849 v.add(" catchStack->cursor = -1;")
3850 v.add(" catchStack->currentSize = 100;")
3851 v.add(" catchStack->envs = malloc(sizeof(jmp_buf)*100);")
3852 v.add("\} else if(catchStack->cursor == catchStack->currentSize - 1) \{")
3853 v.add(" catchStack->currentSize *= 2;")
3854 v.add(" catchStack->envs = realloc(catchStack->envs, sizeof(jmp_buf)*catchStack->currentSize);")
3855 v.add("\}")
3856 v.add("catchStack->cursor += 1;")
3857 v.add("if(!setjmp(catchStack->envs[catchStack->cursor]))\{")
3858 v.stmt(self.n_block)
3859 v.add("catchStack->cursor -= 1;")
3860 v.add("\}else \{")
3861 v.add("catchStack->cursor -= 1;")
3862 v.stmt(self.n_catch)
3863 v.add("\}")
3864 v.add("\}")
3865 else
3866 v.stmt(self.n_block)
3867 end
3868 v.add_escape_label(break_mark)
3869 end
3870 end
3871
3872 redef class AWhileExpr
3873 redef fun stmt(v)
3874 do
3875 v.add("for(;;) \{")
3876 var cond = v.expr_bool(self.n_expr)
3877 v.add("if (!{cond}) break;")
3878 v.stmt(self.n_block)
3879 v.add_escape_label(continue_mark)
3880 v.add("\}")
3881 v.add_escape_label(break_mark)
3882 end
3883 end
3884
3885 redef class ALoopExpr
3886 redef fun stmt(v)
3887 do
3888 v.add("for(;;) \{")
3889 v.stmt(self.n_block)
3890 v.add_escape_label(continue_mark)
3891 v.add("\}")
3892 v.add_escape_label(break_mark)
3893 end
3894 end
3895
3896 redef class AForExpr
3897 redef fun stmt(v)
3898 do
3899 for g in n_groups do
3900 var cl = v.expr(g.n_expr, null)
3901 var it_meth = g.method_iterator
3902 assert it_meth != null
3903 var it = v.compile_callsite(it_meth, [cl])
3904 assert it != null
3905 g.it = it
3906 end
3907 v.add("for(;;) \{")
3908 for g in n_groups do
3909 var it = g.it
3910 var isok_meth = g.method_is_ok
3911 assert isok_meth != null
3912 var ok = v.compile_callsite(isok_meth, [it])
3913 assert ok != null
3914 v.add("if(!{ok}) break;")
3915 if g.variables.length == 1 then
3916 var item_meth = g.method_item
3917 assert item_meth != null
3918 var i = v.compile_callsite(item_meth, [it])
3919 assert i != null
3920 v.assign(v.variable(g.variables.first), i)
3921 else if g.variables.length == 2 then
3922 var key_meth = g.method_key
3923 assert key_meth != null
3924 var i = v.compile_callsite(key_meth, [it])
3925 assert i != null
3926 v.assign(v.variable(g.variables[0]), i)
3927 var item_meth = g.method_item
3928 assert item_meth != null
3929 i = v.compile_callsite(item_meth, [it])
3930 assert i != null
3931 v.assign(v.variable(g.variables[1]), i)
3932 else
3933 abort
3934 end
3935 end
3936 v.stmt(self.n_block)
3937 v.add_escape_label(continue_mark)
3938 for g in n_groups do
3939 var next_meth = g.method_next
3940 assert next_meth != null
3941 v.compile_callsite(next_meth, [g.it])
3942 end
3943 v.add("\}")
3944 v.add_escape_label(break_mark)
3945
3946 for g in n_groups do
3947 var method_finish = g.method_finish
3948 if method_finish != null then
3949 # TODO: Find a way to call this also in long escape (e.g. return)
3950 v.compile_callsite(method_finish, [g.it])
3951 end
3952 end
3953 end
3954 end
3955
3956 redef class AForGroup
3957 # C variable representing the iterator
3958 private var it: RuntimeVariable is noinit
3959 end
3960
3961 redef class AAssertExpr
3962 redef fun stmt(v)
3963 do
3964 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3965
3966 var cond = v.expr_bool(self.n_expr)
3967 v.add("if (unlikely(!{cond})) \{")
3968 v.stmt(self.n_else)
3969
3970 explain_assert v
3971
3972 var nid = self.n_id
3973 if nid != null then
3974 v.add_abort("Assert '{nid.text}' failed")
3975 else
3976 v.add_abort("Assert failed")
3977 end
3978 v.add("\}")
3979 end
3980
3981 # Explain assert if it fails
3982 private fun explain_assert(v: AbstractCompilerVisitor)
3983 do
3984 var explain_assert_str = explain_assert_str
3985 if explain_assert_str == null then return
3986
3987 var nas = v.compiler.modelbuilder.model.get_mclasses_by_name("NativeArray")
3988 if nas == null then return
3989
3990 nas = v.compiler.modelbuilder.model.get_mclasses_by_name("Array")
3991 if nas == null or nas.is_empty then return
3992
3993 var expr = explain_assert_str.expr(v)
3994 if expr == null then return
3995
3996 var cstr = v.send(v.get_property("to_cstring", expr.mtype), [expr])
3997 if cstr == null then return
3998
3999 v.add "PRINT_ERROR(\"Runtime assert: %s\\n\", {cstr});"
4000 end
4001 end
4002
4003 redef class AOrExpr
4004 redef fun expr(v)
4005 do
4006 var res = v.new_var(self.mtype.as(not null))
4007 var i1 = v.expr_bool(self.n_expr)
4008 v.add("if ({i1}) \{")
4009 v.add("{res} = 1;")
4010 v.add("\} else \{")
4011 var i2 = v.expr_bool(self.n_expr2)
4012 v.add("{res} = {i2};")
4013 v.add("\}")
4014 return res
4015 end
4016 end
4017
4018 redef class AImpliesExpr
4019 redef fun expr(v)
4020 do
4021 var res = v.new_var(self.mtype.as(not null))
4022 var i1 = v.expr_bool(self.n_expr)
4023 v.add("if (!{i1}) \{")
4024 v.add("{res} = 1;")
4025 v.add("\} else \{")
4026 var i2 = v.expr_bool(self.n_expr2)
4027 v.add("{res} = {i2};")
4028 v.add("\}")
4029 return res
4030 end
4031 end
4032
4033 redef class AAndExpr
4034 redef fun expr(v)
4035 do
4036 var res = v.new_var(self.mtype.as(not null))
4037 var i1 = v.expr_bool(self.n_expr)
4038 v.add("if (!{i1}) \{")
4039 v.add("{res} = 0;")
4040 v.add("\} else \{")
4041 var i2 = v.expr_bool(self.n_expr2)
4042 v.add("{res} = {i2};")
4043 v.add("\}")
4044 return res
4045 end
4046 end
4047
4048 redef class ANotExpr
4049 redef fun expr(v)
4050 do
4051 var cond = v.expr_bool(self.n_expr)
4052 return v.new_expr("!{cond}", self.mtype.as(not null))
4053 end
4054 end
4055
4056 redef class AOrElseExpr
4057 redef fun expr(v)
4058 do
4059 var res = v.new_var(self.mtype.as(not null))
4060 var i1 = v.expr(self.n_expr, null)
4061
4062 if not v.maybe_null(i1) then return i1
4063
4064 v.add("if ({i1}!=NULL) \{")
4065 v.assign(res, i1)
4066 v.add("\} else \{")
4067 var i2 = v.expr(self.n_expr2, null)
4068 v.assign(res, i2)
4069 v.add("\}")
4070 return res
4071 end
4072 end
4073
4074 redef class AIntegerExpr
4075 redef fun expr(v) do
4076 if value isa Int then return v.int_instance(value.as(Int))
4077 if value isa Byte then return v.byte_instance(value.as(Byte))
4078 if value isa Int8 then return v.int8_instance(value.as(Int8))
4079 if value isa Int16 then return v.int16_instance(value.as(Int16))
4080 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
4081 if value isa Int32 then return v.int32_instance(value.as(Int32))
4082 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
4083 # Should never happen
4084 abort
4085 end
4086 end
4087
4088 redef class AFloatExpr
4089 redef fun expr(v) do return v.float_instance(self.value.as(Float))
4090 end
4091
4092 redef class ACharExpr
4093 redef fun expr(v) do
4094 if is_code_point then
4095 return v.int_instance(value.as(not null).code_point)
4096 end
4097 return v.char_instance(self.value.as(not null))
4098 end
4099 end
4100
4101 redef class AArrayExpr
4102 redef fun expr(v)
4103 do
4104 var mtype = self.element_mtype.as(not null)
4105 var array = new Array[RuntimeVariable]
4106 var res = v.array_instance(array, mtype)
4107
4108 var old_comprehension = v.frame.comprehension
4109 v.frame.comprehension = res
4110 for nexpr in self.n_exprs do
4111 v.stmt(nexpr)
4112 end
4113 v.frame.comprehension = old_comprehension
4114
4115 return res
4116 end
4117 end
4118
4119 redef class AugmentedStringFormExpr
4120 # Factorize the making of a `Regex` object from a literal prefixed string
4121 protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
4122 var re = to_re
4123 assert re != null
4124 var res = v.compile_callsite(re, [rs])
4125 if res == null then
4126 print "Cannot call property `to_re` on {self}"
4127 abort
4128 end
4129 for i in suffix.chars do
4130 if i == 'i' then
4131 var ign = ignore_case
4132 assert ign != null
4133 v.compile_callsite(ign, [res, v.bool_instance(true)])
4134 continue
4135 end
4136 if i == 'm' then
4137 var nl = newline
4138 assert nl != null
4139 v.compile_callsite(nl, [res, v.bool_instance(true)])
4140 continue
4141 end
4142 if i == 'b' then
4143 var ext = extended
4144 assert ext != null
4145 v.compile_callsite(ext, [res, v.bool_instance(false)])
4146 continue
4147 end
4148 # Should not happen, this needs to be updated
4149 # along with the addition of new suffixes
4150 abort
4151 end
4152 return res
4153 end
4154 end
4155
4156 redef class AStringFormExpr
4157 redef fun expr(v) do return v.string_instance(value)
4158 end
4159
4160 redef class AStringExpr
4161 redef fun expr(v) do
4162 var s = v.string_instance(value)
4163 if is_string then return s
4164 if is_bytestring then
4165 var ns = v.c_string_instance(bytes.items, bytes.length)
4166 var ln = v.int_instance(bytes.length)
4167 var cs = to_bytes_with_copy
4168 assert cs != null
4169 var res = v.compile_callsite(cs, [ns, ln])
4170 assert res != null
4171 s = res
4172 else if is_re then
4173 var res = make_re(v, s)
4174 assert res != null
4175 s = res
4176 else
4177 print "Unimplemented prefix or suffix for {self}"
4178 abort
4179 end
4180 return s
4181 end
4182 end
4183
4184 redef class ASuperstringExpr
4185 redef fun expr(v)
4186 do
4187 var type_string = v.mmodule.string_type
4188
4189 # Collect elements of the superstring
4190 var array = new Array[AExpr]
4191 for ne in self.n_exprs do
4192 # Drop literal empty string.
4193 # They appears in things like "{a}" that is ["", a, ""]
4194 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
4195 array.add(ne)
4196 end
4197
4198 # Store the allocated native array in a static variable
4199 # For reusing later
4200 var varonce = v.get_name("varonce")
4201 v.add("if (unlikely({varonce}==NULL)) \{")
4202
4203 # The native array that will contains the elements to_s-ized.
4204 # For fast concatenation.
4205 var a = v.native_array_instance(type_string, v.int_instance(array.length))
4206
4207 v.add_decl("static {a.mtype.ctype} {varonce};")
4208
4209 # Pre-fill the array with the literal string parts.
4210 # So they do not need to be filled again when reused
4211 for i in [0..array.length[ do
4212 var ne = array[i]
4213 if not ne isa AStringFormExpr then continue
4214 var e = v.expr(ne, null)
4215 v.native_array_set(a, i, e)
4216 end
4217
4218 v.add("\} else \{")
4219 # Take the native-array from the store.
4220 # The point is to prevent that some recursive execution use (and corrupt) the same native array
4221 # WARNING: not thread safe! (FIXME?)
4222 v.add("{a} = {varonce};")
4223 v.add("{varonce} = NULL;")
4224 v.add("\}")
4225
4226 # Stringify the elements and put them in the native array
4227 var to_s_method = v.get_property("to_s", v.object_type)
4228 for i in [0..array.length[ do
4229 var ne = array[i]
4230 if ne isa AStringFormExpr then continue
4231 var e = v.expr(ne, null)
4232 # Skip the `to_s` if the element is already a String
4233 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
4234 e = v.send(to_s_method, [e]).as(not null)
4235 end
4236 v.native_array_set(a, i, e)
4237 end
4238
4239 # Fast join the C string to get the result
4240 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
4241 assert res != null
4242
4243 if is_re then res = make_re(v, res)
4244
4245 # We finish to work with the native array,
4246 # so store it so that it can be reused
4247 v.add("{varonce} = {a};")
4248
4249 return res
4250 end
4251 end
4252
4253 redef class ACrangeExpr
4254 redef fun expr(v)
4255 do
4256 var i1 = v.expr(self.n_expr, null)
4257 var i2 = v.expr(self.n_expr2, null)
4258 var mtype = self.mtype.as(MClassType)
4259 var res = v.init_instance(mtype)
4260 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
4261 return res
4262 end
4263 end
4264
4265 redef class AOrangeExpr
4266 redef fun expr(v)
4267 do
4268 var i1 = v.expr(self.n_expr, null)
4269 var i2 = v.expr(self.n_expr2, null)
4270 var mtype = self.mtype.as(MClassType)
4271 var res = v.init_instance(mtype)
4272 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
4273 return res
4274 end
4275 end
4276
4277 redef class ATrueExpr
4278 redef fun expr(v) do return v.bool_instance(true)
4279 end
4280
4281 redef class AFalseExpr
4282 redef fun expr(v) do return v.bool_instance(false)
4283 end
4284
4285 redef class ANullExpr
4286 redef fun expr(v) do return v.null_instance
4287 end
4288
4289 redef class AIsaExpr
4290 redef fun expr(v)
4291 do
4292 var i = v.expr(self.n_expr, null)
4293 var cast_type = self.cast_type
4294 if cast_type == null then return null # no-no on broken node
4295 return v.type_test(i, cast_type, "isa")
4296 end
4297 end
4298
4299 redef class AAsCastExpr
4300 redef fun expr(v)
4301 do
4302 var i = v.expr(self.n_expr, null)
4303 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
4304
4305 v.add_cast(i, self.mtype.as(not null), "as")
4306 return i
4307 end
4308 end
4309
4310 redef class AAsNotnullExpr
4311 redef fun expr(v)
4312 do
4313 var i = v.expr(self.n_expr, null)
4314 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
4315
4316 if not v.maybe_null(i) then return i
4317
4318 v.add("if (unlikely({i} == NULL)) \{")
4319 v.add_abort("Cast failed")
4320 v.add("\}")
4321 return i
4322 end
4323 end
4324
4325 redef class AParExpr
4326 redef fun expr(v) do return v.expr(self.n_expr, null)
4327 end
4328
4329 redef class AOnceExpr
4330 redef fun expr(v)
4331 do
4332 var mtype = self.mtype.as(not null)
4333 var name = v.get_name("varonce")
4334 var guard = v.get_name(name + "_guard")
4335 v.add_decl("static {mtype.ctype} {name};")
4336 v.add_decl("static int {guard};")
4337 var res = v.new_var(mtype)
4338 v.add("if (likely({guard})) \{")
4339 v.add("{res} = {name};")
4340 v.add("\} else \{")
4341 var i = v.expr(self.n_expr, mtype)
4342 v.add("{res} = {i};")
4343 v.add("{name} = {res};")
4344 v.add("{guard} = 1;")
4345 v.add("\}")
4346 return res
4347 end
4348 end
4349
4350 redef class ASendExpr
4351 redef fun expr(v)
4352 do
4353 var recv = v.expr(self.n_expr, null)
4354 if is_safe then
4355 v.add "if ({recv}!=NULL) \{"
4356 end
4357 var callsite = self.callsite.as(not null)
4358 if callsite.is_broken then return null
4359 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
4360 var res = v.compile_callsite(callsite, args)
4361 if is_safe then
4362 if res != null then
4363 var orig_res = res
4364 res = v.new_var(self.mtype.as(not null))
4365 v.add("{res} = {orig_res};")
4366 v.add("\} else \{")
4367 v.add("{res} = NULL;")
4368 end
4369 v.add("\}")
4370 end
4371 return res
4372 end
4373 end
4374
4375 redef class ACallrefExpr
4376 redef fun expr(v)
4377 do
4378 v.add_abort("NOT YET IMPLEMENTED callref expressions.")
4379 return null
4380 end
4381 end
4382
4383 redef class ASendReassignFormExpr
4384 redef fun stmt(v)
4385 do
4386 var recv = v.expr(self.n_expr, null)
4387 var callsite = self.callsite.as(not null)
4388 if callsite.is_broken then return
4389 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
4390
4391 var value = v.expr(self.n_value, null)
4392
4393 var left = v.compile_callsite(callsite, args)
4394 assert left != null
4395
4396 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
4397 assert res != null
4398
4399 args.add(res)
4400 v.compile_callsite(self.write_callsite.as(not null), args)
4401 end
4402 end
4403
4404 redef class ASuperExpr
4405 redef fun expr(v)
4406 do
4407 var frame = v.frame.as(not null)
4408 var recv = frame.arguments.first
4409
4410 var callsite = self.callsite
4411 if callsite != null then
4412 if callsite.is_broken then return null
4413 var args
4414
4415 if self.n_args.n_exprs.is_empty then
4416 # Add automatic arguments for the super init call
4417 args = [recv]
4418 for i in [0..callsite.msignature.arity[ do
4419 args.add(frame.arguments[i+1])
4420 end
4421 else
4422 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
4423 end
4424
4425 # Super init call
4426 var res = v.compile_callsite(callsite, args)
4427 return res
4428 end
4429
4430 var mpropdef = self.mpropdef.as(not null)
4431
4432 var args
4433 if self.n_args.n_exprs.is_empty then
4434 args = frame.arguments
4435 else
4436 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
4437 end
4438
4439 # Standard call-next-method
4440 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
4441 end
4442 end
4443
4444 redef class ANewExpr
4445 redef fun expr(v)
4446 do
4447 var mtype = self.recvtype
4448 assert mtype != null
4449
4450 if mtype.mclass.name == "NativeArray" then
4451 assert self.n_args.n_exprs.length == 1
4452 var l = v.expr(self.n_args.n_exprs.first, null)
4453 assert mtype isa MGenericType
4454 var elttype = mtype.arguments.first
4455 return v.native_array_instance(elttype, l)
4456 end
4457
4458 var callsite = self.callsite
4459 if callsite == null then return v.init_instance_or_extern(mtype)
4460 if callsite.is_broken then return null
4461
4462 var recv
4463 # new factories are badly implemented.
4464 # They assume a stub temporary receiver exists.
4465 # This temporary receiver is required because it
4466 # currently holds the method and the formal types.
4467 #
4468 # However, this object could be reused if the formal types are the same.
4469 # Therefore, the following code will `once` it in these case
4470 if callsite.mproperty.is_new and not mtype.need_anchor then
4471 var name = v.get_name("varoncenew")
4472 var guard = v.get_name(name + "_guard")
4473 v.add_decl("static {mtype.ctype} {name};")
4474 v.add_decl("static int {guard};")
4475 recv = v.new_var(mtype)
4476 v.add("if (likely({guard})) \{")
4477 v.add("{recv} = {name};")
4478 v.add("\} else \{")
4479 var i = v.init_instance_or_extern(mtype)
4480 v.add("{recv} = {i};")
4481 v.add("{name} = {recv};")
4482 v.add("{guard} = 1;")
4483 v.add("\}")
4484 else
4485 recv = v.init_instance_or_extern(mtype)
4486 end
4487
4488 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
4489 var res2 = v.compile_callsite(callsite, args)
4490 if res2 != null then
4491 #self.debug("got {res2} from {mproperty}. drop {recv}")
4492 return res2
4493 end
4494 return recv
4495 end
4496 end
4497
4498 redef class AAttrExpr
4499 redef fun expr(v)
4500 do
4501 var recv = v.expr(self.n_expr, null)
4502 var mproperty = self.mproperty.as(not null)
4503 return v.read_attribute(mproperty, recv)
4504 end
4505 end
4506
4507 redef class AAttrAssignExpr
4508 redef fun expr(v)
4509 do
4510 var recv = v.expr(self.n_expr, null)
4511 var i = v.expr(self.n_value, null)
4512 var mproperty = self.mproperty.as(not null)
4513 v.write_attribute(mproperty, recv, i)
4514 return i
4515 end
4516 end
4517
4518 redef class AAttrReassignExpr
4519 redef fun stmt(v)
4520 do
4521 var recv = v.expr(self.n_expr, null)
4522 var value = v.expr(self.n_value, null)
4523 var mproperty = self.mproperty.as(not null)
4524 var attr = v.read_attribute(mproperty, recv)
4525 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
4526 assert res != null
4527 v.write_attribute(mproperty, recv, res)
4528 end
4529 end
4530
4531 redef class AIssetAttrExpr
4532 redef fun expr(v)
4533 do
4534 var recv = v.expr(self.n_expr, null)
4535 var mproperty = self.mproperty.as(not null)
4536 return v.isset_attribute(mproperty, recv)
4537 end
4538 end
4539
4540 redef class AVarargExpr
4541 redef fun expr(v)
4542 do
4543 return v.expr(self.n_expr, null)
4544 end
4545 end
4546
4547 redef class ASafeExpr
4548 redef fun expr(v)
4549 do
4550 return v.expr(self.n_expr, null)
4551 end
4552 end
4553
4554 redef class ANamedargExpr
4555 redef fun expr(v)
4556 do
4557 return v.expr(self.n_expr, null)
4558 end
4559 end
4560
4561 redef class ADebugTypeExpr
4562 redef fun stmt(v)
4563 do
4564 # do nothing
4565 end
4566 end
4567
4568 # Utils
4569
4570 redef class Array[E]
4571 # Return a new `Array` with the elements only contened in self and not in `o`
4572 fun -(o: Array[E]): Array[E] do
4573 var res = new Array[E]
4574 for e in self do if not o.has(e) then res.add(e)
4575 return res
4576 end
4577 end
4578
4579 redef class MModule
4580 # All `MProperty` associated to all `MClassDef` of `mclass`
4581 fun properties(mclass: MClass): Set[MProperty] do
4582 if not self.properties_cache.has_key(mclass) then
4583 var properties = new HashSet[MProperty]
4584 var parents = new Array[MClass]
4585 if self.flatten_mclass_hierarchy.has(mclass) then
4586 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
4587 end
4588 for parent in parents do
4589 properties.add_all(self.properties(parent))
4590 end
4591 for mclassdef in mclass.mclassdefs do
4592 if not self.in_importation <= mclassdef.mmodule then continue
4593 for mprop in mclassdef.intro_mproperties do
4594 properties.add(mprop)
4595 end
4596 end
4597 self.properties_cache[mclass] = properties
4598 end
4599 return properties_cache[mclass]
4600 end
4601 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
4602
4603 # Write FFI and nitni results to file
4604 fun finalize_ffi(c: AbstractCompiler) do end
4605
4606 # Give requided addinional system libraries (as given to LD_LIBS)
4607 # Note: can return null instead of an empty set
4608 fun collect_linker_libs: nullable Array[String] do return null
4609 end
4610
4611 # Create a tool context to handle options and paths
4612 var toolcontext = new ToolContext
4613
4614 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
4615
4616 # We do not add other options, so process them now!
4617 toolcontext.process_options(args)
4618
4619 # We need a model to collect stufs
4620 var model = new Model
4621 # An a model builder to parse files
4622 var modelbuilder = new ModelBuilder(model, toolcontext)
4623
4624 var arguments = toolcontext.option_context.rest
4625 if toolcontext.opt_run.value then
4626 # When --run, only the first is the program, the rest is the run arguments
4627 arguments = [toolcontext.option_context.rest.shift]
4628 end
4629 if arguments.length > 1 and toolcontext.opt_output.value != null then
4630 print "Option Error: --output needs a single source file. Do you prefer --dir?"
4631 exit 1
4632 end
4633
4634 # Here we load an process all modules passed on the command line
4635 var mmodules = modelbuilder.parse(arguments)
4636
4637 if mmodules.is_empty then toolcontext.quit
4638
4639 modelbuilder.run_phases
4640
4641 for mmodule in mmodules do
4642 toolcontext.info("*** PROCESS {mmodule} ***", 1)
4643 var ms = [mmodule]
4644 toolcontext.run_global_phases(ms)
4645 end