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