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