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