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