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