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