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