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