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