Merge: iOS support
[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
27 # Add compiling options
28 redef class ToolContext
29 # --output
30 var opt_output = new OptionString("Output file", "-o", "--output")
31 # --dir
32 var opt_dir = new OptionString("Output directory", "--dir")
33 # --no-cc
34 var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
35 # --no-main
36 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
37 # --make-flags
38 var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
39 # --max-c-lines
40 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
41 # --group-c-files
42 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
43 # --compile-dir
44 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
45 # --hardening
46 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
47 # --no-check-covariance
48 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
49 # --no-check-attr-isset
50 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
51 # --no-check-assert
52 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
53 # --no-check-autocast
54 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
55 # --no-check-null
56 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
57 # --no-check-all
58 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
59 # --typing-test-metrics
60 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
61 # --invocation-metrics
62 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
63 # --isset-checks-metrics
64 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
65 # --stacktrace
66 var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
67 # --no-gcc-directives
68 var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
69 # --release
70 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
71
72 redef init
73 do
74 super
75 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)
76 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)
77 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
78 self.option_context.add_option(self.opt_stacktrace)
79 self.option_context.add_option(self.opt_no_gcc_directive)
80 self.option_context.add_option(self.opt_release)
81 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
82
83 opt_no_main.hidden = true
84 end
85
86 redef fun process_options(args)
87 do
88 super
89
90 var st = opt_stacktrace.value
91 if st == "none" or st == "libunwind" or st == "nitstack" then
92 # Fine, do nothing
93 else if st == "auto" or st == null then
94 # Default is nitstack
95 opt_stacktrace.value = "nitstack"
96 else
97 print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
98 exit(1)
99 end
100
101 if opt_output.value != null and opt_dir.value != null then
102 print "Error: cannot use both --dir and --output"
103 exit(1)
104 end
105
106 if opt_no_check_all.value then
107 opt_no_check_covariance.value = true
108 opt_no_check_attr_isset.value = true
109 opt_no_check_assert.value = true
110 opt_no_check_autocast.value = true
111 opt_no_check_null.value = true
112 end
113 end
114 end
115
116 redef class ModelBuilder
117 # The compilation directory
118 var compile_dir: String
119
120 # Simple indirection to `Toolchain::write_and_make`
121 protected fun write_and_make(compiler: AbstractCompiler)
122 do
123 var platform = compiler.target_platform
124 var toolchain = platform.toolchain(toolcontext, compiler)
125 compile_dir = toolchain.compile_dir
126 toolchain.write_and_make
127 end
128 end
129
130 redef class Platform
131 # The specific tool-chain associated to the platform
132 fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
133 do
134 return new MakefileToolchain(toolcontext, compiler)
135 end
136 end
137
138 # Build toolchain for a specific target program, varies per `Platform`
139 class Toolchain
140
141 # Toolcontext
142 var toolcontext: ToolContext
143
144 # Compiler of the target program
145 var compiler: AbstractCompiler
146
147 # Directory where to generate all C files
148 fun compile_dir: String
149 do
150 var compile_dir = toolcontext.opt_compile_dir.value
151 if compile_dir == null then compile_dir = ".nit_compile"
152 return compile_dir
153 end
154
155 # Write all C files and compile them
156 fun write_and_make is abstract
157 end
158
159 # Default toolchain using a Makefile
160 class MakefileToolchain
161 super Toolchain
162
163 redef fun write_and_make
164 do
165 var compile_dir = compile_dir
166
167 # Generate the .h and .c files
168 # A single C file regroups many compiled rumtime functions
169 # 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
170 var time0 = get_time
171 self.toolcontext.info("*** WRITING C ***", 1)
172
173 compile_dir.mkdir
174
175 var cfiles = new Array[String]
176 write_files(compile_dir, cfiles)
177
178 # Generate the Makefile
179
180 write_makefile(compile_dir, cfiles)
181
182 var time1 = get_time
183 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
184
185 # Execute the Makefile
186
187 if self.toolcontext.opt_no_cc.value then return
188
189 time0 = time1
190 self.toolcontext.info("*** COMPILING C ***", 1)
191
192 compile_c_code(compile_dir)
193
194 time1 = get_time
195 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
196 end
197
198 # Write all source files to the `compile_dir`
199 fun write_files(compile_dir: String, cfiles: Array[String])
200 do
201 var platform = compiler.target_platform
202 if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
203 var cc_opt_with_libgc = "-DWITH_LIBGC"
204 if not platform.supports_libgc then cc_opt_with_libgc = ""
205
206 # Add gc_choser.h to aditionnal bodies
207 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
208 if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
209 compiler.extern_bodies.add(gc_chooser)
210 var clib = toolcontext.nit_dir / "clib"
211 compiler.files_to_copy.add "{clib}/gc_chooser.c"
212 compiler.files_to_copy.add "{clib}/gc_chooser.h"
213
214 # FFI
215 for m in compiler.mainmodule.in_importation.greaters do
216 compiler.finalize_ffi_for_module(m)
217 end
218
219 # Copy original .[ch] files to compile_dir
220 for src in compiler.files_to_copy do
221 var basename = src.basename("")
222 var dst = "{compile_dir}/{basename}"
223 src.file_copy_to dst
224 end
225
226 var hfilename = compiler.header.file.name + ".h"
227 var hfilepath = "{compile_dir}/{hfilename}"
228 var h = new FileWriter.open(hfilepath)
229 for l in compiler.header.decl_lines do
230 h.write l
231 h.write "\n"
232 end
233 for l in compiler.header.lines do
234 h.write l
235 h.write "\n"
236 end
237 h.close
238
239 var max_c_lines = toolcontext.opt_max_c_lines.value
240 for f in compiler.files do
241 var i = 0
242 var count = 0
243 var file: nullable FileWriter = null
244 for vis in f.writers do
245 if vis == compiler.header then continue
246 var total_lines = vis.lines.length + vis.decl_lines.length
247 if total_lines == 0 then continue
248 count += total_lines
249 if file == null or (count > max_c_lines and max_c_lines > 0) then
250 i += 1
251 if file != null then file.close
252 var cfilename = "{f.name}.{i}.c"
253 var cfilepath = "{compile_dir}/{cfilename}"
254 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
255 cfiles.add(cfilename)
256 file = new FileWriter.open(cfilepath)
257 file.write "#include \"{f.name}.0.h\"\n"
258 count = total_lines
259 end
260 for l in vis.decl_lines do
261 file.write l
262 file.write "\n"
263 end
264 for l in vis.lines do
265 file.write l
266 file.write "\n"
267 end
268 end
269 if file == null then continue
270 file.close
271
272 var cfilename = "{f.name}.0.h"
273 var cfilepath = "{compile_dir}/{cfilename}"
274 var hfile: nullable FileWriter = null
275 hfile = new FileWriter.open(cfilepath)
276 hfile.write "#include \"{hfilename}\"\n"
277 for key in f.required_declarations do
278 if not compiler.provided_declarations.has_key(key) then
279 var node = compiler.requirers_of_declarations.get_or_null(key)
280 if node != null then
281 node.debug "No provided declaration for {key}"
282 else
283 print "No provided declaration for {key}"
284 end
285 abort
286 end
287 hfile.write compiler.provided_declarations[key]
288 hfile.write "\n"
289 end
290 hfile.close
291 end
292
293 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
294 end
295
296 # Get the name of the Makefile to use
297 fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
298
299 # Get the default name of the executable to produce
300 fun default_outname: String
301 do
302 var mainmodule = compiler.mainmodule
303
304 # Search a non fictive module
305 var res = mainmodule.name
306 while mainmodule.is_fictive do
307 mainmodule = mainmodule.in_importation.direct_greaters.first
308 res = mainmodule.name
309 end
310 return res
311 end
312
313 # Combine options and platform informations to get the final path of the outfile
314 fun outfile(mainmodule: MModule): String
315 do
316 var res = self.toolcontext.opt_output.value
317 if res != null then return res
318 res = default_outname
319 var dir = self.toolcontext.opt_dir.value
320 if dir != null then return dir.join_path(res)
321 return res
322 end
323
324 # Write the Makefile
325 fun write_makefile(compile_dir: String, cfiles: Array[String])
326 do
327 var mainmodule = compiler.mainmodule
328 var platform = compiler.target_platform
329
330 var outname = outfile(mainmodule)
331
332 var real_outpath = compile_dir.relpath(outname)
333 var outpath = real_outpath.escape_to_mk
334 if outpath != real_outpath then
335 # If the name is crazy and need escaping, we will do an indirection
336 # 1. generate the binary in the .nit_compile dir under an escaped name
337 # 2. copy the binary at the right place in the `all` goal.
338 outpath = mainmodule.c_name
339 end
340 var makename = makefile_name
341 var makepath = "{compile_dir}/{makename}"
342 var makefile = new FileWriter.open(makepath)
343
344 var linker_options = new HashSet[String]
345 for m in mainmodule.in_importation.greaters do
346 var libs = m.collect_linker_libs
347 if libs != null then linker_options.add_all(libs)
348 end
349
350 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
351
352 var ost = toolcontext.opt_stacktrace.value
353 if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n")
354
355 # Dynamic adaptations
356 # While `platform` enable complex toolchains, they are statically applied
357 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
358
359 # Check and adapt the targeted system
360 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
361 makefile.write("ifeq ($(uname_S),Darwin)\n")
362 # remove -lunwind since it is already included on macosx
363 makefile.write("\tNEED_LIBUNWIND :=\n")
364 makefile.write("endif\n\n")
365
366 # Check and adapt for the compiler used
367 # clang need an additionnal `-Qunused-arguments`
368 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")
369
370 makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
371
372 makefile.write("all: {outpath}\n")
373 if outpath != real_outpath then
374 makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
375 end
376 makefile.write("\n")
377
378 var ofiles = new Array[String]
379 var dep_rules = new Array[String]
380 # Compile each generated file
381 for f in cfiles do
382 var o = f.strip_extension(".c") + ".o"
383 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
384 ofiles.add(o)
385 dep_rules.add(o)
386 end
387
388 # Generate linker script, if any
389 if not compiler.linker_script.is_empty then
390 var linker_script_path = "{compile_dir}/linker_script"
391 ofiles.add "linker_script"
392 var f = new FileWriter.open(linker_script_path)
393 for l in compiler.linker_script do
394 f.write l
395 f.write "\n"
396 end
397 f.close
398 end
399
400 var java_files = new Array[ExternFile]
401
402 var pkgconfigs = new Array[String]
403 for f in compiler.extern_bodies do
404 pkgconfigs.add_all f.pkgconfigs
405 end
406 # Protect pkg-config
407 if not pkgconfigs.is_empty then
408 makefile.write """
409 # does pkg-config exists?
410 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
411 $(error "Command `pkg-config` not found. Please install it")
412 endif
413 """
414 for p in pkgconfigs do
415 makefile.write """
416 # Check for library {{{p}}}
417 ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
418 $(error "pkg-config: package {{{p}}} is not found.")
419 endif
420 """
421 end
422 end
423
424 # Compile each required extern body into a specific .o
425 for f in compiler.extern_bodies do
426 var o = f.makefile_rule_name
427 var ff = f.filename.basename("")
428 makefile.write("{o}: {ff}\n")
429 makefile.write("\t{f.makefile_rule_content}\n\n")
430 dep_rules.add(f.makefile_rule_name)
431
432 if f.compiles_to_o_file then ofiles.add(o)
433 if f.add_to_jar then java_files.add(f)
434 end
435
436 if not java_files.is_empty then
437 var jar_file = "{outpath}.jar"
438
439 var class_files_array = new Array[String]
440 for f in java_files do class_files_array.add(f.makefile_rule_name)
441 var class_files = class_files_array.join(" ")
442
443 makefile.write("{jar_file}: {class_files}\n")
444 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
445 dep_rules.add jar_file
446 end
447
448 # Link edition
449 var pkg = ""
450 if not pkgconfigs.is_empty then
451 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
452 end
453 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
454 # Clean
455 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
456 if outpath != real_outpath then
457 makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
458 end
459 makefile.close
460 self.toolcontext.info("Generated makefile: {makepath}", 2)
461
462 makepath.file_copy_to "{compile_dir}/Makefile"
463 end
464
465 # The C code is generated, compile it to an executable
466 fun compile_c_code(compile_dir: String)
467 do
468 var makename = makefile_name
469
470 var makeflags = self.toolcontext.opt_make_flags.value
471 if makeflags == null then makeflags = ""
472 self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
473
474 var res
475 if self.toolcontext.verbose_level >= 3 then
476 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
477 else
478 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
479 end
480 if res != 0 then
481 toolcontext.error(null, "make failed! Error code: {res}.")
482 end
483 end
484 end
485
486 # Singleton that store the knowledge about the compilation process
487 abstract class AbstractCompiler
488 type VISITOR: AbstractCompilerVisitor
489
490 # Table corresponding c_names to nit names (methods)
491 var names = new HashMap[String, String]
492
493 # The main module of the program currently compiled
494 # Is assigned during the separate compilation
495 var mainmodule: MModule is writable
496
497 # The real main module of the program
498 var realmainmodule: MModule is noinit
499
500 # The modelbuilder used to know the model and the AST
501 var modelbuilder: ModelBuilder is protected writable
502
503 # Is hardening asked? (see --hardening)
504 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
505
506 # The targeted specific platform
507 var target_platform: Platform is noinit
508
509 init
510 do
511 self.realmainmodule = mainmodule
512 target_platform = mainmodule.target_platform or else new Platform
513 end
514
515 # Do the full code generation of the program `mainmodule`
516 # It is the main method usually called after the instantiation
517 fun do_compilation is abstract
518
519 # Force the creation of a new file
520 # The point is to avoid contamination between must-be-compiled-separately files
521 fun new_file(name: String): CodeFile
522 do
523 if modelbuilder.toolcontext.opt_group_c_files.value then
524 if self.files.is_empty then
525 var f = new CodeFile(mainmodule.c_name)
526 self.files.add(f)
527 end
528 return self.files.first
529 end
530 var f = new CodeFile(name)
531 self.files.add(f)
532 return f
533 end
534
535 # The list of all associated files
536 # Used to generate .c files
537 var files = new List[CodeFile]
538
539 # Initialize a visitor specific for a compiler engine
540 fun new_visitor: VISITOR is abstract
541
542 # Where global declaration are stored (the main .h)
543 var header: CodeWriter is writable, noinit
544
545 # Additionnal linker script for `ld`.
546 # Mainly used to do specific link-time symbol resolution
547 var linker_script = new Array[String]
548
549 # Provide a declaration that can be requested (before or latter) by a visitor
550 fun provide_declaration(key: String, s: String)
551 do
552 if self.provided_declarations.has_key(key) then
553 assert self.provided_declarations[key] == s
554 end
555 self.provided_declarations[key] = s
556 end
557
558 private var provided_declarations = new HashMap[String, String]
559
560 private var requirers_of_declarations = new HashMap[String, ANode]
561
562 # Builds the .c and .h files to be used when generating a Stack Trace
563 # Binds the generated C function names to Nit function names
564 fun build_c_to_nit_bindings
565 do
566 var compile_dir = modelbuilder.compile_dir
567
568 var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
569 stream.write("#include <string.h>\n")
570 stream.write("#include <stdlib.h>\n")
571 stream.write("#include \"c_functions_hash.h\"\n")
572 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
573 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
574 stream.write("char* procname = malloc(len+1);")
575 stream.write("memcpy(procname, procproc, len);")
576 stream.write("procname[len] = '\\0';")
577 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
578 for i in names.keys do
579 stream.write("\{\"")
580 stream.write(i.escape_to_c)
581 stream.write("\",\"")
582 stream.write(names[i].escape_to_c)
583 stream.write("\"\},\n")
584 end
585 stream.write("\};\n")
586 stream.write("int i;")
587 stream.write("for(i = 0; i < {names.length}; i++)\{")
588 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
589 stream.write("free(procname);")
590 stream.write("return map[i].nit_name;")
591 stream.write("\}")
592 stream.write("\}")
593 stream.write("free(procname);")
594 stream.write("return NULL;")
595 stream.write("\}\n")
596 stream.close
597
598 stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
599 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
600 stream.close
601
602 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
603 end
604
605 # Compile C headers
606 # This method call compile_header_strucs method that has to be refined
607 fun compile_header do
608 self.header.add_decl("#include <stdlib.h>")
609 self.header.add_decl("#include <stdio.h>")
610 self.header.add_decl("#include <string.h>")
611 self.header.add_decl("#include <sys/types.h>\n")
612 self.header.add_decl("#include <unistd.h>\n")
613 self.header.add_decl("#include \"gc_chooser.h\"")
614 self.header.add_decl("#ifdef ANDROID")
615 self.header.add_decl(" #include <android/log.h>")
616 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
617 self.header.add_decl("#else")
618 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
619 self.header.add_decl("#endif")
620
621 compile_header_structs
622 compile_nitni_structs
623
624 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
625 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
626 # Signal handler function prototype
627 self.header.add_decl("void fatal_exit(int);")
628 else
629 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
630 end
631
632 if gccd_disable.has("likely") or gccd_disable.has("all") then
633 self.header.add_decl("#define likely(x) (x)")
634 self.header.add_decl("#define unlikely(x) (x)")
635 else if gccd_disable.has("correct-likely") then
636 # invert the `likely` definition
637 # Used by masochists to bench the worst case
638 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
639 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
640 else
641 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
642 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
643 end
644
645 # Global variable used by intern methods
646 self.header.add_decl("extern int glob_argc;")
647 self.header.add_decl("extern char **glob_argv;")
648 self.header.add_decl("extern val *glob_sys;")
649 end
650
651 # Declaration of structures for live Nit types
652 protected fun compile_header_structs is abstract
653
654 # Declaration of structures for nitni undelying the FFI
655 protected fun compile_nitni_structs
656 do
657 self.header.add_decl """
658 /* Native reference to Nit objects */
659 /* This structure is used to represent every Nit type in extern methods and custom C code. */
660 struct nitni_ref {
661 struct nitni_ref *next,
662 *prev; /* adjacent global references in global list */
663 int count; /* number of time this global reference has been marked */
664 };
665
666 /* List of global references from C code to Nit objects */
667 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
668 struct nitni_global_ref_list_t {
669 struct nitni_ref *head, *tail;
670 };
671 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
672
673 /* Initializer of global reference list */
674 extern void nitni_global_ref_list_init();
675
676 /* Intern function to add a global reference to the list */
677 extern void nitni_global_ref_add( struct nitni_ref *ref );
678
679 /* Intern function to remove a global reference from the list */
680 extern void nitni_global_ref_remove( struct nitni_ref *ref );
681
682 /* Increase count on an existing global reference */
683 extern void nitni_global_ref_incr( struct nitni_ref *ref );
684
685 /* Decrease count on an existing global reference */
686 extern void nitni_global_ref_decr( struct nitni_ref *ref );
687 """
688 end
689
690 fun compile_finalizer_function
691 do
692 var finalizable_type = mainmodule.finalizable_type
693 if finalizable_type == null then return
694
695 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
696
697 if finalize_meth == null then
698 modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
699 return
700 end
701
702 var v = self.new_visitor
703 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
704 var recv = v.new_expr("obj", finalizable_type)
705 v.send(finalize_meth, [recv])
706 v.add "\}"
707 end
708
709 # Generate the main C function.
710 #
711 # This function:
712 #
713 # * allocate the Sys object if it exists
714 # * call init if is exists
715 # * call main if it exists
716 fun compile_main_function
717 do
718 var v = self.new_visitor
719 v.add_decl("#include <signal.h>")
720 var ost = modelbuilder.toolcontext.opt_stacktrace.value
721 var platform = target_platform
722
723 if not platform.supports_libunwind then ost = "none"
724
725 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
726
727 if ost == "nitstack" or ost == "libunwind" then
728 v.add_decl("#define UNW_LOCAL_ONLY")
729 v.add_decl("#include <libunwind.h>")
730 if ost == "nitstack" then
731 v.add_decl("#include \"c_functions_hash.h\"")
732 end
733 end
734 v.add_decl("int glob_argc;")
735 v.add_decl("char **glob_argv;")
736 v.add_decl("val *glob_sys;")
737
738 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
739 for tag in count_type_test_tags do
740 v.add_decl("long count_type_test_resolved_{tag};")
741 v.add_decl("long count_type_test_unresolved_{tag};")
742 v.add_decl("long count_type_test_skipped_{tag};")
743 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
744 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
745 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
746 end
747 end
748
749 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
750 v.add_decl("long count_invoke_by_tables;")
751 v.add_decl("long count_invoke_by_direct;")
752 v.add_decl("long count_invoke_by_inline;")
753 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
754 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
755 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
756 end
757
758 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
759 v.add_decl("long count_attr_reads = 0;")
760 v.add_decl("long count_isset_checks = 0;")
761 v.compiler.header.add_decl("extern long count_attr_reads;")
762 v.compiler.header.add_decl("extern long count_isset_checks;")
763 end
764
765 v.add_decl("static void show_backtrace(void) \{")
766 if ost == "nitstack" or ost == "libunwind" then
767 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
768 v.add_decl("unw_cursor_t cursor;")
769 v.add_decl("if(opt==NULL)\{")
770 v.add_decl("unw_context_t uc;")
771 v.add_decl("unw_word_t ip;")
772 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
773 v.add_decl("unw_getcontext(&uc);")
774 v.add_decl("unw_init_local(&cursor, &uc);")
775 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
776 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
777 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
778 v.add_decl("while (unw_step(&cursor) > 0) \{")
779 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
780 if ost == "nitstack" then
781 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
782 v.add_decl(" if (recv != NULL)\{")
783 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
784 v.add_decl(" \}else\{")
785 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
786 v.add_decl(" \}")
787 else
788 v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
789 end
790 v.add_decl("\}")
791 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
792 v.add_decl("free(procname);")
793 v.add_decl("\}")
794 end
795 v.add_decl("\}")
796
797 v.add_decl("void sig_handler(int signo)\{")
798 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
799 v.add_decl("show_backtrace();")
800 # rethrows
801 v.add_decl("signal(signo, SIG_DFL);")
802 v.add_decl("kill(getpid(), signo);")
803 v.add_decl("\}")
804
805 v.add_decl("void fatal_exit(int status) \{")
806 v.add_decl("show_backtrace();")
807 v.add_decl("exit(status);")
808 v.add_decl("\}")
809
810 if no_main then
811 v.add_decl("int nit_main(int argc, char** argv) \{")
812 else
813 v.add_decl("int main(int argc, char** argv) \{")
814 end
815
816 v.add("signal(SIGABRT, sig_handler);")
817 v.add("signal(SIGFPE, sig_handler);")
818 v.add("signal(SIGILL, sig_handler);")
819 v.add("signal(SIGINT, sig_handler);")
820 v.add("signal(SIGTERM, sig_handler);")
821 v.add("signal(SIGSEGV, sig_handler);")
822 v.add("signal(SIGPIPE, sig_handler);")
823
824 v.add("glob_argc = argc; glob_argv = argv;")
825 v.add("initialize_gc_option();")
826
827 v.add "initialize_nitni_global_refs();"
828
829 var main_type = mainmodule.sys_type
830 if main_type != null then
831 var mainmodule = v.compiler.mainmodule
832 var glob_sys = v.init_instance(main_type)
833 v.add("glob_sys = {glob_sys};")
834 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
835 if main_init != null then
836 v.send(main_init, [glob_sys])
837 end
838 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
839 mainmodule.try_get_primitive_method("main", main_type.mclass)
840 if main_method != null then
841 v.send(main_method, [glob_sys])
842 end
843 end
844
845 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
846 v.add_decl("long count_type_test_resolved_total = 0;")
847 v.add_decl("long count_type_test_unresolved_total = 0;")
848 v.add_decl("long count_type_test_skipped_total = 0;")
849 v.add_decl("long count_type_test_total_total = 0;")
850 for tag in count_type_test_tags do
851 v.add_decl("long count_type_test_total_{tag};")
852 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
853 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
854 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
855 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
856 v.add("count_type_test_total_total += count_type_test_total_{tag};")
857 end
858 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
859 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
860 var tags = count_type_test_tags.to_a
861 tags.add("total")
862 for tag in tags do
863 v.add("printf(\"{tag}\");")
864 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
865 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
866 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
867 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
868 end
869 end
870
871 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
872 v.add_decl("long count_invoke_total;")
873 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
874 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
875 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
876 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
877 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
878 end
879
880 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
881 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
882 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
883 end
884
885 v.add("return 0;")
886 v.add("\}")
887
888 for m in mainmodule.in_importation.greaters do
889 var f = "FILE_"+m.c_name
890 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
891 provide_declaration(f, "extern const char {f}[];")
892 end
893 end
894
895 # Copile all C functions related to the [incr|decr]_ref features of the FFI
896 fun compile_nitni_global_ref_functions
897 do
898 var v = self.new_visitor
899 v.add """
900 struct nitni_global_ref_list_t *nitni_global_ref_list;
901 void initialize_nitni_global_refs() {
902 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
903 nitni_global_ref_list->head = NULL;
904 nitni_global_ref_list->tail = NULL;
905 }
906
907 void nitni_global_ref_add( struct nitni_ref *ref ) {
908 if ( nitni_global_ref_list->head == NULL ) {
909 nitni_global_ref_list->head = ref;
910 ref->prev = NULL;
911 } else {
912 nitni_global_ref_list->tail->next = ref;
913 ref->prev = nitni_global_ref_list->tail;
914 }
915 nitni_global_ref_list->tail = ref;
916
917 ref->next = NULL;
918 }
919
920 void nitni_global_ref_remove( struct nitni_ref *ref ) {
921 if ( ref->prev == NULL ) {
922 nitni_global_ref_list->head = ref->next;
923 } else {
924 ref->prev->next = ref->next;
925 }
926
927 if ( ref->next == NULL ) {
928 nitni_global_ref_list->tail = ref->prev;
929 } else {
930 ref->next->prev = ref->prev;
931 }
932 }
933
934 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
935 if ( ref->count == 0 ) /* not registered */
936 {
937 /* add to list */
938 nitni_global_ref_add( ref );
939 }
940
941 ref->count ++;
942 }
943
944 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
945 if ( ref->count == 1 ) /* was last reference */
946 {
947 /* remove from list */
948 nitni_global_ref_remove( ref );
949 }
950
951 ref->count --;
952 }
953 """
954 end
955
956 # List of additional files required to compile (FFI)
957 var extern_bodies = new Array[ExternFile]
958
959 # List of source files to copy over to the compile dir
960 var files_to_copy = new Array[String]
961
962 # This is used to avoid adding an extern file more than once
963 private var seen_extern = new ArraySet[String]
964
965 # Generate code that initialize the attributes on a new instance
966 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
967 do
968 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
969 self.mainmodule.linearize_mclassdefs(cds)
970 for cd in cds do
971 for npropdef in modelbuilder.collect_attr_propdef(cd) do
972 npropdef.init_expr(v, recv)
973 end
974 end
975 end
976
977 # Generate code that check if an attribute is correctly initialized
978 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
979 do
980 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
981 self.mainmodule.linearize_mclassdefs(cds)
982 for cd in cds do
983 for npropdef in modelbuilder.collect_attr_propdef(cd) do
984 npropdef.check_expr(v, recv)
985 end
986 end
987 end
988
989 # stats
990
991 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
992 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
993 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
994 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
995
996 protected fun init_count_type_test_tags: HashMap[String, Int]
997 do
998 var res = new HashMap[String, Int]
999 for tag in count_type_test_tags do
1000 res[tag] = 0
1001 end
1002 return res
1003 end
1004
1005 # Display stats about compilation process
1006 #
1007 # Metrics used:
1008 #
1009 # * type tests against resolved types (`x isa Collection[Animal]`)
1010 # * type tests against unresolved types (`x isa Collection[E]`)
1011 # * type tests skipped
1012 # * type tests total
1013 fun display_stats
1014 do
1015 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1016 print "# static count_type_test"
1017 print "\tresolved:\tunresolved\tskipped\ttotal"
1018 var count_type_test_total = init_count_type_test_tags
1019 count_type_test_resolved["total"] = 0
1020 count_type_test_unresolved["total"] = 0
1021 count_type_test_skipped["total"] = 0
1022 count_type_test_total["total"] = 0
1023 for tag in count_type_test_tags do
1024 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1025 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1026 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1027 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1028 count_type_test_total["total"] += count_type_test_total[tag]
1029 end
1030 var count_type_test = count_type_test_total["total"]
1031 var tags = count_type_test_tags.to_a
1032 tags.add("total")
1033 for tag in tags do
1034 printn tag
1035 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1036 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1037 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1038 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1039 print ""
1040 end
1041 end
1042 end
1043
1044 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1045
1046 # Division facility
1047 # Avoid division by zero by returning the string "n/a"
1048 fun div(a,b:Int):String
1049 do
1050 if b == 0 then return "n/a"
1051 return ((a*10000/b).to_f / 100.0).to_precision(2)
1052 end
1053 end
1054
1055 # A file unit (may be more than one file if
1056 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1057 class CodeFile
1058 var name: String
1059 var writers = new Array[CodeWriter]
1060 var required_declarations = new HashSet[String]
1061 end
1062
1063 # Where to store generated lines
1064 class CodeWriter
1065 var file: CodeFile
1066 var lines: List[String] = new List[String]
1067 var decl_lines: List[String] = new List[String]
1068
1069 # Add a line in the main part of the generated C
1070 fun add(s: String) do self.lines.add(s)
1071
1072 # Add a line in the
1073 # (used for local or global declaration)
1074 fun add_decl(s: String) do self.decl_lines.add(s)
1075
1076 init
1077 do
1078 file.writers.add(self)
1079 end
1080 end
1081
1082 # A visitor on the AST of property definition that generate the C code.
1083 abstract class AbstractCompilerVisitor
1084
1085 type COMPILER: AbstractCompiler
1086
1087 # The associated compiler
1088 var compiler: COMPILER
1089
1090 # The current visited AST node
1091 var current_node: nullable ANode = null is writable
1092
1093 # The current `StaticFrame`
1094 var frame: nullable StaticFrame = null is writable
1095
1096 # Alias for self.compiler.mainmodule.object_type
1097 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1098
1099 # Alias for self.compiler.mainmodule.bool_type
1100 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1101
1102 var writer: CodeWriter is noinit
1103
1104 init
1105 do
1106 self.writer = new CodeWriter(compiler.files.last)
1107 end
1108
1109 # Force to get the primitive property named `name` in the instance `recv` or abort
1110 fun get_property(name: String, recv: MType): MMethod
1111 do
1112 assert recv isa MClassType
1113 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1114 end
1115
1116 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1117 do
1118 var initializers = callsite.mpropdef.initializers
1119 if not initializers.is_empty then
1120 var recv = arguments.first
1121
1122 var i = 1
1123 for p in initializers do
1124 if p isa MMethod then
1125 var args = [recv]
1126 for x in p.intro.msignature.mparameters do
1127 args.add arguments[i]
1128 i += 1
1129 end
1130 self.send(p, args)
1131 else if p isa MAttribute then
1132 self.write_attribute(p, recv, arguments[i])
1133 i += 1
1134 else abort
1135 end
1136 assert i == arguments.length
1137
1138 return self.send(callsite.mproperty, [recv])
1139 end
1140
1141 return self.send(callsite.mproperty, arguments)
1142 end
1143
1144 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1145
1146 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1147
1148 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1149
1150 # Return an element of a native array.
1151 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1152 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1153
1154 # Store an element in a native array.
1155 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1156 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1157
1158 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1159 # This method is used to manage varargs in signatures and returns the real array
1160 # of runtime variables to use in the call.
1161 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1162 do
1163 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1164 var res = new Array[RuntimeVariable]
1165 res.add(recv)
1166
1167 if args.is_empty then return res
1168
1169 var vararg_rank = msignature.vararg_rank
1170 var vararg_len = args.length - msignature.arity
1171 if vararg_len < 0 then vararg_len = 0
1172
1173 for i in [0..msignature.arity[ do
1174 if i == vararg_rank then
1175 var ne = args[i]
1176 if ne isa AVarargExpr then
1177 var e = self.expr(ne.n_expr, null)
1178 res.add(e)
1179 continue
1180 end
1181 var vararg = new Array[RuntimeVariable]
1182 for j in [vararg_rank..vararg_rank+vararg_len] do
1183 var e = self.expr(args[j], null)
1184 vararg.add(e)
1185 end
1186 var elttype = msignature.mparameters[vararg_rank].mtype
1187 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1188 res.add(arg)
1189 else
1190 var j = i
1191 if i > vararg_rank then j += vararg_len
1192 var e = self.expr(args[j], null)
1193 res.add(e)
1194 end
1195 end
1196 return res
1197 end
1198
1199 # Type handling
1200
1201 # Anchor a type to the main module and the current receiver
1202 fun anchor(mtype: MType): MType
1203 do
1204 if not mtype.need_anchor then return mtype
1205 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1206 end
1207
1208 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1209 do
1210 if not mtype.need_anchor then return mtype
1211 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1212 end
1213
1214 # Unsafely cast a value to a new type
1215 # ie the result share the same C variable but my have a different mcasttype
1216 # NOTE: if the adaptation is useless then `value` is returned as it.
1217 # ENSURE: `result.name == value.name`
1218 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1219 do
1220 mtype = self.anchor(mtype)
1221 var valmtype = value.mcasttype
1222 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1223 return value
1224 end
1225
1226 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1227 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1228 return res
1229 else
1230 var res = new RuntimeVariable(value.name, valmtype, mtype)
1231 return res
1232 end
1233 end
1234
1235 # Generate a super call from a method definition
1236 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1237
1238 # Adapt the arguments of a method according to targetted `MMethodDef`
1239 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1240
1241 # Unbox all the arguments of a method when implemented `extern` or `intern`
1242 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1243
1244 # Box or unbox a value to another type iff a C type conversion is needed
1245 # ENSURE: `result.mtype.ctype == mtype.ctype`
1246 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1247
1248 # Box extern classes to be used in the generated code
1249 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1250
1251 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1252 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1253
1254 # Generate a polymorphic subtype test
1255 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1256
1257 # Generate the code required to dynamically check if 2 objects share the same runtime type
1258 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1259
1260 # Generate a Nit "is" for two runtime_variables
1261 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1262
1263 # Sends
1264
1265 # Generate a static call on a method definition
1266 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1267
1268 # Generate a polymorphic send for the method `m` and the arguments `args`
1269 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1270
1271 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1272 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1273 do
1274 assert t isa MClassType
1275 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1276 return self.call(propdef, t, args)
1277 end
1278
1279 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1280 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1281 do
1282 assert t isa MClassType
1283 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1284 return self.call(m, t, args)
1285 end
1286
1287 # Attributes handling
1288
1289 # Generate a polymorphic attribute is_set test
1290 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1291
1292 # Generate a polymorphic attribute read
1293 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1294
1295 # Generate a polymorphic attribute write
1296 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1297
1298 # Checks
1299
1300 # Add a check and an abort for a null receiver if needed
1301 fun check_recv_notnull(recv: RuntimeVariable)
1302 do
1303 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1304
1305 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1306 if maybenull then
1307 self.add("if (unlikely({recv} == NULL)) \{")
1308 self.add_abort("Receiver is null")
1309 self.add("\}")
1310 end
1311 end
1312
1313 # Names handling
1314
1315 private var names = new HashSet[String]
1316 private var last: Int = 0
1317
1318 # Return a new name based on `s` and unique in the visitor
1319 fun get_name(s: String): String
1320 do
1321 if not self.names.has(s) then
1322 self.names.add(s)
1323 return s
1324 end
1325 var i = self.last + 1
1326 loop
1327 var s2 = s + i.to_s
1328 if not self.names.has(s2) then
1329 self.last = i
1330 self.names.add(s2)
1331 return s2
1332 end
1333 i = i + 1
1334 end
1335 end
1336
1337 # Return an unique and stable identifier associated with an escapemark
1338 fun escapemark_name(e: nullable EscapeMark): String
1339 do
1340 assert e != null
1341 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1342 var name = e.name
1343 if name == null then name = "label"
1344 name = get_name(name)
1345 frame.escapemark_names[e] = name
1346 return name
1347 end
1348
1349 # Insert a C label for associated with an escapemark
1350 fun add_escape_label(e: nullable EscapeMark)
1351 do
1352 if e == null then return
1353 if e.escapes.is_empty then return
1354 add("BREAK_{escapemark_name(e)}: (void)0;")
1355 end
1356
1357 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1358 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1359 fun class_name_string(value: RuntimeVariable): String is abstract
1360
1361 # Variables handling
1362
1363 protected var variables = new HashMap[Variable, RuntimeVariable]
1364
1365 # Return the local runtime_variable associated to a Nit local variable
1366 fun variable(variable: Variable): RuntimeVariable
1367 do
1368 if self.variables.has_key(variable) then
1369 return self.variables[variable]
1370 else
1371 var name = self.get_name("var_{variable.name}")
1372 var mtype = variable.declared_type.as(not null)
1373 mtype = self.anchor(mtype)
1374 var res = new RuntimeVariable(name, mtype, mtype)
1375 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1376 self.variables[variable] = res
1377 return res
1378 end
1379 end
1380
1381 # Return a new uninitialized local runtime_variable
1382 fun new_var(mtype: MType): RuntimeVariable
1383 do
1384 mtype = self.anchor(mtype)
1385 var name = self.get_name("var")
1386 var res = new RuntimeVariable(name, mtype, mtype)
1387 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1388 return res
1389 end
1390
1391 # The difference with `new_var` is the C static type of the local variable
1392 fun new_var_extern(mtype: MType): RuntimeVariable
1393 do
1394 mtype = self.anchor(mtype)
1395 var name = self.get_name("var")
1396 var res = new RuntimeVariable(name, mtype, mtype)
1397 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1398 return res
1399 end
1400
1401 # Return a new uninitialized named runtime_variable
1402 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1403 do
1404 mtype = self.anchor(mtype)
1405 var res = new RuntimeVariable(name, mtype, mtype)
1406 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1407 return res
1408 end
1409
1410 # Correctly assign a left and a right value
1411 # Boxing and unboxing is performed if required
1412 fun assign(left, right: RuntimeVariable)
1413 do
1414 right = self.autobox(right, left.mtype)
1415 self.add("{left} = {right};")
1416 end
1417
1418 # Generate instances
1419
1420 # Generate a alloc-instance + init-attributes
1421 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1422
1423 # Allocate and init attributes of an instance of a standard or extern class
1424 #
1425 # Does not support universals and the pseudo-internal `NativeArray` class.
1426 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1427 do
1428 var recv
1429 var ctype = mtype.ctype
1430 assert mtype.mclass.name != "NativeArray"
1431 if not mtype.is_c_primitive then
1432 recv = init_instance(mtype)
1433 else if ctype == "char*" then
1434 recv = new_expr("NULL/*special!*/", mtype)
1435 else
1436 recv = new_expr("({ctype})0/*special!*/", mtype)
1437 end
1438 return recv
1439 end
1440
1441 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1442 fun set_finalizer(recv: RuntimeVariable)
1443 do
1444 var mtype = recv.mtype
1445 var finalizable_type = compiler.mainmodule.finalizable_type
1446 if finalizable_type != null and not mtype.need_anchor and
1447 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1448 add "gc_register_finalizer({recv});"
1449 end
1450 end
1451
1452 # The currently processed module
1453 #
1454 # alias for `compiler.mainmodule`
1455 fun mmodule: MModule do return compiler.mainmodule
1456
1457 # Generate an integer value
1458 fun int_instance(value: Int): RuntimeVariable
1459 do
1460 var t = mmodule.int_type
1461 var res = new RuntimeVariable("{value.to_s}l", t, t)
1462 return res
1463 end
1464
1465 # Generate a char value
1466 fun char_instance(value: Char): RuntimeVariable
1467 do
1468 var t = mmodule.char_type
1469 var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1470 return res
1471 end
1472
1473 # Generate a float value
1474 #
1475 # FIXME pass a Float, not a string
1476 fun float_instance(value: String): RuntimeVariable
1477 do
1478 var t = mmodule.float_type
1479 var res = new RuntimeVariable("{value}", t, t)
1480 return res
1481 end
1482
1483 # Generate an integer value
1484 fun bool_instance(value: Bool): RuntimeVariable
1485 do
1486 var s = if value then "1" else "0"
1487 var res = new RuntimeVariable(s, bool_type, bool_type)
1488 return res
1489 end
1490
1491 # Generate the `null` value
1492 fun null_instance: RuntimeVariable
1493 do
1494 var t = compiler.mainmodule.model.null_type
1495 var res = new RuntimeVariable("((val*)NULL)", t, t)
1496 return res
1497 end
1498
1499 # Generate a string value
1500 fun string_instance(string: String): RuntimeVariable
1501 do
1502 var mtype = mmodule.string_type
1503 var name = self.get_name("varonce")
1504 self.add_decl("static {mtype.ctype} {name};")
1505 var res = self.new_var(mtype)
1506 self.add("if (likely({name}!=NULL)) \{")
1507 self.add("{res} = {name};")
1508 self.add("\} else \{")
1509 var native_mtype = mmodule.native_string_type
1510 var nat = self.new_var(native_mtype)
1511 self.add("{nat} = \"{string.escape_to_c}\";")
1512 var length = self.int_instance(string.length)
1513 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1514 self.add("{name} = {res};")
1515 self.add("\}")
1516 return res
1517 end
1518
1519 fun value_instance(object: Object): RuntimeVariable
1520 do
1521 if object isa Int then
1522 return int_instance(object)
1523 else if object isa Bool then
1524 return bool_instance(object)
1525 else if object isa String then
1526 return string_instance(object)
1527 else
1528 abort
1529 end
1530 end
1531
1532 # Generate an array value
1533 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1534
1535 # Get an instance of a array for a vararg
1536 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1537
1538 # Code generation
1539
1540 # Add a line in the main part of the generated C
1541 fun add(s: String) do self.writer.lines.add(s)
1542
1543 # Add a line in the
1544 # (used for local or global declaration)
1545 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1546
1547 # Request the presence of a global declaration
1548 fun require_declaration(key: String)
1549 do
1550 var reqs = self.writer.file.required_declarations
1551 if reqs.has(key) then return
1552 reqs.add(key)
1553 var node = current_node
1554 if node != null then compiler.requirers_of_declarations[key] = node
1555 end
1556
1557 # Add a declaration in the local-header
1558 # The declaration is ensured to be present once
1559 fun declare_once(s: String)
1560 do
1561 self.compiler.provide_declaration(s, s)
1562 self.require_declaration(s)
1563 end
1564
1565 # Look for a needed .h and .c file for a given module
1566 # This is used for the legacy FFI
1567 fun add_extern(mmodule: MModule)
1568 do
1569 var file = mmodule.location.file.filename
1570 file = file.strip_extension(".nit")
1571 var tryfile = file + ".nit.h"
1572 if tryfile.file_exists then
1573 self.declare_once("#include \"{tryfile.basename("")}\"")
1574 self.compiler.files_to_copy.add(tryfile)
1575 end
1576 tryfile = file + "_nit.h"
1577 if tryfile.file_exists then
1578 self.declare_once("#include \"{tryfile.basename("")}\"")
1579 self.compiler.files_to_copy.add(tryfile)
1580 end
1581
1582 if self.compiler.seen_extern.has(file) then return
1583 self.compiler.seen_extern.add(file)
1584 tryfile = file + ".nit.c"
1585 if not tryfile.file_exists then
1586 tryfile = file + "_nit.c"
1587 if not tryfile.file_exists then return
1588 end
1589 var f = new ExternCFile(tryfile.basename(""), "")
1590 self.compiler.extern_bodies.add(f)
1591 self.compiler.files_to_copy.add(tryfile)
1592 end
1593
1594 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1595 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1596 do
1597 var res = new_var(mtype)
1598 self.add("{res} = {cexpr};")
1599 return res
1600 end
1601
1602 # Generate generic abort
1603 # used by aborts, asserts, casts, etc.
1604 fun add_abort(message: String)
1605 do
1606 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1607 add_raw_abort
1608 end
1609
1610 fun add_raw_abort
1611 do
1612 if self.current_node != null and self.current_node.location.file != null and
1613 self.current_node.location.file.mmodule != null then
1614 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1615 self.require_declaration(f)
1616 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1617 else
1618 self.add("PRINT_ERROR(\"\\n\");")
1619 end
1620 self.add("fatal_exit(1);")
1621 end
1622
1623 # Add a dynamic cast
1624 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1625 do
1626 var res = self.type_test(value, mtype, tag)
1627 self.add("if (unlikely(!{res})) \{")
1628 var cn = self.class_name_string(value)
1629 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1630 self.add_raw_abort
1631 self.add("\}")
1632 end
1633
1634 # Generate a return with the value `s`
1635 fun ret(s: RuntimeVariable)
1636 do
1637 self.assign(self.frame.returnvar.as(not null), s)
1638 self.add("goto {self.frame.returnlabel.as(not null)};")
1639 end
1640
1641 # Compile a statement (if any)
1642 fun stmt(nexpr: nullable AExpr)
1643 do
1644 if nexpr == null then return
1645
1646 var narray = nexpr.comprehension
1647 if narray != null then
1648 var recv = frame.comprehension.as(not null)
1649 var val = expr(nexpr, narray.element_mtype)
1650 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1651 return
1652 end
1653
1654 var old = self.current_node
1655 self.current_node = nexpr
1656 nexpr.stmt(self)
1657 self.current_node = old
1658 end
1659
1660 # Compile an expression an return its result
1661 # `mtype` is the expected return type, pass null if no specific type is expected.
1662 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1663 do
1664 var old = self.current_node
1665 self.current_node = nexpr
1666 var res = nexpr.expr(self).as(not null)
1667 if mtype != null then
1668 mtype = self.anchor(mtype)
1669 res = self.autobox(res, mtype)
1670 end
1671 res = autoadapt(res, nexpr.mtype.as(not null))
1672 var implicit_cast_to = nexpr.implicit_cast_to
1673 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1674 add_cast(res, implicit_cast_to, "auto")
1675 res = autoadapt(res, implicit_cast_to)
1676 end
1677 self.current_node = old
1678 return res
1679 end
1680
1681 # Alias for `self.expr(nexpr, self.bool_type)`
1682 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1683
1684 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1685 fun debug(message: String)
1686 do
1687 var node = self.current_node
1688 if node == null then
1689 print "?: {message}"
1690 else
1691 node.debug(message)
1692 end
1693 self.add("/* DEBUG: {message} */")
1694 end
1695 end
1696
1697 # A C function associated to a Nit method
1698 # Because of customization, a given Nit method can be compiler more that once
1699 abstract class AbstractRuntimeFunction
1700
1701 type COMPILER: AbstractCompiler
1702 type VISITOR: AbstractCompilerVisitor
1703
1704 # The associated Nit method
1705 var mmethoddef: MMethodDef
1706
1707 # The mangled c name of the runtime_function
1708 # Subclasses should redefine `build_c_name` instead
1709 fun c_name: String
1710 do
1711 var res = self.c_name_cache
1712 if res != null then return res
1713 res = self.build_c_name
1714 self.c_name_cache = res
1715 return res
1716 end
1717
1718 # Non cached version of `c_name`
1719 protected fun build_c_name: String is abstract
1720
1721 protected var c_name_cache: nullable String = null is writable
1722
1723 # Implements a call of the runtime_function
1724 # May inline the body or generate a C function call
1725 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1726
1727 # Generate the code for the `AbstractRuntimeFunction`
1728 # Warning: compile more than once compilation makes CC unhappy
1729 fun compile_to_c(compiler: COMPILER) is abstract
1730 end
1731
1732 # A runtime variable hold a runtime value in C.
1733 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1734 #
1735 # 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.
1736 class RuntimeVariable
1737 # The name of the variable in the C code
1738 var name: String
1739
1740 # The static type of the variable (as declard in C)
1741 var mtype: MType
1742
1743 # The current casted type of the variable (as known in Nit)
1744 var mcasttype: MType is writable
1745
1746 # If the variable exaclty a mcasttype?
1747 # false (usual value) means that the variable is a mcasttype or a subtype.
1748 var is_exact: Bool = false is writable
1749
1750 init
1751 do
1752 assert not mtype.need_anchor
1753 assert not mcasttype.need_anchor
1754 end
1755
1756 redef fun to_s do return name
1757
1758 redef fun inspect
1759 do
1760 var exact_str
1761 if self.is_exact then
1762 exact_str = " exact"
1763 else
1764 exact_str = ""
1765 end
1766 var type_str
1767 if self.mtype == self.mcasttype then
1768 type_str = "{mtype}{exact_str}"
1769 else
1770 type_str = "{mtype}({mcasttype}{exact_str})"
1771 end
1772 return "<{name}:{type_str}>"
1773 end
1774 end
1775
1776 # The static context of a visited property in a `AbstractCompilerVisitor`
1777 class StaticFrame
1778
1779 type VISITOR: AbstractCompilerVisitor
1780
1781 # The associated visitor
1782 var visitor: VISITOR
1783
1784 # The executed property.
1785 # A Method in case of a call, an attribute in case of a default initialization.
1786 var mpropdef: MPropDef
1787
1788 # The static type of the receiver
1789 var receiver: MClassType
1790
1791 # Arguments of the method (the first is the receiver)
1792 var arguments: Array[RuntimeVariable]
1793
1794 # The runtime_variable associated to the return (in a function)
1795 var returnvar: nullable RuntimeVariable = null is writable
1796
1797 # The label at the end of the property
1798 var returnlabel: nullable String = null is writable
1799
1800 # Labels associated to a each escapemarks.
1801 # Because of inlinings, escape-marks must be associated to their context (the frame)
1802 private var escapemark_names = new HashMap[EscapeMark, String]
1803
1804 # The array comprehension currently filled, if any
1805 private var comprehension: nullable RuntimeVariable = null
1806 end
1807
1808 redef class MType
1809 # Return the C type associated to a given Nit static type
1810 fun ctype: String do return "val*"
1811
1812 # C type outside of the compiler code and in boxes
1813 fun ctype_extern: String do return "val*"
1814
1815 # Short name of the `ctype` to use in unions
1816 fun ctypename: String do return "val"
1817
1818 # Is the associated C type a primitive one?
1819 #
1820 # ENSURE `result == (ctype != "val*")`
1821 fun is_c_primitive: Bool do return false
1822 end
1823
1824 redef class MClassType
1825
1826 redef var ctype is lazy do
1827 if mclass.name == "Int" then
1828 return "long"
1829 else if mclass.name == "Bool" then
1830 return "short int"
1831 else if mclass.name == "Char" then
1832 return "char"
1833 else if mclass.name == "Float" then
1834 return "double"
1835 else if mclass.name == "NativeString" then
1836 return "char*"
1837 else if mclass.name == "NativeArray" then
1838 return "val*"
1839 else
1840 return "val*"
1841 end
1842 end
1843
1844 redef var is_c_primitive is lazy do return ctype != "val*"
1845
1846 redef fun ctype_extern: String
1847 do
1848 if mclass.kind == extern_kind then
1849 return "void*"
1850 else
1851 return ctype
1852 end
1853 end
1854
1855 redef fun ctypename: String
1856 do
1857 if mclass.name == "Int" then
1858 return "l"
1859 else if mclass.name == "Bool" then
1860 return "s"
1861 else if mclass.name == "Char" then
1862 return "c"
1863 else if mclass.name == "Float" then
1864 return "d"
1865 else if mclass.name == "NativeString" then
1866 return "str"
1867 else if mclass.name == "NativeArray" then
1868 #return "{self.arguments.first.ctype}*"
1869 return "val"
1870 else
1871 return "val"
1872 end
1873 end
1874 end
1875
1876 redef class MPropDef
1877 type VISITOR: AbstractCompilerVisitor
1878 end
1879
1880 redef class MMethodDef
1881 # Can the body be inlined?
1882 fun can_inline(v: VISITOR): Bool
1883 do
1884 if is_abstract then return true
1885 var modelbuilder = v.compiler.modelbuilder
1886 var node = modelbuilder.mpropdef2node(self)
1887 if node isa APropdef then
1888 return node.can_inline
1889 else if node isa AClassdef then
1890 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1891 return true
1892 else
1893 abort
1894 end
1895 end
1896
1897 # Inline the body in another visitor
1898 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1899 do
1900 var modelbuilder = v.compiler.modelbuilder
1901 var val = constant_value
1902 var node = modelbuilder.mpropdef2node(self)
1903
1904 if is_abstract then
1905 var cn = v.class_name_string(arguments.first)
1906 v.current_node = node
1907 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1908 v.add_raw_abort
1909 return null
1910 end
1911
1912 if node isa APropdef then
1913 var oldnode = v.current_node
1914 v.current_node = node
1915 self.compile_parameter_check(v, arguments)
1916 node.compile_to_c(v, self, arguments)
1917 v.current_node = oldnode
1918 else if node isa AClassdef then
1919 var oldnode = v.current_node
1920 v.current_node = node
1921 self.compile_parameter_check(v, arguments)
1922 node.compile_to_c(v, self, arguments)
1923 v.current_node = oldnode
1924 else if val != null then
1925 v.ret(v.value_instance(val))
1926 else
1927 abort
1928 end
1929 return null
1930 end
1931
1932 # Generate type checks in the C code to check covariant parameters
1933 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1934 do
1935 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1936
1937 for i in [0..msignature.arity[ do
1938 # skip test for vararg since the array is instantiated with the correct polymorphic type
1939 if msignature.vararg_rank == i then continue
1940
1941 # skip if the cast is not required
1942 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1943 if not origmtype.need_anchor then continue
1944
1945 # get the parameter type
1946 var mtype = self.msignature.mparameters[i].mtype
1947
1948 # generate the cast
1949 # note that v decides if and how to implements the cast
1950 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1951 v.add_cast(arguments[i+1], mtype, "covariance")
1952 end
1953 end
1954 end
1955
1956 # Node visit
1957
1958 redef class APropdef
1959 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1960 do
1961 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1962 debug("Not yet implemented")
1963 end
1964
1965 fun can_inline: Bool do return true
1966 end
1967
1968 redef class AMethPropdef
1969 redef fun compile_to_c(v, mpropdef, arguments)
1970 do
1971 # Call the implicit super-init
1972 var auto_super_inits = self.auto_super_inits
1973 if auto_super_inits != null then
1974 var args = [arguments.first]
1975 for auto_super_init in auto_super_inits do
1976 assert auto_super_init.mproperty != mpropdef.mproperty
1977 args.clear
1978 for i in [0..auto_super_init.msignature.arity+1[ do
1979 args.add(arguments[i])
1980 end
1981 assert auto_super_init.mproperty != mpropdef.mproperty
1982 v.compile_callsite(auto_super_init, args)
1983 end
1984 end
1985 if auto_super_call then
1986 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1987 end
1988
1989 # Try special compilation
1990 if mpropdef.is_intern then
1991 if compile_intern_to_c(v, mpropdef, arguments) then return
1992 else if mpropdef.is_extern then
1993 if mpropdef.mproperty.is_init then
1994 if compile_externinit_to_c(v, mpropdef, arguments) then return
1995 else
1996 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1997 end
1998 end
1999
2000 # Compile block if any
2001 var n_block = n_block
2002 if n_block != null then
2003 for i in [0..mpropdef.msignature.arity[ do
2004 var variable = self.n_signature.n_params[i].variable.as(not null)
2005 v.assign(v.variable(variable), arguments[i+1])
2006 end
2007 v.stmt(n_block)
2008 return
2009 end
2010
2011 # We have a problem
2012 var cn = v.class_name_string(arguments.first)
2013 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2014 v.add_raw_abort
2015 end
2016
2017 redef fun can_inline
2018 do
2019 if self.auto_super_inits != null then return false
2020 var nblock = self.n_block
2021 if nblock == null then return true
2022 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2023 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2024 return false
2025 end
2026
2027 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2028 do
2029 var pname = mpropdef.mproperty.name
2030 var cname = mpropdef.mclassdef.mclass.name
2031 var ret = mpropdef.msignature.return_mtype
2032 if ret != null then
2033 ret = v.resolve_for(ret, arguments.first)
2034 end
2035 if pname != "==" and pname != "!=" then
2036 v.adapt_signature(mpropdef, arguments)
2037 v.unbox_signature_extern(mpropdef, arguments)
2038 end
2039 if cname == "Int" then
2040 if pname == "output" then
2041 v.add("printf(\"%ld\\n\", {arguments.first});")
2042 return true
2043 else if pname == "object_id" then
2044 v.ret(arguments.first)
2045 return true
2046 else if pname == "+" then
2047 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2048 return true
2049 else if pname == "-" then
2050 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2051 return true
2052 else if pname == "unary -" then
2053 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2054 return true
2055 else if pname == "*" then
2056 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2057 return true
2058 else if pname == "/" then
2059 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2060 return true
2061 else if pname == "%" then
2062 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2063 return true
2064 else if pname == "lshift" then
2065 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2066 return true
2067 else if pname == "rshift" then
2068 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2069 return true
2070 else if pname == "==" then
2071 v.ret(v.equal_test(arguments[0], arguments[1]))
2072 return true
2073 else if pname == "!=" then
2074 var res = v.equal_test(arguments[0], arguments[1])
2075 v.ret(v.new_expr("!{res}", ret.as(not null)))
2076 return true
2077 else if pname == "<" then
2078 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2079 return true
2080 else if pname == ">" then
2081 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2082 return true
2083 else if pname == "<=" then
2084 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2085 return true
2086 else if pname == ">=" then
2087 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2088 return true
2089 else if pname == "to_f" then
2090 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2091 return true
2092 else if pname == "ascii" then
2093 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2094 return true
2095 end
2096 else if cname == "Char" then
2097 if pname == "output" then
2098 v.add("printf(\"%c\", {arguments.first});")
2099 return true
2100 else if pname == "object_id" then
2101 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2102 return true
2103 else if pname == "successor" then
2104 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2105 return true
2106 else if pname == "predecessor" then
2107 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2108 return true
2109 else if pname == "==" then
2110 v.ret(v.equal_test(arguments[0], arguments[1]))
2111 return true
2112 else if pname == "!=" then
2113 var res = v.equal_test(arguments[0], arguments[1])
2114 v.ret(v.new_expr("!{res}", ret.as(not null)))
2115 return true
2116 else if pname == "<" then
2117 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2118 return true
2119 else if pname == ">" then
2120 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2121 return true
2122 else if pname == "<=" then
2123 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2124 return true
2125 else if pname == ">=" then
2126 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2127 return true
2128 else if pname == "to_i" then
2129 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2130 return true
2131 else if pname == "ascii" then
2132 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2133 return true
2134 end
2135 else if cname == "Bool" then
2136 if pname == "output" then
2137 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2138 return true
2139 else if pname == "object_id" then
2140 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2141 return true
2142 else if pname == "==" then
2143 v.ret(v.equal_test(arguments[0], arguments[1]))
2144 return true
2145 else if pname == "!=" then
2146 var res = v.equal_test(arguments[0], arguments[1])
2147 v.ret(v.new_expr("!{res}", ret.as(not null)))
2148 return true
2149 end
2150 else if cname == "Float" then
2151 if pname == "output" then
2152 v.add("printf(\"%f\\n\", {arguments.first});")
2153 return true
2154 else if pname == "object_id" then
2155 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2156 return true
2157 else if pname == "+" then
2158 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2159 return true
2160 else if pname == "-" then
2161 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2162 return true
2163 else if pname == "unary -" then
2164 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2165 return true
2166 else if pname == "succ" then
2167 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2168 return true
2169 else if pname == "prec" then
2170 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2171 return true
2172 else if pname == "*" then
2173 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2174 return true
2175 else if pname == "/" then
2176 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2177 return true
2178 else if pname == "==" then
2179 v.ret(v.equal_test(arguments[0], arguments[1]))
2180 return true
2181 else if pname == "!=" then
2182 var res = v.equal_test(arguments[0], arguments[1])
2183 v.ret(v.new_expr("!{res}", ret.as(not null)))
2184 return true
2185 else if pname == "<" then
2186 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2187 return true
2188 else if pname == ">" then
2189 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2190 return true
2191 else if pname == "<=" then
2192 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2193 return true
2194 else if pname == ">=" then
2195 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2196 return true
2197 else if pname == "to_i" then
2198 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2199 return true
2200 end
2201 else if cname == "NativeString" then
2202 if pname == "[]" then
2203 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2204 return true
2205 else if pname == "[]=" then
2206 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2207 return true
2208 else if pname == "copy_to" then
2209 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2210 return true
2211 else if pname == "atoi" then
2212 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2213 return true
2214 else if pname == "new" then
2215 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2216 return true
2217 end
2218 else if cname == "NativeArray" then
2219 v.native_array_def(pname, ret, arguments)
2220 return true
2221 end
2222 if pname == "exit" then
2223 v.add("exit({arguments[1]});")
2224 return true
2225 else if pname == "sys" then
2226 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2227 return true
2228 else if pname == "calloc_string" then
2229 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2230 return true
2231 else if pname == "calloc_array" then
2232 v.calloc_array(ret.as(not null), arguments)
2233 return true
2234 else if pname == "object_id" then
2235 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2236 return true
2237 else if pname == "is_same_type" then
2238 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2239 return true
2240 else if pname == "is_same_instance" then
2241 v.ret(v.equal_test(arguments[0], arguments[1]))
2242 return true
2243 else if pname == "output_class_name" then
2244 var nat = v.class_name_string(arguments.first)
2245 v.add("printf(\"%s\\n\", {nat});")
2246 return true
2247 else if pname == "native_class_name" then
2248 var nat = v.class_name_string(arguments.first)
2249 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2250 return true
2251 else if pname == "force_garbage_collection" then
2252 v.add("nit_gcollect();")
2253 return true
2254 else if pname == "native_argc" then
2255 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2256 return true
2257 else if pname == "native_argv" then
2258 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2259 return true
2260 end
2261 return false
2262 end
2263
2264 # Compile an extern method
2265 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2266 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2267 do
2268 var externname
2269 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2270 if at != null and at.n_args.length == 1 then
2271 externname = at.arg_as_string(v.compiler.modelbuilder)
2272 if externname == null then return false
2273 else
2274 return false
2275 end
2276 v.add_extern(mpropdef.mclassdef.mmodule)
2277 var res: nullable RuntimeVariable = null
2278 var ret = mpropdef.msignature.return_mtype
2279 if ret != null then
2280 ret = v.resolve_for(ret, arguments.first)
2281 res = v.new_var_extern(ret)
2282 end
2283 v.adapt_signature(mpropdef, arguments)
2284 v.unbox_signature_extern(mpropdef, arguments)
2285
2286 if res == null then
2287 v.add("{externname}({arguments.join(", ")});")
2288 else
2289 v.add("{res} = {externname}({arguments.join(", ")});")
2290 res = v.box_extern(res, ret.as(not null))
2291 v.ret(res)
2292 end
2293 return true
2294 end
2295
2296 # Compile an extern factory
2297 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2298 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2299 do
2300 var externname
2301 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2302 if at != null then
2303 externname = at.arg_as_string(v.compiler.modelbuilder)
2304 if externname == null then return false
2305 else
2306 return false
2307 end
2308 v.add_extern(mpropdef.mclassdef.mmodule)
2309 v.adapt_signature(mpropdef, arguments)
2310 v.unbox_signature_extern(mpropdef, arguments)
2311 var ret = arguments.first.mtype
2312 var res = v.new_var_extern(ret)
2313
2314 arguments.shift
2315
2316 v.add("{res} = {externname}({arguments.join(", ")});")
2317 res = v.box_extern(res, ret)
2318 v.ret(res)
2319 return true
2320 end
2321 end
2322
2323 redef class AAttrPropdef
2324 redef fun can_inline: Bool do return not is_lazy
2325
2326 redef fun compile_to_c(v, mpropdef, arguments)
2327 do
2328 if mpropdef == mreadpropdef then
2329 assert arguments.length == 1
2330 var recv = arguments.first
2331 var res
2332 if is_lazy then
2333 var set
2334 var ret = self.mpropdef.static_mtype
2335 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2336 var guard = self.mlazypropdef.mproperty
2337 if useiset then
2338 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2339 else
2340 set = v.read_attribute(guard, recv)
2341 end
2342 v.add("if(likely({set})) \{")
2343 res = v.read_attribute(self.mpropdef.mproperty, recv)
2344 v.add("\} else \{")
2345
2346 var value = evaluate_expr(v, recv)
2347
2348 v.assign(res, value)
2349 if not useiset then
2350 var true_v = v.bool_instance(true)
2351 v.write_attribute(guard, arguments.first, true_v)
2352 end
2353 v.add("\}")
2354 else
2355 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2356 end
2357 v.assign(v.frame.returnvar.as(not null), res)
2358 else if mpropdef == mwritepropdef then
2359 assert arguments.length == 2
2360 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2361 if is_lazy then
2362 var ret = self.mpropdef.static_mtype
2363 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2364 if not useiset then
2365 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2366 end
2367 end
2368 else
2369 abort
2370 end
2371 end
2372
2373 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2374 do
2375 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2376 end
2377
2378 # Evaluate, store and return the default value of the attribute
2379 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2380 do
2381 var oldnode = v.current_node
2382 v.current_node = self
2383 var old_frame = v.frame
2384 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2385 v.frame = frame
2386
2387 var value
2388 var mtype = self.mpropdef.static_mtype
2389 assert mtype != null
2390
2391 var nexpr = self.n_expr
2392 var nblock = self.n_block
2393 if nexpr != null then
2394 value = v.expr(nexpr, mtype)
2395 else if nblock != null then
2396 value = v.new_var(mtype)
2397 frame.returnvar = value
2398 frame.returnlabel = v.get_name("RET_LABEL")
2399 v.add("\{")
2400 v.stmt(nblock)
2401 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2402 v.add("\}")
2403 else
2404 abort
2405 end
2406
2407 v.write_attribute(self.mpropdef.mproperty, recv, value)
2408
2409 v.frame = old_frame
2410 v.current_node = oldnode
2411
2412 return value
2413 end
2414
2415 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2416 do
2417 var nexpr = self.n_expr
2418 if nexpr != null then return
2419
2420 var oldnode = v.current_node
2421 v.current_node = self
2422 var old_frame = v.frame
2423 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2424 v.frame = frame
2425 # Force read to check the initialization
2426 v.read_attribute(self.mpropdef.mproperty, recv)
2427 v.frame = old_frame
2428 v.current_node = oldnode
2429 end
2430 end
2431
2432 redef class AClassdef
2433 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2434 do
2435 if mpropdef == self.mfree_init then
2436 assert mpropdef.mproperty.is_root_init
2437 assert arguments.length == 1
2438 if not mpropdef.is_intro then
2439 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2440 end
2441 return
2442 else
2443 abort
2444 end
2445 end
2446 end
2447
2448 redef class AExpr
2449 # Try to compile self as an expression
2450 # Do not call this method directly, use `v.expr` instead
2451 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2452 do
2453 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2454 var mtype = self.mtype
2455 if mtype == null then
2456 return null
2457 else
2458 var res = v.new_var(mtype)
2459 v.add("/* {res} = NOT YET {class_name} */")
2460 return res
2461 end
2462 end
2463
2464 # Try to compile self as a statement
2465 # Do not call this method directly, use `v.stmt` instead
2466 private fun stmt(v: AbstractCompilerVisitor)
2467 do
2468 expr(v)
2469 end
2470 end
2471
2472 redef class ABlockExpr
2473 redef fun stmt(v)
2474 do
2475 for e in self.n_expr do v.stmt(e)
2476 end
2477 redef fun expr(v)
2478 do
2479 var last = self.n_expr.last
2480 for e in self.n_expr do
2481 if e == last then break
2482 v.stmt(e)
2483 end
2484 return v.expr(last, null)
2485 end
2486 end
2487
2488 redef class AVardeclExpr
2489 redef fun stmt(v)
2490 do
2491 var variable = self.variable.as(not null)
2492 var ne = self.n_expr
2493 if ne != null then
2494 var i = v.expr(ne, variable.declared_type)
2495 v.assign(v.variable(variable), i)
2496 end
2497 end
2498 end
2499
2500 redef class AVarExpr
2501 redef fun expr(v)
2502 do
2503 var res = v.variable(self.variable.as(not null))
2504 var mtype = self.mtype.as(not null)
2505 return v.autoadapt(res, mtype)
2506 end
2507 end
2508
2509 redef class AVarAssignExpr
2510 redef fun expr(v)
2511 do
2512 var variable = self.variable.as(not null)
2513 var i = v.expr(self.n_value, variable.declared_type)
2514 v.assign(v.variable(variable), i)
2515 return i
2516 end
2517 end
2518
2519 redef class AVarReassignExpr
2520 redef fun stmt(v)
2521 do
2522 var variable = self.variable.as(not null)
2523 var vari = v.variable(variable)
2524 var value = v.expr(self.n_value, variable.declared_type)
2525 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2526 assert res != null
2527 v.assign(v.variable(variable), res)
2528 end
2529 end
2530
2531 redef class ASelfExpr
2532 redef fun expr(v) do return v.frame.arguments.first
2533 end
2534
2535 redef class AEscapeExpr
2536 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2537 end
2538
2539 redef class AReturnExpr
2540 redef fun stmt(v)
2541 do
2542 var nexpr = self.n_expr
2543 if nexpr != null then
2544 var returnvar = v.frame.returnvar.as(not null)
2545 var i = v.expr(nexpr, returnvar.mtype)
2546 v.assign(returnvar, i)
2547 end
2548 v.add("goto {v.frame.returnlabel.as(not null)};")
2549 end
2550 end
2551
2552 redef class AAbortExpr
2553 redef fun stmt(v) do v.add_abort("Aborted")
2554 end
2555
2556 redef class AIfExpr
2557 redef fun stmt(v)
2558 do
2559 var cond = v.expr_bool(self.n_expr)
2560 v.add("if ({cond})\{")
2561 v.stmt(self.n_then)
2562 v.add("\} else \{")
2563 v.stmt(self.n_else)
2564 v.add("\}")
2565 end
2566
2567 redef fun expr(v)
2568 do
2569 var res = v.new_var(self.mtype.as(not null))
2570 var cond = v.expr_bool(self.n_expr)
2571 v.add("if ({cond})\{")
2572 v.assign(res, v.expr(self.n_then.as(not null), null))
2573 v.add("\} else \{")
2574 v.assign(res, v.expr(self.n_else.as(not null), null))
2575 v.add("\}")
2576 return res
2577 end
2578 end
2579
2580 redef class AIfexprExpr
2581 redef fun expr(v)
2582 do
2583 var res = v.new_var(self.mtype.as(not null))
2584 var cond = v.expr_bool(self.n_expr)
2585 v.add("if ({cond})\{")
2586 v.assign(res, v.expr(self.n_then, null))
2587 v.add("\} else \{")
2588 v.assign(res, v.expr(self.n_else, null))
2589 v.add("\}")
2590 return res
2591 end
2592 end
2593
2594 redef class ADoExpr
2595 redef fun stmt(v)
2596 do
2597 v.stmt(self.n_block)
2598 v.add_escape_label(break_mark)
2599 end
2600 end
2601
2602 redef class AWhileExpr
2603 redef fun stmt(v)
2604 do
2605 v.add("for(;;) \{")
2606 var cond = v.expr_bool(self.n_expr)
2607 v.add("if (!{cond}) break;")
2608 v.stmt(self.n_block)
2609 v.add_escape_label(continue_mark)
2610 v.add("\}")
2611 v.add_escape_label(break_mark)
2612 end
2613 end
2614
2615 redef class ALoopExpr
2616 redef fun stmt(v)
2617 do
2618 v.add("for(;;) \{")
2619 v.stmt(self.n_block)
2620 v.add_escape_label(continue_mark)
2621 v.add("\}")
2622 v.add_escape_label(break_mark)
2623 end
2624 end
2625
2626 redef class AForExpr
2627 redef fun stmt(v)
2628 do
2629 var cl = v.expr(self.n_expr, null)
2630 var it_meth = self.method_iterator
2631 assert it_meth != null
2632 var it = v.compile_callsite(it_meth, [cl])
2633 assert it != null
2634 v.add("for(;;) \{")
2635 var isok_meth = self.method_is_ok
2636 assert isok_meth != null
2637 var ok = v.compile_callsite(isok_meth, [it])
2638 assert ok != null
2639 v.add("if(!{ok}) break;")
2640 if self.variables.length == 1 then
2641 var item_meth = self.method_item
2642 assert item_meth != null
2643 var i = v.compile_callsite(item_meth, [it])
2644 assert i != null
2645 v.assign(v.variable(variables.first), i)
2646 else if self.variables.length == 2 then
2647 var key_meth = self.method_key
2648 assert key_meth != null
2649 var i = v.compile_callsite(key_meth, [it])
2650 assert i != null
2651 v.assign(v.variable(variables[0]), i)
2652 var item_meth = self.method_item
2653 assert item_meth != null
2654 i = v.compile_callsite(item_meth, [it])
2655 assert i != null
2656 v.assign(v.variable(variables[1]), i)
2657 else
2658 abort
2659 end
2660 v.stmt(self.n_block)
2661 v.add_escape_label(continue_mark)
2662 var next_meth = self.method_next
2663 assert next_meth != null
2664 v.compile_callsite(next_meth, [it])
2665 v.add("\}")
2666 v.add_escape_label(break_mark)
2667
2668 var method_finish = self.method_finish
2669 if method_finish != null then
2670 # TODO: Find a way to call this also in long escape (e.g. return)
2671 v.compile_callsite(method_finish, [it])
2672 end
2673 end
2674 end
2675
2676 redef class AAssertExpr
2677 redef fun stmt(v)
2678 do
2679 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2680
2681 var cond = v.expr_bool(self.n_expr)
2682 v.add("if (unlikely(!{cond})) \{")
2683 v.stmt(self.n_else)
2684 var nid = self.n_id
2685 if nid != null then
2686 v.add_abort("Assert '{nid.text}' failed")
2687 else
2688 v.add_abort("Assert failed")
2689 end
2690 v.add("\}")
2691 end
2692 end
2693
2694 redef class AOrExpr
2695 redef fun expr(v)
2696 do
2697 var res = v.new_var(self.mtype.as(not null))
2698 var i1 = v.expr_bool(self.n_expr)
2699 v.add("if ({i1}) \{")
2700 v.add("{res} = 1;")
2701 v.add("\} else \{")
2702 var i2 = v.expr_bool(self.n_expr2)
2703 v.add("{res} = {i2};")
2704 v.add("\}")
2705 return res
2706 end
2707 end
2708
2709 redef class AImpliesExpr
2710 redef fun expr(v)
2711 do
2712 var res = v.new_var(self.mtype.as(not null))
2713 var i1 = v.expr_bool(self.n_expr)
2714 v.add("if (!{i1}) \{")
2715 v.add("{res} = 1;")
2716 v.add("\} else \{")
2717 var i2 = v.expr_bool(self.n_expr2)
2718 v.add("{res} = {i2};")
2719 v.add("\}")
2720 return res
2721 end
2722 end
2723
2724 redef class AAndExpr
2725 redef fun expr(v)
2726 do
2727 var res = v.new_var(self.mtype.as(not null))
2728 var i1 = v.expr_bool(self.n_expr)
2729 v.add("if (!{i1}) \{")
2730 v.add("{res} = 0;")
2731 v.add("\} else \{")
2732 var i2 = v.expr_bool(self.n_expr2)
2733 v.add("{res} = {i2};")
2734 v.add("\}")
2735 return res
2736 end
2737 end
2738
2739 redef class ANotExpr
2740 redef fun expr(v)
2741 do
2742 var cond = v.expr_bool(self.n_expr)
2743 return v.new_expr("!{cond}", self.mtype.as(not null))
2744 end
2745 end
2746
2747 redef class AOrElseExpr
2748 redef fun expr(v)
2749 do
2750 var res = v.new_var(self.mtype.as(not null))
2751 var i1 = v.expr(self.n_expr, null)
2752 v.add("if ({i1}!=NULL) \{")
2753 v.assign(res, i1)
2754 v.add("\} else \{")
2755 var i2 = v.expr(self.n_expr2, null)
2756 v.assign(res, i2)
2757 v.add("\}")
2758 return res
2759 end
2760 end
2761
2762 redef class AIntExpr
2763 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2764 end
2765
2766 redef class AFloatExpr
2767 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2768 end
2769
2770 redef class ACharExpr
2771 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2772 end
2773
2774 redef class AArrayExpr
2775 redef fun expr(v)
2776 do
2777 var mtype = self.element_mtype.as(not null)
2778 var array = new Array[RuntimeVariable]
2779 var res = v.array_instance(array, mtype)
2780
2781 var old_comprehension = v.frame.comprehension
2782 v.frame.comprehension = res
2783 for nexpr in self.n_exprs do
2784 v.stmt(nexpr)
2785 end
2786 v.frame.comprehension = old_comprehension
2787
2788 return res
2789 end
2790 end
2791
2792 redef class AStringFormExpr
2793 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2794 end
2795
2796 redef class ASuperstringExpr
2797 redef fun expr(v)
2798 do
2799 var type_string = mtype.as(not null)
2800
2801 # Collect elements of the superstring
2802 var array = new Array[AExpr]
2803 for ne in self.n_exprs do
2804 # Drop literal empty string.
2805 # They appears in things like "{a}" that is ["", a, ""]
2806 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2807 array.add(ne)
2808 end
2809
2810 # Store the allocated native array in a static variable
2811 # For reusing later
2812 var varonce = v.get_name("varonce")
2813 v.add("if (unlikely({varonce}==NULL)) \{")
2814
2815 # The native array that will contains the elements to_s-ized.
2816 # For fast concatenation.
2817 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2818
2819 v.add_decl("static {a.mtype.ctype} {varonce};")
2820
2821 # Pre-fill the array with the literal string parts.
2822 # So they do not need to be filled again when reused
2823 for i in [0..array.length[ do
2824 var ne = array[i]
2825 if not ne isa AStringFormExpr then continue
2826 var e = v.expr(ne, null)
2827 v.native_array_set(a, i, e)
2828 end
2829
2830 v.add("\} else \{")
2831 # Take the native-array from the store.
2832 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2833 # WARNING: not thread safe! (FIXME?)
2834 v.add("{a} = {varonce};")
2835 v.add("{varonce} = NULL;")
2836 v.add("\}")
2837
2838 # Stringify the elements and put them in the native array
2839 var to_s_method = v.get_property("to_s", v.object_type)
2840 for i in [0..array.length[ do
2841 var ne = array[i]
2842 if ne isa AStringFormExpr then continue
2843 var e = v.expr(ne, null)
2844 # Skip the `to_s` if the element is already a String
2845 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
2846 e = v.send(to_s_method, [e]).as(not null)
2847 end
2848 v.native_array_set(a, i, e)
2849 end
2850
2851 # Fast join the native string to get the result
2852 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
2853
2854 # We finish to work with the native array,
2855 # so store it so that it can be reused
2856 v.add("{varonce} = {a};")
2857 return res
2858 end
2859 end
2860
2861 redef class ACrangeExpr
2862 redef fun expr(v)
2863 do
2864 var i1 = v.expr(self.n_expr, null)
2865 var i2 = v.expr(self.n_expr2, null)
2866 var mtype = self.mtype.as(MClassType)
2867 var res = v.init_instance(mtype)
2868 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2869 return res
2870 end
2871 end
2872
2873 redef class AOrangeExpr
2874 redef fun expr(v)
2875 do
2876 var i1 = v.expr(self.n_expr, null)
2877 var i2 = v.expr(self.n_expr2, null)
2878 var mtype = self.mtype.as(MClassType)
2879 var res = v.init_instance(mtype)
2880 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2881 return res
2882 end
2883 end
2884
2885 redef class ATrueExpr
2886 redef fun expr(v) do return v.bool_instance(true)
2887 end
2888
2889 redef class AFalseExpr
2890 redef fun expr(v) do return v.bool_instance(false)
2891 end
2892
2893 redef class ANullExpr
2894 redef fun expr(v) do return v.null_instance
2895 end
2896
2897 redef class AIsaExpr
2898 redef fun expr(v)
2899 do
2900 var i = v.expr(self.n_expr, null)
2901 return v.type_test(i, self.cast_type.as(not null), "isa")
2902 end
2903 end
2904
2905 redef class AAsCastExpr
2906 redef fun expr(v)
2907 do
2908 var i = v.expr(self.n_expr, null)
2909 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2910
2911 v.add_cast(i, self.mtype.as(not null), "as")
2912 return i
2913 end
2914 end
2915
2916 redef class AAsNotnullExpr
2917 redef fun expr(v)
2918 do
2919 var i = v.expr(self.n_expr, null)
2920 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2921
2922 if i.mtype.is_c_primitive then return i
2923
2924 v.add("if (unlikely({i} == NULL)) \{")
2925 v.add_abort("Cast failed")
2926 v.add("\}")
2927 return i
2928 end
2929 end
2930
2931 redef class AParExpr
2932 redef fun expr(v) do return v.expr(self.n_expr, null)
2933 end
2934
2935 redef class AOnceExpr
2936 redef fun expr(v)
2937 do
2938 var mtype = self.mtype.as(not null)
2939 var name = v.get_name("varonce")
2940 var guard = v.get_name(name + "_guard")
2941 v.add_decl("static {mtype.ctype} {name};")
2942 v.add_decl("static int {guard};")
2943 var res = v.new_var(mtype)
2944 v.add("if (likely({guard})) \{")
2945 v.add("{res} = {name};")
2946 v.add("\} else \{")
2947 var i = v.expr(self.n_expr, mtype)
2948 v.add("{res} = {i};")
2949 v.add("{name} = {res};")
2950 v.add("{guard} = 1;")
2951 v.add("\}")
2952 return res
2953 end
2954 end
2955
2956 redef class ASendExpr
2957 redef fun expr(v)
2958 do
2959 var recv = v.expr(self.n_expr, null)
2960 var callsite = self.callsite.as(not null)
2961 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2962 return v.compile_callsite(callsite, args)
2963 end
2964 end
2965
2966 redef class ASendReassignFormExpr
2967 redef fun stmt(v)
2968 do
2969 var recv = v.expr(self.n_expr, null)
2970 var callsite = self.callsite.as(not null)
2971 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2972
2973 var value = v.expr(self.n_value, null)
2974
2975 var left = v.compile_callsite(callsite, args)
2976 assert left != null
2977
2978 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2979 assert res != null
2980
2981 args.add(res)
2982 v.compile_callsite(self.write_callsite.as(not null), args)
2983 end
2984 end
2985
2986 redef class ASuperExpr
2987 redef fun expr(v)
2988 do
2989 var recv = v.frame.arguments.first
2990
2991 var callsite = self.callsite
2992 if callsite != null then
2993 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2994
2995 # Add additional arguments for the super init call
2996 if args.length == 1 then
2997 for i in [0..callsite.msignature.arity[ do
2998 args.add(v.frame.arguments[i+1])
2999 end
3000 end
3001 # Super init call
3002 var res = v.compile_callsite(callsite, args)
3003 return res
3004 end
3005
3006 var mpropdef = self.mpropdef.as(not null)
3007 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
3008 if args.length == 1 then
3009 args = v.frame.arguments
3010 end
3011
3012 # stantard call-next-method
3013 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3014 end
3015 end
3016
3017 redef class ANewExpr
3018 redef fun expr(v)
3019 do
3020 var mtype = self.recvtype
3021 assert mtype != null
3022
3023 if mtype.mclass.name == "NativeArray" then
3024 assert self.n_args.n_exprs.length == 1
3025 var l = v.expr(self.n_args.n_exprs.first, null)
3026 assert mtype isa MGenericType
3027 var elttype = mtype.arguments.first
3028 return v.native_array_instance(elttype, l)
3029 end
3030
3031 var recv = v.init_instance_or_extern(mtype)
3032
3033 var callsite = self.callsite.as(not null)
3034 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
3035 var res2 = v.compile_callsite(callsite, args)
3036 if res2 != null then
3037 #self.debug("got {res2} from {mproperty}. drop {recv}")
3038 return res2
3039 end
3040 return recv
3041 end
3042 end
3043
3044 redef class AAttrExpr
3045 redef fun expr(v)
3046 do
3047 var recv = v.expr(self.n_expr, null)
3048 var mproperty = self.mproperty.as(not null)
3049 return v.read_attribute(mproperty, recv)
3050 end
3051 end
3052
3053 redef class AAttrAssignExpr
3054 redef fun expr(v)
3055 do
3056 var recv = v.expr(self.n_expr, null)
3057 var i = v.expr(self.n_value, null)
3058 var mproperty = self.mproperty.as(not null)
3059 v.write_attribute(mproperty, recv, i)
3060 return i
3061 end
3062 end
3063
3064 redef class AAttrReassignExpr
3065 redef fun stmt(v)
3066 do
3067 var recv = v.expr(self.n_expr, null)
3068 var value = v.expr(self.n_value, null)
3069 var mproperty = self.mproperty.as(not null)
3070 var attr = v.read_attribute(mproperty, recv)
3071 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3072 assert res != null
3073 v.write_attribute(mproperty, recv, res)
3074 end
3075 end
3076
3077 redef class AIssetAttrExpr
3078 redef fun expr(v)
3079 do
3080 var recv = v.expr(self.n_expr, null)
3081 var mproperty = self.mproperty.as(not null)
3082 return v.isset_attribute(mproperty, recv)
3083 end
3084 end
3085
3086 redef class ADebugTypeExpr
3087 redef fun stmt(v)
3088 do
3089 # do nothing
3090 end
3091 end
3092
3093 # Utils
3094
3095 redef class Array[E]
3096 # Return a new `Array` with the elements only contened in self and not in `o`
3097 fun -(o: Array[E]): Array[E] do
3098 var res = new Array[E]
3099 for e in self do if not o.has(e) then res.add(e)
3100 return res
3101 end
3102 end
3103
3104 redef class MModule
3105 # All `MProperty` associated to all `MClassDef` of `mclass`
3106 fun properties(mclass: MClass): Set[MProperty] do
3107 if not self.properties_cache.has_key(mclass) then
3108 var properties = new HashSet[MProperty]
3109 var parents = new Array[MClass]
3110 if self.flatten_mclass_hierarchy.has(mclass) then
3111 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3112 end
3113 for parent in parents do
3114 properties.add_all(self.properties(parent))
3115 end
3116 for mclassdef in mclass.mclassdefs do
3117 if not self.in_importation <= mclassdef.mmodule then continue
3118 for mprop in mclassdef.intro_mproperties do
3119 properties.add(mprop)
3120 end
3121 end
3122 self.properties_cache[mclass] = properties
3123 end
3124 return properties_cache[mclass]
3125 end
3126 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3127
3128 # Write FFI and nitni results to file
3129 fun finalize_ffi(c: AbstractCompiler) do end
3130
3131 # Give requided addinional system libraries (as given to LD_LIBS)
3132 # Note: can return null instead of an empty set
3133 fun collect_linker_libs: nullable Array[String] do return null
3134 end
3135
3136 # Create a tool context to handle options and paths
3137 var toolcontext = new ToolContext
3138
3139 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3140
3141 # We do not add other options, so process them now!
3142 toolcontext.process_options(args)
3143
3144 # We need a model to collect stufs
3145 var model = new Model
3146 # An a model builder to parse files
3147 var modelbuilder = new ModelBuilder(model, toolcontext)
3148
3149 var arguments = toolcontext.option_context.rest
3150 if arguments.length > 1 and toolcontext.opt_output.value != null then
3151 print "Error: --output needs a single source file. Do you prefer --dir?"
3152 exit 1
3153 end
3154
3155 # Here we load an process all modules passed on the command line
3156 var mmodules = modelbuilder.parse(arguments)
3157
3158 if mmodules.is_empty then return
3159 modelbuilder.run_phases
3160
3161 for mmodule in mmodules do
3162 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3163 var ms = [mmodule]
3164 toolcontext.run_global_phases(ms)
3165 end