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