Merge: sepcomp: fixup trampoline with tagged primitive values
[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 # Return an element of a native array.
1132 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1133 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1134
1135 # Store an element in a native array.
1136 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1137 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1138
1139 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1140 # This method is used to manage varargs in signatures and returns the real array
1141 # of runtime variables to use in the call.
1142 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1143 do
1144 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1145 var res = new Array[RuntimeVariable]
1146 res.add(recv)
1147
1148 if args.is_empty then return res
1149
1150 var vararg_rank = msignature.vararg_rank
1151 var vararg_len = args.length - msignature.arity
1152 if vararg_len < 0 then vararg_len = 0
1153
1154 for i in [0..msignature.arity[ do
1155 if i == vararg_rank then
1156 var ne = args[i]
1157 if ne isa AVarargExpr then
1158 var e = self.expr(ne.n_expr, null)
1159 res.add(e)
1160 continue
1161 end
1162 var vararg = new Array[RuntimeVariable]
1163 for j in [vararg_rank..vararg_rank+vararg_len] do
1164 var e = self.expr(args[j], null)
1165 vararg.add(e)
1166 end
1167 var elttype = msignature.mparameters[vararg_rank].mtype
1168 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1169 res.add(arg)
1170 else
1171 var j = i
1172 if i > vararg_rank then j += vararg_len
1173 var e = self.expr(args[j], null)
1174 res.add(e)
1175 end
1176 end
1177 return res
1178 end
1179
1180 # Type handling
1181
1182 # Anchor a type to the main module and the current receiver
1183 fun anchor(mtype: MType): MType
1184 do
1185 if not mtype.need_anchor then return mtype
1186 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1187 end
1188
1189 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1190 do
1191 if not mtype.need_anchor then return mtype
1192 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1193 end
1194
1195 # Unsafely cast a value to a new type
1196 # ie the result share the same C variable but my have a different mcasttype
1197 # NOTE: if the adaptation is useless then `value` is returned as it.
1198 # ENSURE: `result.name == value.name`
1199 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1200 do
1201 mtype = self.anchor(mtype)
1202 var valmtype = value.mcasttype
1203 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1204 return value
1205 end
1206
1207 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1208 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1209 return res
1210 else
1211 var res = new RuntimeVariable(value.name, valmtype, mtype)
1212 return res
1213 end
1214 end
1215
1216 # Generate a super call from a method definition
1217 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1218
1219 # Adapt the arguments of a method according to targetted `MMethodDef`
1220 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1221
1222 # Unbox all the arguments of a method when implemented `extern` or `intern`
1223 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1224
1225 # Box or unbox a value to another type iff a C type conversion is needed
1226 # ENSURE: `result.mtype.ctype == mtype.ctype`
1227 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1228
1229 # Box extern classes to be used in the generated code
1230 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1231
1232 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1233 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1234
1235 # Generate a polymorphic subtype test
1236 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1237
1238 # Generate the code required to dynamically check if 2 objects share the same runtime type
1239 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1240
1241 # Generate a Nit "is" for two runtime_variables
1242 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1243
1244 # Sends
1245
1246 # Generate a static call on a method definition
1247 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1248
1249 # Generate a polymorphic send for the method `m` and the arguments `args`
1250 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1251
1252 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1253 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1254 do
1255 assert t isa MClassType
1256 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1257 return self.call(propdef, t, args)
1258 end
1259
1260 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1261 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1262 do
1263 assert t isa MClassType
1264 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1265 return self.call(m, t, args)
1266 end
1267
1268 # Attributes handling
1269
1270 # Generate a polymorphic attribute is_set test
1271 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1272
1273 # Generate a polymorphic attribute read
1274 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1275
1276 # Generate a polymorphic attribute write
1277 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1278
1279 # Checks
1280
1281 # Add a check and an abort for a null receiver if needed
1282 fun check_recv_notnull(recv: RuntimeVariable)
1283 do
1284 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1285
1286 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1287 if maybenull then
1288 self.add("if (unlikely({recv} == NULL)) \{")
1289 self.add_abort("Receiver is null")
1290 self.add("\}")
1291 end
1292 end
1293
1294 # Names handling
1295
1296 private var names = new HashSet[String]
1297 private var last: Int = 0
1298
1299 # Return a new name based on `s` and unique in the visitor
1300 fun get_name(s: String): String
1301 do
1302 if not self.names.has(s) then
1303 self.names.add(s)
1304 return s
1305 end
1306 var i = self.last + 1
1307 loop
1308 var s2 = s + i.to_s
1309 if not self.names.has(s2) then
1310 self.last = i
1311 self.names.add(s2)
1312 return s2
1313 end
1314 i = i + 1
1315 end
1316 end
1317
1318 # Return an unique and stable identifier associated with an escapemark
1319 fun escapemark_name(e: nullable EscapeMark): String
1320 do
1321 assert e != null
1322 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1323 var name = e.name
1324 if name == null then name = "label"
1325 name = get_name(name)
1326 frame.escapemark_names[e] = name
1327 return name
1328 end
1329
1330 # Insert a C label for associated with an escapemark
1331 fun add_escape_label(e: nullable EscapeMark)
1332 do
1333 if e == null then return
1334 if e.escapes.is_empty then return
1335 add("BREAK_{escapemark_name(e)}: (void)0;")
1336 end
1337
1338 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1339 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1340 fun class_name_string(value: RuntimeVariable): String is abstract
1341
1342 # Variables handling
1343
1344 protected var variables = new HashMap[Variable, RuntimeVariable]
1345
1346 # Return the local runtime_variable associated to a Nit local variable
1347 fun variable(variable: Variable): RuntimeVariable
1348 do
1349 if self.variables.has_key(variable) then
1350 return self.variables[variable]
1351 else
1352 var name = self.get_name("var_{variable.name}")
1353 var mtype = variable.declared_type.as(not null)
1354 mtype = self.anchor(mtype)
1355 var res = new RuntimeVariable(name, mtype, mtype)
1356 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1357 self.variables[variable] = res
1358 return res
1359 end
1360 end
1361
1362 # Return a new uninitialized local runtime_variable
1363 fun new_var(mtype: MType): RuntimeVariable
1364 do
1365 mtype = self.anchor(mtype)
1366 var name = self.get_name("var")
1367 var res = new RuntimeVariable(name, mtype, mtype)
1368 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1369 return res
1370 end
1371
1372 # The difference with `new_var` is the C static type of the local variable
1373 fun new_var_extern(mtype: MType): RuntimeVariable
1374 do
1375 mtype = self.anchor(mtype)
1376 var name = self.get_name("var")
1377 var res = new RuntimeVariable(name, mtype, mtype)
1378 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1379 return res
1380 end
1381
1382 # Return a new uninitialized named runtime_variable
1383 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1384 do
1385 mtype = self.anchor(mtype)
1386 var res = new RuntimeVariable(name, mtype, mtype)
1387 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1388 return res
1389 end
1390
1391 # Correctly assign a left and a right value
1392 # Boxing and unboxing is performed if required
1393 fun assign(left, right: RuntimeVariable)
1394 do
1395 right = self.autobox(right, left.mtype)
1396 self.add("{left} = {right};")
1397 end
1398
1399 # Generate instances
1400
1401 # Generate a alloc-instance + init-attributes
1402 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1403
1404 # Allocate and init attributes of an instance of a standard or extern class
1405 #
1406 # Does not support universals and the pseudo-internal `NativeArray` class.
1407 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1408 do
1409 var recv
1410 var ctype = mtype.ctype
1411 assert mtype.mclass.name != "NativeArray"
1412 if not mtype.is_c_primitive then
1413 recv = init_instance(mtype)
1414 else if ctype == "char*" then
1415 recv = new_expr("NULL/*special!*/", mtype)
1416 else
1417 recv = new_expr("({ctype})0/*special!*/", mtype)
1418 end
1419 return recv
1420 end
1421
1422 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1423 fun set_finalizer(recv: RuntimeVariable)
1424 do
1425 var mtype = recv.mtype
1426 var finalizable_type = compiler.mainmodule.finalizable_type
1427 if finalizable_type != null and not mtype.need_anchor and
1428 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1429 add "gc_register_finalizer({recv});"
1430 end
1431 end
1432
1433 # The currently processed module
1434 #
1435 # alias for `compiler.mainmodule`
1436 fun mmodule: MModule do return compiler.mainmodule
1437
1438 # Generate an integer value
1439 fun int_instance(value: Int): RuntimeVariable
1440 do
1441 var t = mmodule.int_type
1442 var res = new RuntimeVariable("{value.to_s}l", t, t)
1443 return res
1444 end
1445
1446 # Generate a char value
1447 fun char_instance(value: Char): RuntimeVariable
1448 do
1449 var t = mmodule.char_type
1450 var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1451 return res
1452 end
1453
1454 # Generate a float value
1455 #
1456 # FIXME pass a Float, not a string
1457 fun float_instance(value: String): RuntimeVariable
1458 do
1459 var t = mmodule.float_type
1460 var res = new RuntimeVariable("{value}", t, t)
1461 return res
1462 end
1463
1464 # Generate an integer value
1465 fun bool_instance(value: Bool): RuntimeVariable
1466 do
1467 var s = if value then "1" else "0"
1468 var res = new RuntimeVariable(s, bool_type, bool_type)
1469 return res
1470 end
1471
1472 # Generate the `null` value
1473 fun null_instance: RuntimeVariable
1474 do
1475 var t = compiler.mainmodule.model.null_type
1476 var res = new RuntimeVariable("((val*)NULL)", t, t)
1477 return res
1478 end
1479
1480 # Generate a string value
1481 fun string_instance(string: String): RuntimeVariable
1482 do
1483 var mtype = mmodule.string_type
1484 var name = self.get_name("varonce")
1485 self.add_decl("static {mtype.ctype} {name};")
1486 var res = self.new_var(mtype)
1487 self.add("if (likely({name}!=NULL)) \{")
1488 self.add("{res} = {name};")
1489 self.add("\} else \{")
1490 var native_mtype = mmodule.native_string_type
1491 var nat = self.new_var(native_mtype)
1492 self.add("{nat} = \"{string.escape_to_c}\";")
1493 var length = self.int_instance(string.length)
1494 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1495 self.add("{name} = {res};")
1496 self.add("\}")
1497 return res
1498 end
1499
1500 fun value_instance(object: Object): RuntimeVariable
1501 do
1502 if object isa Int then
1503 return int_instance(object)
1504 else if object isa Bool then
1505 return bool_instance(object)
1506 else if object isa String then
1507 return string_instance(object)
1508 else
1509 abort
1510 end
1511 end
1512
1513 # Generate an array value
1514 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1515
1516 # Get an instance of a array for a vararg
1517 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1518
1519 # Code generation
1520
1521 # Add a line in the main part of the generated C
1522 fun add(s: String) do self.writer.lines.add(s)
1523
1524 # Add a line in the
1525 # (used for local or global declaration)
1526 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1527
1528 # Request the presence of a global declaration
1529 fun require_declaration(key: String)
1530 do
1531 var reqs = self.writer.file.required_declarations
1532 if reqs.has(key) then return
1533 reqs.add(key)
1534 var node = current_node
1535 if node != null then compiler.requirers_of_declarations[key] = node
1536 end
1537
1538 # Add a declaration in the local-header
1539 # The declaration is ensured to be present once
1540 fun declare_once(s: String)
1541 do
1542 self.compiler.provide_declaration(s, s)
1543 self.require_declaration(s)
1544 end
1545
1546 # Look for a needed .h and .c file for a given module
1547 # This is used for the legacy FFI
1548 fun add_extern(mmodule: MModule)
1549 do
1550 var file = mmodule.location.file.filename
1551 file = file.strip_extension(".nit")
1552 var tryfile = file + ".nit.h"
1553 if tryfile.file_exists then
1554 self.declare_once("#include \"{tryfile.basename("")}\"")
1555 self.compiler.files_to_copy.add(tryfile)
1556 end
1557 tryfile = file + "_nit.h"
1558 if tryfile.file_exists then
1559 self.declare_once("#include \"{tryfile.basename("")}\"")
1560 self.compiler.files_to_copy.add(tryfile)
1561 end
1562
1563 if self.compiler.seen_extern.has(file) then return
1564 self.compiler.seen_extern.add(file)
1565 tryfile = file + ".nit.c"
1566 if not tryfile.file_exists then
1567 tryfile = file + "_nit.c"
1568 if not tryfile.file_exists then return
1569 end
1570 var f = new ExternCFile(tryfile.basename(""), "")
1571 self.compiler.extern_bodies.add(f)
1572 self.compiler.files_to_copy.add(tryfile)
1573 end
1574
1575 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1576 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1577 do
1578 var res = new_var(mtype)
1579 self.add("{res} = {cexpr};")
1580 return res
1581 end
1582
1583 # Generate generic abort
1584 # used by aborts, asserts, casts, etc.
1585 fun add_abort(message: String)
1586 do
1587 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1588 add_raw_abort
1589 end
1590
1591 fun add_raw_abort
1592 do
1593 if self.current_node != null and self.current_node.location.file != null and
1594 self.current_node.location.file.mmodule != null then
1595 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1596 self.require_declaration(f)
1597 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1598 else
1599 self.add("PRINT_ERROR(\"\\n\");")
1600 end
1601 self.add("fatal_exit(1);")
1602 end
1603
1604 # Add a dynamic cast
1605 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1606 do
1607 var res = self.type_test(value, mtype, tag)
1608 self.add("if (unlikely(!{res})) \{")
1609 var cn = self.class_name_string(value)
1610 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1611 self.add_raw_abort
1612 self.add("\}")
1613 end
1614
1615 # Generate a return with the value `s`
1616 fun ret(s: RuntimeVariable)
1617 do
1618 self.assign(self.frame.returnvar.as(not null), s)
1619 self.add("goto {self.frame.returnlabel.as(not null)};")
1620 end
1621
1622 # Compile a statement (if any)
1623 fun stmt(nexpr: nullable AExpr)
1624 do
1625 if nexpr == null then return
1626
1627 var narray = nexpr.comprehension
1628 if narray != null then
1629 var recv = frame.comprehension.as(not null)
1630 var val = expr(nexpr, narray.element_mtype)
1631 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1632 return
1633 end
1634
1635 var old = self.current_node
1636 self.current_node = nexpr
1637 nexpr.stmt(self)
1638 self.current_node = old
1639 end
1640
1641 # Compile an expression an return its result
1642 # `mtype` is the expected return type, pass null if no specific type is expected.
1643 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1644 do
1645 var old = self.current_node
1646 self.current_node = nexpr
1647 var res = nexpr.expr(self).as(not null)
1648 if mtype != null then
1649 mtype = self.anchor(mtype)
1650 res = self.autobox(res, mtype)
1651 end
1652 res = autoadapt(res, nexpr.mtype.as(not null))
1653 var implicit_cast_to = nexpr.implicit_cast_to
1654 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1655 add_cast(res, implicit_cast_to, "auto")
1656 res = autoadapt(res, implicit_cast_to)
1657 end
1658 self.current_node = old
1659 return res
1660 end
1661
1662 # Alias for `self.expr(nexpr, self.bool_type)`
1663 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1664
1665 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1666 fun debug(message: String)
1667 do
1668 var node = self.current_node
1669 if node == null then
1670 print "?: {message}"
1671 else
1672 node.debug(message)
1673 end
1674 self.add("/* DEBUG: {message} */")
1675 end
1676 end
1677
1678 # A C function associated to a Nit method
1679 # Because of customization, a given Nit method can be compiler more that once
1680 abstract class AbstractRuntimeFunction
1681
1682 type COMPILER: AbstractCompiler
1683 type VISITOR: AbstractCompilerVisitor
1684
1685 # The associated Nit method
1686 var mmethoddef: MMethodDef
1687
1688 # The mangled c name of the runtime_function
1689 # Subclasses should redefine `build_c_name` instead
1690 fun c_name: String
1691 do
1692 var res = self.c_name_cache
1693 if res != null then return res
1694 res = self.build_c_name
1695 self.c_name_cache = res
1696 return res
1697 end
1698
1699 # Non cached version of `c_name`
1700 protected fun build_c_name: String is abstract
1701
1702 protected var c_name_cache: nullable String = null is writable
1703
1704 # Implements a call of the runtime_function
1705 # May inline the body or generate a C function call
1706 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1707
1708 # Generate the code for the `AbstractRuntimeFunction`
1709 # Warning: compile more than once compilation makes CC unhappy
1710 fun compile_to_c(compiler: COMPILER) is abstract
1711 end
1712
1713 # A runtime variable hold a runtime value in C.
1714 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1715 #
1716 # 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.
1717 class RuntimeVariable
1718 # The name of the variable in the C code
1719 var name: String
1720
1721 # The static type of the variable (as declard in C)
1722 var mtype: MType
1723
1724 # The current casted type of the variable (as known in Nit)
1725 var mcasttype: MType is writable
1726
1727 # If the variable exaclty a mcasttype?
1728 # false (usual value) means that the variable is a mcasttype or a subtype.
1729 var is_exact: Bool = false is writable
1730
1731 init
1732 do
1733 assert not mtype.need_anchor
1734 assert not mcasttype.need_anchor
1735 end
1736
1737 redef fun to_s do return name
1738
1739 redef fun inspect
1740 do
1741 var exact_str
1742 if self.is_exact then
1743 exact_str = " exact"
1744 else
1745 exact_str = ""
1746 end
1747 var type_str
1748 if self.mtype == self.mcasttype then
1749 type_str = "{mtype}{exact_str}"
1750 else
1751 type_str = "{mtype}({mcasttype}{exact_str})"
1752 end
1753 return "<{name}:{type_str}>"
1754 end
1755 end
1756
1757 # The static context of a visited property in a `AbstractCompilerVisitor`
1758 class StaticFrame
1759
1760 type VISITOR: AbstractCompilerVisitor
1761
1762 # The associated visitor
1763 var visitor: VISITOR
1764
1765 # The executed property.
1766 # A Method in case of a call, an attribute in case of a default initialization.
1767 var mpropdef: MPropDef
1768
1769 # The static type of the receiver
1770 var receiver: MClassType
1771
1772 # Arguments of the method (the first is the receiver)
1773 var arguments: Array[RuntimeVariable]
1774
1775 # The runtime_variable associated to the return (in a function)
1776 var returnvar: nullable RuntimeVariable = null is writable
1777
1778 # The label at the end of the property
1779 var returnlabel: nullable String = null is writable
1780
1781 # Labels associated to a each escapemarks.
1782 # Because of inlinings, escape-marks must be associated to their context (the frame)
1783 private var escapemark_names = new HashMap[EscapeMark, String]
1784
1785 # The array comprehension currently filled, if any
1786 private var comprehension: nullable RuntimeVariable = null
1787 end
1788
1789 redef class MType
1790 # Return the C type associated to a given Nit static type
1791 fun ctype: String do return "val*"
1792
1793 # C type outside of the compiler code and in boxes
1794 fun ctype_extern: String do return "val*"
1795
1796 # Short name of the `ctype` to use in unions
1797 fun ctypename: String do return "val"
1798
1799 # Is the associated C type a primitive one?
1800 #
1801 # ENSURE `result == (ctype != "val*")`
1802 fun is_c_primitive: Bool do return false
1803 end
1804
1805 redef class MClassType
1806
1807 redef var ctype is lazy do
1808 if mclass.name == "Int" then
1809 return "long"
1810 else if mclass.name == "Bool" then
1811 return "short int"
1812 else if mclass.name == "Char" then
1813 return "char"
1814 else if mclass.name == "Float" then
1815 return "double"
1816 else if mclass.name == "NativeString" then
1817 return "char*"
1818 else if mclass.name == "NativeArray" then
1819 return "val*"
1820 else
1821 return "val*"
1822 end
1823 end
1824
1825 redef var is_c_primitive is lazy do return ctype != "val*"
1826
1827 redef fun ctype_extern: String
1828 do
1829 if mclass.kind == extern_kind then
1830 return "void*"
1831 else
1832 return ctype
1833 end
1834 end
1835
1836 redef fun ctypename: String
1837 do
1838 if mclass.name == "Int" then
1839 return "l"
1840 else if mclass.name == "Bool" then
1841 return "s"
1842 else if mclass.name == "Char" then
1843 return "c"
1844 else if mclass.name == "Float" then
1845 return "d"
1846 else if mclass.name == "NativeString" then
1847 return "str"
1848 else if mclass.name == "NativeArray" then
1849 #return "{self.arguments.first.ctype}*"
1850 return "val"
1851 else
1852 return "val"
1853 end
1854 end
1855 end
1856
1857 redef class MPropDef
1858 type VISITOR: AbstractCompilerVisitor
1859 end
1860
1861 redef class MMethodDef
1862 # Can the body be inlined?
1863 fun can_inline(v: VISITOR): Bool
1864 do
1865 if is_abstract then return true
1866 var modelbuilder = v.compiler.modelbuilder
1867 var node = modelbuilder.mpropdef2node(self)
1868 if node isa APropdef then
1869 return node.can_inline
1870 else if node isa AClassdef then
1871 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1872 return true
1873 else
1874 abort
1875 end
1876 end
1877
1878 # Inline the body in another visitor
1879 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1880 do
1881 var modelbuilder = v.compiler.modelbuilder
1882 var val = constant_value
1883 var node = modelbuilder.mpropdef2node(self)
1884
1885 if is_abstract then
1886 var cn = v.class_name_string(arguments.first)
1887 v.current_node = node
1888 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1889 v.add_raw_abort
1890 return null
1891 end
1892
1893 if node isa APropdef then
1894 var oldnode = v.current_node
1895 v.current_node = node
1896 self.compile_parameter_check(v, arguments)
1897 node.compile_to_c(v, self, arguments)
1898 v.current_node = oldnode
1899 else if node isa AClassdef then
1900 var oldnode = v.current_node
1901 v.current_node = node
1902 self.compile_parameter_check(v, arguments)
1903 node.compile_to_c(v, self, arguments)
1904 v.current_node = oldnode
1905 else if val != null then
1906 v.ret(v.value_instance(val))
1907 else
1908 abort
1909 end
1910 return null
1911 end
1912
1913 # Generate type checks in the C code to check covariant parameters
1914 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1915 do
1916 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1917
1918 for i in [0..msignature.arity[ do
1919 # skip test for vararg since the array is instantiated with the correct polymorphic type
1920 if msignature.vararg_rank == i then continue
1921
1922 # skip if the cast is not required
1923 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1924 if not origmtype.need_anchor then continue
1925
1926 # get the parameter type
1927 var mtype = self.msignature.mparameters[i].mtype
1928
1929 # generate the cast
1930 # note that v decides if and how to implements the cast
1931 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1932 v.add_cast(arguments[i+1], mtype, "covariance")
1933 end
1934 end
1935 end
1936
1937 # Node visit
1938
1939 redef class APropdef
1940 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1941 do
1942 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1943 debug("Not yet implemented")
1944 end
1945
1946 fun can_inline: Bool do return true
1947 end
1948
1949 redef class AMethPropdef
1950 redef fun compile_to_c(v, mpropdef, arguments)
1951 do
1952 # Call the implicit super-init
1953 var auto_super_inits = self.auto_super_inits
1954 if auto_super_inits != null then
1955 var args = [arguments.first]
1956 for auto_super_init in auto_super_inits do
1957 assert auto_super_init.mproperty != mpropdef.mproperty
1958 args.clear
1959 for i in [0..auto_super_init.msignature.arity+1[ do
1960 args.add(arguments[i])
1961 end
1962 assert auto_super_init.mproperty != mpropdef.mproperty
1963 v.compile_callsite(auto_super_init, args)
1964 end
1965 end
1966 if auto_super_call then
1967 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1968 end
1969
1970 # Try special compilation
1971 if mpropdef.is_intern then
1972 if compile_intern_to_c(v, mpropdef, arguments) then return
1973 else if mpropdef.is_extern then
1974 if mpropdef.mproperty.is_init then
1975 if compile_externinit_to_c(v, mpropdef, arguments) then return
1976 else
1977 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1978 end
1979 end
1980
1981 # Compile block if any
1982 var n_block = n_block
1983 if n_block != null then
1984 for i in [0..mpropdef.msignature.arity[ do
1985 var variable = self.n_signature.n_params[i].variable.as(not null)
1986 v.assign(v.variable(variable), arguments[i+1])
1987 end
1988 v.stmt(n_block)
1989 return
1990 end
1991
1992 # We have a problem
1993 var cn = v.class_name_string(arguments.first)
1994 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1995 v.add_raw_abort
1996 end
1997
1998 redef fun can_inline
1999 do
2000 if self.auto_super_inits != null then return false
2001 var nblock = self.n_block
2002 if nblock == null then return true
2003 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2004 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2005 return false
2006 end
2007
2008 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2009 do
2010 var pname = mpropdef.mproperty.name
2011 var cname = mpropdef.mclassdef.mclass.name
2012 var ret = mpropdef.msignature.return_mtype
2013 if ret != null then
2014 ret = v.resolve_for(ret, arguments.first)
2015 end
2016 if pname != "==" and pname != "!=" then
2017 v.adapt_signature(mpropdef, arguments)
2018 v.unbox_signature_extern(mpropdef, arguments)
2019 end
2020 if cname == "Int" then
2021 if pname == "output" then
2022 v.add("printf(\"%ld\\n\", {arguments.first});")
2023 return true
2024 else if pname == "object_id" then
2025 v.ret(arguments.first)
2026 return true
2027 else if pname == "+" then
2028 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2029 return true
2030 else if pname == "-" then
2031 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2032 return true
2033 else if pname == "unary -" then
2034 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2035 return true
2036 else if pname == "*" then
2037 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2038 return true
2039 else if pname == "/" then
2040 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2041 return true
2042 else if pname == "%" then
2043 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2044 return true
2045 else if pname == "lshift" then
2046 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2047 return true
2048 else if pname == "rshift" then
2049 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2050 return true
2051 else if pname == "==" then
2052 v.ret(v.equal_test(arguments[0], arguments[1]))
2053 return true
2054 else if pname == "!=" then
2055 var res = v.equal_test(arguments[0], arguments[1])
2056 v.ret(v.new_expr("!{res}", ret.as(not null)))
2057 return true
2058 else if pname == "<" then
2059 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2060 return true
2061 else if pname == ">" then
2062 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2063 return true
2064 else if pname == "<=" then
2065 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2066 return true
2067 else if pname == ">=" then
2068 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2069 return true
2070 else if pname == "to_f" then
2071 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2072 return true
2073 else if pname == "ascii" then
2074 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2075 return true
2076 end
2077 else if cname == "Char" then
2078 if pname == "output" then
2079 v.add("printf(\"%c\", {arguments.first});")
2080 return true
2081 else if pname == "object_id" then
2082 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2083 return true
2084 else if pname == "successor" then
2085 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2086 return true
2087 else if pname == "predecessor" then
2088 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2089 return true
2090 else if pname == "==" then
2091 v.ret(v.equal_test(arguments[0], arguments[1]))
2092 return true
2093 else if pname == "!=" then
2094 var res = v.equal_test(arguments[0], arguments[1])
2095 v.ret(v.new_expr("!{res}", ret.as(not null)))
2096 return true
2097 else if pname == "<" then
2098 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2099 return true
2100 else if pname == ">" then
2101 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2102 return true
2103 else if pname == "<=" then
2104 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2105 return true
2106 else if pname == ">=" then
2107 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2108 return true
2109 else if pname == "to_i" then
2110 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2111 return true
2112 else if pname == "ascii" then
2113 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2114 return true
2115 end
2116 else if cname == "Bool" then
2117 if pname == "output" then
2118 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2119 return true
2120 else if pname == "object_id" then
2121 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2122 return true
2123 else if pname == "==" then
2124 v.ret(v.equal_test(arguments[0], arguments[1]))
2125 return true
2126 else if pname == "!=" then
2127 var res = v.equal_test(arguments[0], arguments[1])
2128 v.ret(v.new_expr("!{res}", ret.as(not null)))
2129 return true
2130 end
2131 else if cname == "Float" then
2132 if pname == "output" then
2133 v.add("printf(\"%f\\n\", {arguments.first});")
2134 return true
2135 else if pname == "object_id" then
2136 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2137 return true
2138 else if pname == "+" then
2139 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2140 return true
2141 else if pname == "-" then
2142 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2143 return true
2144 else if pname == "unary -" then
2145 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2146 return true
2147 else if pname == "succ" then
2148 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2149 return true
2150 else if pname == "prec" then
2151 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2152 return true
2153 else if pname == "*" then
2154 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2155 return true
2156 else if pname == "/" then
2157 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2158 return true
2159 else if pname == "==" then
2160 v.ret(v.equal_test(arguments[0], arguments[1]))
2161 return true
2162 else if pname == "!=" then
2163 var res = v.equal_test(arguments[0], arguments[1])
2164 v.ret(v.new_expr("!{res}", ret.as(not null)))
2165 return true
2166 else if pname == "<" then
2167 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2168 return true
2169 else if pname == ">" then
2170 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2171 return true
2172 else if pname == "<=" then
2173 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2174 return true
2175 else if pname == ">=" then
2176 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2177 return true
2178 else if pname == "to_i" then
2179 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2180 return true
2181 end
2182 else if cname == "NativeString" then
2183 if pname == "[]" then
2184 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2185 return true
2186 else if pname == "[]=" then
2187 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2188 return true
2189 else if pname == "copy_to" then
2190 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2191 return true
2192 else if pname == "atoi" then
2193 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2194 return true
2195 else if pname == "new" then
2196 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2197 return true
2198 end
2199 else if cname == "NativeArray" then
2200 v.native_array_def(pname, ret, arguments)
2201 return true
2202 end
2203 if pname == "exit" then
2204 v.add("exit({arguments[1]});")
2205 return true
2206 else if pname == "sys" then
2207 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2208 return true
2209 else if pname == "calloc_string" then
2210 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2211 return true
2212 else if pname == "calloc_array" then
2213 v.calloc_array(ret.as(not null), arguments)
2214 return true
2215 else if pname == "object_id" then
2216 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2217 return true
2218 else if pname == "is_same_type" then
2219 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2220 return true
2221 else if pname == "is_same_instance" then
2222 v.ret(v.equal_test(arguments[0], arguments[1]))
2223 return true
2224 else if pname == "output_class_name" then
2225 var nat = v.class_name_string(arguments.first)
2226 v.add("printf(\"%s\\n\", {nat});")
2227 return true
2228 else if pname == "native_class_name" then
2229 var nat = v.class_name_string(arguments.first)
2230 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2231 return true
2232 else if pname == "force_garbage_collection" then
2233 v.add("nit_gcollect();")
2234 return true
2235 else if pname == "native_argc" then
2236 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2237 return true
2238 else if pname == "native_argv" then
2239 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2240 return true
2241 end
2242 return false
2243 end
2244
2245 # Compile an extern method
2246 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2247 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2248 do
2249 var externname
2250 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2251 if at != null and at.n_args.length == 1 then
2252 externname = at.arg_as_string(v.compiler.modelbuilder)
2253 if externname == null then return false
2254 else
2255 return false
2256 end
2257 v.add_extern(mpropdef.mclassdef.mmodule)
2258 var res: nullable RuntimeVariable = null
2259 var ret = mpropdef.msignature.return_mtype
2260 if ret != null then
2261 ret = v.resolve_for(ret, arguments.first)
2262 res = v.new_var_extern(ret)
2263 end
2264 v.adapt_signature(mpropdef, arguments)
2265 v.unbox_signature_extern(mpropdef, arguments)
2266
2267 if res == null then
2268 v.add("{externname}({arguments.join(", ")});")
2269 else
2270 v.add("{res} = {externname}({arguments.join(", ")});")
2271 res = v.box_extern(res, ret.as(not null))
2272 v.ret(res)
2273 end
2274 return true
2275 end
2276
2277 # Compile an extern factory
2278 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2279 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2280 do
2281 var externname
2282 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2283 if at != null then
2284 externname = at.arg_as_string(v.compiler.modelbuilder)
2285 if externname == null then return false
2286 else
2287 return false
2288 end
2289 v.add_extern(mpropdef.mclassdef.mmodule)
2290 v.adapt_signature(mpropdef, arguments)
2291 v.unbox_signature_extern(mpropdef, arguments)
2292 var ret = arguments.first.mtype
2293 var res = v.new_var_extern(ret)
2294
2295 arguments.shift
2296
2297 v.add("{res} = {externname}({arguments.join(", ")});")
2298 res = v.box_extern(res, ret)
2299 v.ret(res)
2300 return true
2301 end
2302 end
2303
2304 redef class AAttrPropdef
2305 redef fun can_inline: Bool do return not is_lazy
2306
2307 redef fun compile_to_c(v, mpropdef, arguments)
2308 do
2309 if mpropdef == mreadpropdef then
2310 assert arguments.length == 1
2311 var recv = arguments.first
2312 var res
2313 if is_lazy then
2314 var set
2315 var ret = self.mpropdef.static_mtype
2316 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2317 var guard = self.mlazypropdef.mproperty
2318 if useiset then
2319 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2320 else
2321 set = v.read_attribute(guard, recv)
2322 end
2323 v.add("if(likely({set})) \{")
2324 res = v.read_attribute(self.mpropdef.mproperty, recv)
2325 v.add("\} else \{")
2326
2327 var value = evaluate_expr(v, recv)
2328
2329 v.assign(res, value)
2330 if not useiset then
2331 var true_v = v.bool_instance(true)
2332 v.write_attribute(guard, arguments.first, true_v)
2333 end
2334 v.add("\}")
2335 else
2336 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2337 end
2338 v.assign(v.frame.returnvar.as(not null), res)
2339 else if mpropdef == mwritepropdef then
2340 assert arguments.length == 2
2341 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2342 if is_lazy then
2343 var ret = self.mpropdef.static_mtype
2344 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2345 if not useiset then
2346 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2347 end
2348 end
2349 else
2350 abort
2351 end
2352 end
2353
2354 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2355 do
2356 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2357 end
2358
2359 # Evaluate, store and return the default value of the attribute
2360 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2361 do
2362 var oldnode = v.current_node
2363 v.current_node = self
2364 var old_frame = v.frame
2365 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2366 v.frame = frame
2367
2368 var value
2369 var mtype = self.mpropdef.static_mtype
2370 assert mtype != null
2371
2372 var nexpr = self.n_expr
2373 var nblock = self.n_block
2374 if nexpr != null then
2375 value = v.expr(nexpr, mtype)
2376 else if nblock != null then
2377 value = v.new_var(mtype)
2378 frame.returnvar = value
2379 frame.returnlabel = v.get_name("RET_LABEL")
2380 v.add("\{")
2381 v.stmt(nblock)
2382 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2383 v.add("\}")
2384 else
2385 abort
2386 end
2387
2388 v.write_attribute(self.mpropdef.mproperty, recv, value)
2389
2390 v.frame = old_frame
2391 v.current_node = oldnode
2392
2393 return value
2394 end
2395
2396 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2397 do
2398 var nexpr = self.n_expr
2399 if nexpr != null then return
2400
2401 var oldnode = v.current_node
2402 v.current_node = self
2403 var old_frame = v.frame
2404 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2405 v.frame = frame
2406 # Force read to check the initialization
2407 v.read_attribute(self.mpropdef.mproperty, recv)
2408 v.frame = old_frame
2409 v.current_node = oldnode
2410 end
2411 end
2412
2413 redef class AClassdef
2414 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2415 do
2416 if mpropdef == self.mfree_init then
2417 assert mpropdef.mproperty.is_root_init
2418 assert arguments.length == 1
2419 if not mpropdef.is_intro then
2420 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2421 end
2422 return
2423 else
2424 abort
2425 end
2426 end
2427 end
2428
2429 redef class AExpr
2430 # Try to compile self as an expression
2431 # Do not call this method directly, use `v.expr` instead
2432 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2433 do
2434 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2435 var mtype = self.mtype
2436 if mtype == null then
2437 return null
2438 else
2439 var res = v.new_var(mtype)
2440 v.add("/* {res} = NOT YET {class_name} */")
2441 return res
2442 end
2443 end
2444
2445 # Try to compile self as a statement
2446 # Do not call this method directly, use `v.stmt` instead
2447 private fun stmt(v: AbstractCompilerVisitor)
2448 do
2449 expr(v)
2450 end
2451 end
2452
2453 redef class ABlockExpr
2454 redef fun stmt(v)
2455 do
2456 for e in self.n_expr do v.stmt(e)
2457 end
2458 redef fun expr(v)
2459 do
2460 var last = self.n_expr.last
2461 for e in self.n_expr do
2462 if e == last then break
2463 v.stmt(e)
2464 end
2465 return v.expr(last, null)
2466 end
2467 end
2468
2469 redef class AVardeclExpr
2470 redef fun stmt(v)
2471 do
2472 var variable = self.variable.as(not null)
2473 var ne = self.n_expr
2474 if ne != null then
2475 var i = v.expr(ne, variable.declared_type)
2476 v.assign(v.variable(variable), i)
2477 end
2478 end
2479 end
2480
2481 redef class AVarExpr
2482 redef fun expr(v)
2483 do
2484 var res = v.variable(self.variable.as(not null))
2485 var mtype = self.mtype.as(not null)
2486 return v.autoadapt(res, mtype)
2487 end
2488 end
2489
2490 redef class AVarAssignExpr
2491 redef fun expr(v)
2492 do
2493 var variable = self.variable.as(not null)
2494 var i = v.expr(self.n_value, variable.declared_type)
2495 v.assign(v.variable(variable), i)
2496 return i
2497 end
2498 end
2499
2500 redef class AVarReassignExpr
2501 redef fun stmt(v)
2502 do
2503 var variable = self.variable.as(not null)
2504 var vari = v.variable(variable)
2505 var value = v.expr(self.n_value, variable.declared_type)
2506 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2507 assert res != null
2508 v.assign(v.variable(variable), res)
2509 end
2510 end
2511
2512 redef class ASelfExpr
2513 redef fun expr(v) do return v.frame.arguments.first
2514 end
2515
2516 redef class AEscapeExpr
2517 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2518 end
2519
2520 redef class AReturnExpr
2521 redef fun stmt(v)
2522 do
2523 var nexpr = self.n_expr
2524 if nexpr != null then
2525 var returnvar = v.frame.returnvar.as(not null)
2526 var i = v.expr(nexpr, returnvar.mtype)
2527 v.assign(returnvar, i)
2528 end
2529 v.add("goto {v.frame.returnlabel.as(not null)};")
2530 end
2531 end
2532
2533 redef class AAbortExpr
2534 redef fun stmt(v) do v.add_abort("Aborted")
2535 end
2536
2537 redef class AIfExpr
2538 redef fun stmt(v)
2539 do
2540 var cond = v.expr_bool(self.n_expr)
2541 v.add("if ({cond})\{")
2542 v.stmt(self.n_then)
2543 v.add("\} else \{")
2544 v.stmt(self.n_else)
2545 v.add("\}")
2546 end
2547
2548 redef fun expr(v)
2549 do
2550 var res = v.new_var(self.mtype.as(not null))
2551 var cond = v.expr_bool(self.n_expr)
2552 v.add("if ({cond})\{")
2553 v.assign(res, v.expr(self.n_then.as(not null), null))
2554 v.add("\} else \{")
2555 v.assign(res, v.expr(self.n_else.as(not null), null))
2556 v.add("\}")
2557 return res
2558 end
2559 end
2560
2561 redef class AIfexprExpr
2562 redef fun expr(v)
2563 do
2564 var res = v.new_var(self.mtype.as(not null))
2565 var cond = v.expr_bool(self.n_expr)
2566 v.add("if ({cond})\{")
2567 v.assign(res, v.expr(self.n_then, null))
2568 v.add("\} else \{")
2569 v.assign(res, v.expr(self.n_else, null))
2570 v.add("\}")
2571 return res
2572 end
2573 end
2574
2575 redef class ADoExpr
2576 redef fun stmt(v)
2577 do
2578 v.stmt(self.n_block)
2579 v.add_escape_label(break_mark)
2580 end
2581 end
2582
2583 redef class AWhileExpr
2584 redef fun stmt(v)
2585 do
2586 v.add("for(;;) \{")
2587 var cond = v.expr_bool(self.n_expr)
2588 v.add("if (!{cond}) break;")
2589 v.stmt(self.n_block)
2590 v.add_escape_label(continue_mark)
2591 v.add("\}")
2592 v.add_escape_label(break_mark)
2593 end
2594 end
2595
2596 redef class ALoopExpr
2597 redef fun stmt(v)
2598 do
2599 v.add("for(;;) \{")
2600 v.stmt(self.n_block)
2601 v.add_escape_label(continue_mark)
2602 v.add("\}")
2603 v.add_escape_label(break_mark)
2604 end
2605 end
2606
2607 redef class AForExpr
2608 redef fun stmt(v)
2609 do
2610 var cl = v.expr(self.n_expr, null)
2611 var it_meth = self.method_iterator
2612 assert it_meth != null
2613 var it = v.compile_callsite(it_meth, [cl])
2614 assert it != null
2615 v.add("for(;;) \{")
2616 var isok_meth = self.method_is_ok
2617 assert isok_meth != null
2618 var ok = v.compile_callsite(isok_meth, [it])
2619 assert ok != null
2620 v.add("if(!{ok}) break;")
2621 if self.variables.length == 1 then
2622 var item_meth = self.method_item
2623 assert item_meth != null
2624 var i = v.compile_callsite(item_meth, [it])
2625 assert i != null
2626 v.assign(v.variable(variables.first), i)
2627 else if self.variables.length == 2 then
2628 var key_meth = self.method_key
2629 assert key_meth != null
2630 var i = v.compile_callsite(key_meth, [it])
2631 assert i != null
2632 v.assign(v.variable(variables[0]), i)
2633 var item_meth = self.method_item
2634 assert item_meth != null
2635 i = v.compile_callsite(item_meth, [it])
2636 assert i != null
2637 v.assign(v.variable(variables[1]), i)
2638 else
2639 abort
2640 end
2641 v.stmt(self.n_block)
2642 v.add_escape_label(continue_mark)
2643 var next_meth = self.method_next
2644 assert next_meth != null
2645 v.compile_callsite(next_meth, [it])
2646 v.add("\}")
2647 v.add_escape_label(break_mark)
2648
2649 var method_finish = self.method_finish
2650 if method_finish != null then
2651 # TODO: Find a way to call this also in long escape (e.g. return)
2652 v.compile_callsite(method_finish, [it])
2653 end
2654 end
2655 end
2656
2657 redef class AAssertExpr
2658 redef fun stmt(v)
2659 do
2660 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2661
2662 var cond = v.expr_bool(self.n_expr)
2663 v.add("if (unlikely(!{cond})) \{")
2664 v.stmt(self.n_else)
2665 var nid = self.n_id
2666 if nid != null then
2667 v.add_abort("Assert '{nid.text}' failed")
2668 else
2669 v.add_abort("Assert failed")
2670 end
2671 v.add("\}")
2672 end
2673 end
2674
2675 redef class AOrExpr
2676 redef fun expr(v)
2677 do
2678 var res = v.new_var(self.mtype.as(not null))
2679 var i1 = v.expr_bool(self.n_expr)
2680 v.add("if ({i1}) \{")
2681 v.add("{res} = 1;")
2682 v.add("\} else \{")
2683 var i2 = v.expr_bool(self.n_expr2)
2684 v.add("{res} = {i2};")
2685 v.add("\}")
2686 return res
2687 end
2688 end
2689
2690 redef class AImpliesExpr
2691 redef fun expr(v)
2692 do
2693 var res = v.new_var(self.mtype.as(not null))
2694 var i1 = v.expr_bool(self.n_expr)
2695 v.add("if (!{i1}) \{")
2696 v.add("{res} = 1;")
2697 v.add("\} else \{")
2698 var i2 = v.expr_bool(self.n_expr2)
2699 v.add("{res} = {i2};")
2700 v.add("\}")
2701 return res
2702 end
2703 end
2704
2705 redef class AAndExpr
2706 redef fun expr(v)
2707 do
2708 var res = v.new_var(self.mtype.as(not null))
2709 var i1 = v.expr_bool(self.n_expr)
2710 v.add("if (!{i1}) \{")
2711 v.add("{res} = 0;")
2712 v.add("\} else \{")
2713 var i2 = v.expr_bool(self.n_expr2)
2714 v.add("{res} = {i2};")
2715 v.add("\}")
2716 return res
2717 end
2718 end
2719
2720 redef class ANotExpr
2721 redef fun expr(v)
2722 do
2723 var cond = v.expr_bool(self.n_expr)
2724 return v.new_expr("!{cond}", self.mtype.as(not null))
2725 end
2726 end
2727
2728 redef class AOrElseExpr
2729 redef fun expr(v)
2730 do
2731 var res = v.new_var(self.mtype.as(not null))
2732 var i1 = v.expr(self.n_expr, null)
2733 v.add("if ({i1}!=NULL) \{")
2734 v.assign(res, i1)
2735 v.add("\} else \{")
2736 var i2 = v.expr(self.n_expr2, null)
2737 v.assign(res, i2)
2738 v.add("\}")
2739 return res
2740 end
2741 end
2742
2743 redef class AIntExpr
2744 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2745 end
2746
2747 redef class AFloatExpr
2748 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2749 end
2750
2751 redef class ACharExpr
2752 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2753 end
2754
2755 redef class AArrayExpr
2756 redef fun expr(v)
2757 do
2758 var mtype = self.element_mtype.as(not null)
2759 var array = new Array[RuntimeVariable]
2760 var res = v.array_instance(array, mtype)
2761
2762 var old_comprehension = v.frame.comprehension
2763 v.frame.comprehension = res
2764 for nexpr in self.n_exprs do
2765 v.stmt(nexpr)
2766 end
2767 v.frame.comprehension = old_comprehension
2768
2769 return res
2770 end
2771 end
2772
2773 redef class AStringFormExpr
2774 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2775 end
2776
2777 redef class ASuperstringExpr
2778 redef fun expr(v)
2779 do
2780 var type_string = mtype.as(not null)
2781
2782 # Collect elements of the superstring
2783 var array = new Array[AExpr]
2784 for ne in self.n_exprs do
2785 # Drop literal empty string.
2786 # They appears in things like "{a}" that is ["", a, ""]
2787 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2788 array.add(ne)
2789 end
2790
2791 # Store the allocated native array in a static variable
2792 # For reusing later
2793 var varonce = v.get_name("varonce")
2794 v.add("if (unlikely({varonce}==NULL)) \{")
2795
2796 # The native array that will contains the elements to_s-ized.
2797 # For fast concatenation.
2798 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2799
2800 v.add_decl("static {a.mtype.ctype} {varonce};")
2801
2802 # Pre-fill the array with the literal string parts.
2803 # So they do not need to be filled again when reused
2804 for i in [0..array.length[ do
2805 var ne = array[i]
2806 if not ne isa AStringFormExpr then continue
2807 var e = v.expr(ne, null)
2808 v.native_array_set(a, i, e)
2809 end
2810
2811 v.add("\} else \{")
2812 # Take the native-array from the store.
2813 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2814 # WARNING: not thread safe! (FIXME?)
2815 v.add("{a} = {varonce};")
2816 v.add("{varonce} = NULL;")
2817 v.add("\}")
2818
2819 # Stringify the elements and put them in the native array
2820 var to_s_method = v.get_property("to_s", v.object_type)
2821 for i in [0..array.length[ do
2822 var ne = array[i]
2823 if ne isa AStringFormExpr then continue
2824 var e = v.expr(ne, null)
2825 # Skip the `to_s` if the element is already a String
2826 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
2827 e = v.send(to_s_method, [e]).as(not null)
2828 end
2829 v.native_array_set(a, i, e)
2830 end
2831
2832 # Fast join the native string to get the result
2833 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
2834
2835 # We finish to work with the native array,
2836 # so store it so that it can be reused
2837 v.add("{varonce} = {a};")
2838 return res
2839 end
2840 end
2841
2842 redef class ACrangeExpr
2843 redef fun expr(v)
2844 do
2845 var i1 = v.expr(self.n_expr, null)
2846 var i2 = v.expr(self.n_expr2, null)
2847 var mtype = self.mtype.as(MClassType)
2848 var res = v.init_instance(mtype)
2849 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2850 return res
2851 end
2852 end
2853
2854 redef class AOrangeExpr
2855 redef fun expr(v)
2856 do
2857 var i1 = v.expr(self.n_expr, null)
2858 var i2 = v.expr(self.n_expr2, null)
2859 var mtype = self.mtype.as(MClassType)
2860 var res = v.init_instance(mtype)
2861 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2862 return res
2863 end
2864 end
2865
2866 redef class ATrueExpr
2867 redef fun expr(v) do return v.bool_instance(true)
2868 end
2869
2870 redef class AFalseExpr
2871 redef fun expr(v) do return v.bool_instance(false)
2872 end
2873
2874 redef class ANullExpr
2875 redef fun expr(v) do return v.null_instance
2876 end
2877
2878 redef class AIsaExpr
2879 redef fun expr(v)
2880 do
2881 var i = v.expr(self.n_expr, null)
2882 return v.type_test(i, self.cast_type.as(not null), "isa")
2883 end
2884 end
2885
2886 redef class AAsCastExpr
2887 redef fun expr(v)
2888 do
2889 var i = v.expr(self.n_expr, null)
2890 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2891
2892 v.add_cast(i, self.mtype.as(not null), "as")
2893 return i
2894 end
2895 end
2896
2897 redef class AAsNotnullExpr
2898 redef fun expr(v)
2899 do
2900 var i = v.expr(self.n_expr, null)
2901 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2902
2903 if i.mtype.is_c_primitive then return i
2904
2905 v.add("if (unlikely({i} == NULL)) \{")
2906 v.add_abort("Cast failed")
2907 v.add("\}")
2908 return i
2909 end
2910 end
2911
2912 redef class AParExpr
2913 redef fun expr(v) do return v.expr(self.n_expr, null)
2914 end
2915
2916 redef class AOnceExpr
2917 redef fun expr(v)
2918 do
2919 var mtype = self.mtype.as(not null)
2920 var name = v.get_name("varonce")
2921 var guard = v.get_name(name + "_guard")
2922 v.add_decl("static {mtype.ctype} {name};")
2923 v.add_decl("static int {guard};")
2924 var res = v.new_var(mtype)
2925 v.add("if (likely({guard})) \{")
2926 v.add("{res} = {name};")
2927 v.add("\} else \{")
2928 var i = v.expr(self.n_expr, mtype)
2929 v.add("{res} = {i};")
2930 v.add("{name} = {res};")
2931 v.add("{guard} = 1;")
2932 v.add("\}")
2933 return res
2934 end
2935 end
2936
2937 redef class ASendExpr
2938 redef fun expr(v)
2939 do
2940 var recv = v.expr(self.n_expr, null)
2941 var callsite = self.callsite.as(not null)
2942 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2943 return v.compile_callsite(callsite, args)
2944 end
2945 end
2946
2947 redef class ASendReassignFormExpr
2948 redef fun stmt(v)
2949 do
2950 var recv = v.expr(self.n_expr, null)
2951 var callsite = self.callsite.as(not null)
2952 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2953
2954 var value = v.expr(self.n_value, null)
2955
2956 var left = v.compile_callsite(callsite, args)
2957 assert left != null
2958
2959 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2960 assert res != null
2961
2962 args.add(res)
2963 v.compile_callsite(self.write_callsite.as(not null), args)
2964 end
2965 end
2966
2967 redef class ASuperExpr
2968 redef fun expr(v)
2969 do
2970 var recv = v.frame.arguments.first
2971
2972 var callsite = self.callsite
2973 if callsite != null then
2974 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2975
2976 # Add additional arguments for the super init call
2977 if args.length == 1 then
2978 for i in [0..callsite.msignature.arity[ do
2979 args.add(v.frame.arguments[i+1])
2980 end
2981 end
2982 # Super init call
2983 var res = v.compile_callsite(callsite, args)
2984 return res
2985 end
2986
2987 var mpropdef = self.mpropdef.as(not null)
2988 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2989 if args.length == 1 then
2990 args = v.frame.arguments
2991 end
2992
2993 # stantard call-next-method
2994 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
2995 end
2996 end
2997
2998 redef class ANewExpr
2999 redef fun expr(v)
3000 do
3001 var mtype = self.recvtype
3002 assert mtype != null
3003
3004 if mtype.mclass.name == "NativeArray" then
3005 assert self.n_args.n_exprs.length == 1
3006 var l = v.expr(self.n_args.n_exprs.first, null)
3007 assert mtype isa MGenericType
3008 var elttype = mtype.arguments.first
3009 return v.native_array_instance(elttype, l)
3010 end
3011
3012 var recv = v.init_instance_or_extern(mtype)
3013
3014 var callsite = self.callsite.as(not null)
3015 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
3016 var res2 = v.compile_callsite(callsite, args)
3017 if res2 != null then
3018 #self.debug("got {res2} from {mproperty}. drop {recv}")
3019 return res2
3020 end
3021 return recv
3022 end
3023 end
3024
3025 redef class AAttrExpr
3026 redef fun expr(v)
3027 do
3028 var recv = v.expr(self.n_expr, null)
3029 var mproperty = self.mproperty.as(not null)
3030 return v.read_attribute(mproperty, recv)
3031 end
3032 end
3033
3034 redef class AAttrAssignExpr
3035 redef fun expr(v)
3036 do
3037 var recv = v.expr(self.n_expr, null)
3038 var i = v.expr(self.n_value, null)
3039 var mproperty = self.mproperty.as(not null)
3040 v.write_attribute(mproperty, recv, i)
3041 return i
3042 end
3043 end
3044
3045 redef class AAttrReassignExpr
3046 redef fun stmt(v)
3047 do
3048 var recv = v.expr(self.n_expr, null)
3049 var value = v.expr(self.n_value, null)
3050 var mproperty = self.mproperty.as(not null)
3051 var attr = v.read_attribute(mproperty, recv)
3052 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3053 assert res != null
3054 v.write_attribute(mproperty, recv, res)
3055 end
3056 end
3057
3058 redef class AIssetAttrExpr
3059 redef fun expr(v)
3060 do
3061 var recv = v.expr(self.n_expr, null)
3062 var mproperty = self.mproperty.as(not null)
3063 return v.isset_attribute(mproperty, recv)
3064 end
3065 end
3066
3067 redef class ADebugTypeExpr
3068 redef fun stmt(v)
3069 do
3070 # do nothing
3071 end
3072 end
3073
3074 # Utils
3075
3076 redef class Array[E]
3077 # Return a new `Array` with the elements only contened in self and not in `o`
3078 fun -(o: Array[E]): Array[E] do
3079 var res = new Array[E]
3080 for e in self do if not o.has(e) then res.add(e)
3081 return res
3082 end
3083 end
3084
3085 redef class MModule
3086 # All `MProperty` associated to all `MClassDef` of `mclass`
3087 fun properties(mclass: MClass): Set[MProperty] do
3088 if not self.properties_cache.has_key(mclass) then
3089 var properties = new HashSet[MProperty]
3090 var parents = new Array[MClass]
3091 if self.flatten_mclass_hierarchy.has(mclass) then
3092 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3093 end
3094 for parent in parents do
3095 properties.add_all(self.properties(parent))
3096 end
3097 for mclassdef in mclass.mclassdefs do
3098 if not self.in_importation <= mclassdef.mmodule then continue
3099 for mprop in mclassdef.intro_mproperties do
3100 properties.add(mprop)
3101 end
3102 end
3103 self.properties_cache[mclass] = properties
3104 end
3105 return properties_cache[mclass]
3106 end
3107 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3108
3109 # Write FFI and nitni results to file
3110 fun finalize_ffi(c: AbstractCompiler) do end
3111
3112 # Give requided addinional system libraries (as given to LD_LIBS)
3113 # Note: can return null instead of an empty set
3114 fun collect_linker_libs: nullable Array[String] do return null
3115 end
3116
3117 # Create a tool context to handle options and paths
3118 var toolcontext = new ToolContext
3119
3120 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3121
3122 # We do not add other options, so process them now!
3123 toolcontext.process_options(args)
3124
3125 # We need a model to collect stufs
3126 var model = new Model
3127 # An a model builder to parse files
3128 var modelbuilder = new ModelBuilder(model, toolcontext)
3129
3130 var arguments = toolcontext.option_context.rest
3131 if arguments.length > 1 and toolcontext.opt_output.value != null then
3132 print "Error: --output needs a single source file. Do you prefer --dir?"
3133 exit 1
3134 end
3135
3136 # Here we load an process all modules passed on the command line
3137 var mmodules = modelbuilder.parse(arguments)
3138
3139 if mmodules.is_empty then return
3140 modelbuilder.run_phases
3141
3142 for mmodule in mmodules do
3143 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3144 var ms = [mmodule]
3145 toolcontext.run_global_phases(ms)
3146 end