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