model: intro `MModule::first_real_mmodule` to get the first non-fictive module
[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 == "new" then
2201 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2202 return true
2203 end
2204 else if cname == "NativeArray" then
2205 v.native_array_def(pname, ret, arguments)
2206 return true
2207 end
2208 if pname == "exit" then
2209 v.add("exit({arguments[1]});")
2210 return true
2211 else if pname == "sys" then
2212 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2213 return true
2214 else if pname == "calloc_string" then
2215 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2216 return true
2217 else if pname == "calloc_array" then
2218 v.calloc_array(ret.as(not null), arguments)
2219 return true
2220 else if pname == "object_id" then
2221 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2222 return true
2223 else if pname == "is_same_type" then
2224 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2225 return true
2226 else if pname == "is_same_instance" then
2227 v.ret(v.equal_test(arguments[0], arguments[1]))
2228 return true
2229 else if pname == "output_class_name" then
2230 var nat = v.class_name_string(arguments.first)
2231 v.add("printf(\"%s\\n\", {nat});")
2232 return true
2233 else if pname == "native_class_name" then
2234 var nat = v.class_name_string(arguments.first)
2235 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2236 return true
2237 else if pname == "force_garbage_collection" then
2238 v.add("nit_gcollect();")
2239 return true
2240 else if pname == "native_argc" then
2241 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2242 return true
2243 else if pname == "native_argv" then
2244 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2245 return true
2246 end
2247 return false
2248 end
2249
2250 # Compile an extern method
2251 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2252 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2253 do
2254 var externname
2255 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2256 if at != null and at.n_args.length == 1 then
2257 externname = at.arg_as_string(v.compiler.modelbuilder)
2258 if externname == null then return false
2259 else
2260 return false
2261 end
2262 v.add_extern(mpropdef.mclassdef.mmodule)
2263 var res: nullable RuntimeVariable = null
2264 var ret = mpropdef.msignature.return_mtype
2265 if ret != null then
2266 ret = v.resolve_for(ret, arguments.first)
2267 res = v.new_var_extern(ret)
2268 end
2269 v.adapt_signature(mpropdef, arguments)
2270 v.unbox_signature_extern(mpropdef, arguments)
2271
2272 if res == null then
2273 v.add("{externname}({arguments.join(", ")});")
2274 else
2275 v.add("{res} = {externname}({arguments.join(", ")});")
2276 res = v.box_extern(res, ret.as(not null))
2277 v.ret(res)
2278 end
2279 return true
2280 end
2281
2282 # Compile an extern factory
2283 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2284 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2285 do
2286 var externname
2287 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2288 if at != null then
2289 externname = at.arg_as_string(v.compiler.modelbuilder)
2290 if externname == null then return false
2291 else
2292 return false
2293 end
2294 v.add_extern(mpropdef.mclassdef.mmodule)
2295 v.adapt_signature(mpropdef, arguments)
2296 v.unbox_signature_extern(mpropdef, arguments)
2297 var ret = arguments.first.mtype
2298 var res = v.new_var_extern(ret)
2299
2300 arguments.shift
2301
2302 v.add("{res} = {externname}({arguments.join(", ")});")
2303 res = v.box_extern(res, ret)
2304 v.ret(res)
2305 return true
2306 end
2307 end
2308
2309 redef class AAttrPropdef
2310 redef fun can_inline: Bool do return not is_lazy
2311
2312 redef fun compile_to_c(v, mpropdef, arguments)
2313 do
2314 if mpropdef == mreadpropdef then
2315 assert arguments.length == 1
2316 var recv = arguments.first
2317 var res
2318 if is_lazy then
2319 var set
2320 var ret = self.mpropdef.static_mtype
2321 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2322 var guard = self.mlazypropdef.mproperty
2323 if useiset then
2324 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2325 else
2326 set = v.read_attribute(guard, recv)
2327 end
2328 v.add("if(likely({set})) \{")
2329 res = v.read_attribute(self.mpropdef.mproperty, recv)
2330 v.add("\} else \{")
2331
2332 var value = evaluate_expr(v, recv)
2333
2334 v.assign(res, value)
2335 if not useiset then
2336 var true_v = v.bool_instance(true)
2337 v.write_attribute(guard, arguments.first, true_v)
2338 end
2339 v.add("\}")
2340 else
2341 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2342 end
2343 v.assign(v.frame.returnvar.as(not null), res)
2344 else if mpropdef == mwritepropdef then
2345 assert arguments.length == 2
2346 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2347 if is_lazy then
2348 var ret = self.mpropdef.static_mtype
2349 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2350 if not useiset then
2351 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2352 end
2353 end
2354 else
2355 abort
2356 end
2357 end
2358
2359 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2360 do
2361 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2362 end
2363
2364 # Evaluate, store and return the default value of the attribute
2365 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2366 do
2367 var oldnode = v.current_node
2368 v.current_node = self
2369 var old_frame = v.frame
2370 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2371 v.frame = frame
2372
2373 var value
2374 var mtype = self.mpropdef.static_mtype
2375 assert mtype != null
2376
2377 var nexpr = self.n_expr
2378 var nblock = self.n_block
2379 if nexpr != null then
2380 value = v.expr(nexpr, mtype)
2381 else if nblock != null then
2382 value = v.new_var(mtype)
2383 frame.returnvar = value
2384 frame.returnlabel = v.get_name("RET_LABEL")
2385 v.add("\{")
2386 v.stmt(nblock)
2387 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2388 v.add("\}")
2389 else
2390 abort
2391 end
2392
2393 v.write_attribute(self.mpropdef.mproperty, recv, value)
2394
2395 v.frame = old_frame
2396 v.current_node = oldnode
2397
2398 return value
2399 end
2400
2401 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2402 do
2403 var nexpr = self.n_expr
2404 if nexpr != null then return
2405
2406 var oldnode = v.current_node
2407 v.current_node = self
2408 var old_frame = v.frame
2409 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2410 v.frame = frame
2411 # Force read to check the initialization
2412 v.read_attribute(self.mpropdef.mproperty, recv)
2413 v.frame = old_frame
2414 v.current_node = oldnode
2415 end
2416 end
2417
2418 redef class AClassdef
2419 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2420 do
2421 if mpropdef == self.mfree_init then
2422 assert mpropdef.mproperty.is_root_init
2423 assert arguments.length == 1
2424 if not mpropdef.is_intro then
2425 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2426 end
2427 return
2428 else
2429 abort
2430 end
2431 end
2432 end
2433
2434 redef class AExpr
2435 # Try to compile self as an expression
2436 # Do not call this method directly, use `v.expr` instead
2437 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2438 do
2439 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2440 var mtype = self.mtype
2441 if mtype == null then
2442 return null
2443 else
2444 var res = v.new_var(mtype)
2445 v.add("/* {res} = NOT YET {class_name} */")
2446 return res
2447 end
2448 end
2449
2450 # Try to compile self as a statement
2451 # Do not call this method directly, use `v.stmt` instead
2452 private fun stmt(v: AbstractCompilerVisitor)
2453 do
2454 expr(v)
2455 end
2456 end
2457
2458 redef class ABlockExpr
2459 redef fun stmt(v)
2460 do
2461 for e in self.n_expr do v.stmt(e)
2462 end
2463 redef fun expr(v)
2464 do
2465 var last = self.n_expr.last
2466 for e in self.n_expr do
2467 if e == last then break
2468 v.stmt(e)
2469 end
2470 return v.expr(last, null)
2471 end
2472 end
2473
2474 redef class AVardeclExpr
2475 redef fun stmt(v)
2476 do
2477 var variable = self.variable.as(not null)
2478 var ne = self.n_expr
2479 if ne != null then
2480 var i = v.expr(ne, variable.declared_type)
2481 v.assign(v.variable(variable), i)
2482 end
2483 end
2484 end
2485
2486 redef class AVarExpr
2487 redef fun expr(v)
2488 do
2489 var res = v.variable(self.variable.as(not null))
2490 var mtype = self.mtype.as(not null)
2491 return v.autoadapt(res, mtype)
2492 end
2493 end
2494
2495 redef class AVarAssignExpr
2496 redef fun expr(v)
2497 do
2498 var variable = self.variable.as(not null)
2499 var i = v.expr(self.n_value, variable.declared_type)
2500 v.assign(v.variable(variable), i)
2501 return i
2502 end
2503 end
2504
2505 redef class AVarReassignExpr
2506 redef fun stmt(v)
2507 do
2508 var variable = self.variable.as(not null)
2509 var vari = v.variable(variable)
2510 var value = v.expr(self.n_value, variable.declared_type)
2511 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2512 assert res != null
2513 v.assign(v.variable(variable), res)
2514 end
2515 end
2516
2517 redef class ASelfExpr
2518 redef fun expr(v) do return v.frame.arguments.first
2519 end
2520
2521 redef class AEscapeExpr
2522 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2523 end
2524
2525 redef class AReturnExpr
2526 redef fun stmt(v)
2527 do
2528 var nexpr = self.n_expr
2529 if nexpr != null then
2530 var returnvar = v.frame.returnvar.as(not null)
2531 var i = v.expr(nexpr, returnvar.mtype)
2532 v.assign(returnvar, i)
2533 end
2534 v.add("goto {v.frame.returnlabel.as(not null)};")
2535 end
2536 end
2537
2538 redef class AAbortExpr
2539 redef fun stmt(v) do v.add_abort("Aborted")
2540 end
2541
2542 redef class AIfExpr
2543 redef fun stmt(v)
2544 do
2545 var cond = v.expr_bool(self.n_expr)
2546 v.add("if ({cond})\{")
2547 v.stmt(self.n_then)
2548 v.add("\} else \{")
2549 v.stmt(self.n_else)
2550 v.add("\}")
2551 end
2552
2553 redef fun expr(v)
2554 do
2555 var res = v.new_var(self.mtype.as(not null))
2556 var cond = v.expr_bool(self.n_expr)
2557 v.add("if ({cond})\{")
2558 v.assign(res, v.expr(self.n_then.as(not null), null))
2559 v.add("\} else \{")
2560 v.assign(res, v.expr(self.n_else.as(not null), null))
2561 v.add("\}")
2562 return res
2563 end
2564 end
2565
2566 redef class AIfexprExpr
2567 redef fun expr(v)
2568 do
2569 var res = v.new_var(self.mtype.as(not null))
2570 var cond = v.expr_bool(self.n_expr)
2571 v.add("if ({cond})\{")
2572 v.assign(res, v.expr(self.n_then, null))
2573 v.add("\} else \{")
2574 v.assign(res, v.expr(self.n_else, null))
2575 v.add("\}")
2576 return res
2577 end
2578 end
2579
2580 redef class ADoExpr
2581 redef fun stmt(v)
2582 do
2583 v.stmt(self.n_block)
2584 v.add_escape_label(break_mark)
2585 end
2586 end
2587
2588 redef class AWhileExpr
2589 redef fun stmt(v)
2590 do
2591 v.add("for(;;) \{")
2592 var cond = v.expr_bool(self.n_expr)
2593 v.add("if (!{cond}) break;")
2594 v.stmt(self.n_block)
2595 v.add_escape_label(continue_mark)
2596 v.add("\}")
2597 v.add_escape_label(break_mark)
2598 end
2599 end
2600
2601 redef class ALoopExpr
2602 redef fun stmt(v)
2603 do
2604 v.add("for(;;) \{")
2605 v.stmt(self.n_block)
2606 v.add_escape_label(continue_mark)
2607 v.add("\}")
2608 v.add_escape_label(break_mark)
2609 end
2610 end
2611
2612 redef class AForExpr
2613 redef fun stmt(v)
2614 do
2615 var cl = v.expr(self.n_expr, null)
2616 var it_meth = self.method_iterator
2617 assert it_meth != null
2618 var it = v.compile_callsite(it_meth, [cl])
2619 assert it != null
2620 v.add("for(;;) \{")
2621 var isok_meth = self.method_is_ok
2622 assert isok_meth != null
2623 var ok = v.compile_callsite(isok_meth, [it])
2624 assert ok != null
2625 v.add("if(!{ok}) break;")
2626 if self.variables.length == 1 then
2627 var item_meth = self.method_item
2628 assert item_meth != null
2629 var i = v.compile_callsite(item_meth, [it])
2630 assert i != null
2631 v.assign(v.variable(variables.first), i)
2632 else if self.variables.length == 2 then
2633 var key_meth = self.method_key
2634 assert key_meth != null
2635 var i = v.compile_callsite(key_meth, [it])
2636 assert i != null
2637 v.assign(v.variable(variables[0]), i)
2638 var item_meth = self.method_item
2639 assert item_meth != null
2640 i = v.compile_callsite(item_meth, [it])
2641 assert i != null
2642 v.assign(v.variable(variables[1]), i)
2643 else
2644 abort
2645 end
2646 v.stmt(self.n_block)
2647 v.add_escape_label(continue_mark)
2648 var next_meth = self.method_next
2649 assert next_meth != null
2650 v.compile_callsite(next_meth, [it])
2651 v.add("\}")
2652 v.add_escape_label(break_mark)
2653
2654 var method_finish = self.method_finish
2655 if method_finish != null then
2656 # TODO: Find a way to call this also in long escape (e.g. return)
2657 v.compile_callsite(method_finish, [it])
2658 end
2659 end
2660 end
2661
2662 redef class AAssertExpr
2663 redef fun stmt(v)
2664 do
2665 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2666
2667 var cond = v.expr_bool(self.n_expr)
2668 v.add("if (unlikely(!{cond})) \{")
2669 v.stmt(self.n_else)
2670 var nid = self.n_id
2671 if nid != null then
2672 v.add_abort("Assert '{nid.text}' failed")
2673 else
2674 v.add_abort("Assert failed")
2675 end
2676 v.add("\}")
2677 end
2678 end
2679
2680 redef class AOrExpr
2681 redef fun expr(v)
2682 do
2683 var res = v.new_var(self.mtype.as(not null))
2684 var i1 = v.expr_bool(self.n_expr)
2685 v.add("if ({i1}) \{")
2686 v.add("{res} = 1;")
2687 v.add("\} else \{")
2688 var i2 = v.expr_bool(self.n_expr2)
2689 v.add("{res} = {i2};")
2690 v.add("\}")
2691 return res
2692 end
2693 end
2694
2695 redef class AImpliesExpr
2696 redef fun expr(v)
2697 do
2698 var res = v.new_var(self.mtype.as(not null))
2699 var i1 = v.expr_bool(self.n_expr)
2700 v.add("if (!{i1}) \{")
2701 v.add("{res} = 1;")
2702 v.add("\} else \{")
2703 var i2 = v.expr_bool(self.n_expr2)
2704 v.add("{res} = {i2};")
2705 v.add("\}")
2706 return res
2707 end
2708 end
2709
2710 redef class AAndExpr
2711 redef fun expr(v)
2712 do
2713 var res = v.new_var(self.mtype.as(not null))
2714 var i1 = v.expr_bool(self.n_expr)
2715 v.add("if (!{i1}) \{")
2716 v.add("{res} = 0;")
2717 v.add("\} else \{")
2718 var i2 = v.expr_bool(self.n_expr2)
2719 v.add("{res} = {i2};")
2720 v.add("\}")
2721 return res
2722 end
2723 end
2724
2725 redef class ANotExpr
2726 redef fun expr(v)
2727 do
2728 var cond = v.expr_bool(self.n_expr)
2729 return v.new_expr("!{cond}", self.mtype.as(not null))
2730 end
2731 end
2732
2733 redef class AOrElseExpr
2734 redef fun expr(v)
2735 do
2736 var res = v.new_var(self.mtype.as(not null))
2737 var i1 = v.expr(self.n_expr, null)
2738 v.add("if ({i1}!=NULL) \{")
2739 v.assign(res, i1)
2740 v.add("\} else \{")
2741 var i2 = v.expr(self.n_expr2, null)
2742 v.assign(res, i2)
2743 v.add("\}")
2744 return res
2745 end
2746 end
2747
2748 redef class AIntExpr
2749 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2750 end
2751
2752 redef class AFloatExpr
2753 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2754 end
2755
2756 redef class ACharExpr
2757 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2758 end
2759
2760 redef class AArrayExpr
2761 redef fun expr(v)
2762 do
2763 var mtype = self.element_mtype.as(not null)
2764 var array = new Array[RuntimeVariable]
2765 var res = v.array_instance(array, mtype)
2766
2767 var old_comprehension = v.frame.comprehension
2768 v.frame.comprehension = res
2769 for nexpr in self.n_exprs do
2770 v.stmt(nexpr)
2771 end
2772 v.frame.comprehension = old_comprehension
2773
2774 return res
2775 end
2776 end
2777
2778 redef class AStringFormExpr
2779 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2780 end
2781
2782 redef class ASuperstringExpr
2783 redef fun expr(v)
2784 do
2785 var type_string = mtype.as(not null)
2786
2787 # Collect elements of the superstring
2788 var array = new Array[AExpr]
2789 for ne in self.n_exprs do
2790 # Drop literal empty string.
2791 # They appears in things like "{a}" that is ["", a, ""]
2792 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2793 array.add(ne)
2794 end
2795
2796 # Store the allocated native array in a static variable
2797 # For reusing later
2798 var varonce = v.get_name("varonce")
2799 v.add("if (unlikely({varonce}==NULL)) \{")
2800
2801 # The native array that will contains the elements to_s-ized.
2802 # For fast concatenation.
2803 var a = v.native_array_instance(type_string, v.int_instance(array.length))
2804
2805 v.add_decl("static {a.mtype.ctype} {varonce};")
2806
2807 # Pre-fill the array with the literal string parts.
2808 # So they do not need to be filled again when reused
2809 for i in [0..array.length[ do
2810 var ne = array[i]
2811 if not ne isa AStringFormExpr then continue
2812 var e = v.expr(ne, null)
2813 v.native_array_set(a, i, e)
2814 end
2815
2816 v.add("\} else \{")
2817 # Take the native-array from the store.
2818 # The point is to prevent that some recursive execution use (and corrupt) the same native array
2819 # WARNING: not thread safe! (FIXME?)
2820 v.add("{a} = {varonce};")
2821 v.add("{varonce} = NULL;")
2822 v.add("\}")
2823
2824 # Stringify the elements and put them in the native array
2825 var to_s_method = v.get_property("to_s", v.object_type)
2826 for i in [0..array.length[ do
2827 var ne = array[i]
2828 if ne isa AStringFormExpr then continue
2829 var e = v.expr(ne, null)
2830 # Skip the `to_s` if the element is already a String
2831 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
2832 e = v.send(to_s_method, [e]).as(not null)
2833 end
2834 v.native_array_set(a, i, e)
2835 end
2836
2837 # Fast join the native string to get the result
2838 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
2839
2840 # We finish to work with the native array,
2841 # so store it so that it can be reused
2842 v.add("{varonce} = {a};")
2843 return res
2844 end
2845 end
2846
2847 redef class ACrangeExpr
2848 redef fun expr(v)
2849 do
2850 var i1 = v.expr(self.n_expr, null)
2851 var i2 = v.expr(self.n_expr2, null)
2852 var mtype = self.mtype.as(MClassType)
2853 var res = v.init_instance(mtype)
2854 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2855 return res
2856 end
2857 end
2858
2859 redef class AOrangeExpr
2860 redef fun expr(v)
2861 do
2862 var i1 = v.expr(self.n_expr, null)
2863 var i2 = v.expr(self.n_expr2, null)
2864 var mtype = self.mtype.as(MClassType)
2865 var res = v.init_instance(mtype)
2866 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2867 return res
2868 end
2869 end
2870
2871 redef class ATrueExpr
2872 redef fun expr(v) do return v.bool_instance(true)
2873 end
2874
2875 redef class AFalseExpr
2876 redef fun expr(v) do return v.bool_instance(false)
2877 end
2878
2879 redef class ANullExpr
2880 redef fun expr(v) do return v.null_instance
2881 end
2882
2883 redef class AIsaExpr
2884 redef fun expr(v)
2885 do
2886 var i = v.expr(self.n_expr, null)
2887 return v.type_test(i, self.cast_type.as(not null), "isa")
2888 end
2889 end
2890
2891 redef class AAsCastExpr
2892 redef fun expr(v)
2893 do
2894 var i = v.expr(self.n_expr, null)
2895 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2896
2897 v.add_cast(i, self.mtype.as(not null), "as")
2898 return i
2899 end
2900 end
2901
2902 redef class AAsNotnullExpr
2903 redef fun expr(v)
2904 do
2905 var i = v.expr(self.n_expr, null)
2906 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2907
2908 if i.mtype.is_c_primitive then return i
2909
2910 v.add("if (unlikely({i} == NULL)) \{")
2911 v.add_abort("Cast failed")
2912 v.add("\}")
2913 return i
2914 end
2915 end
2916
2917 redef class AParExpr
2918 redef fun expr(v) do return v.expr(self.n_expr, null)
2919 end
2920
2921 redef class AOnceExpr
2922 redef fun expr(v)
2923 do
2924 var mtype = self.mtype.as(not null)
2925 var name = v.get_name("varonce")
2926 var guard = v.get_name(name + "_guard")
2927 v.add_decl("static {mtype.ctype} {name};")
2928 v.add_decl("static int {guard};")
2929 var res = v.new_var(mtype)
2930 v.add("if (likely({guard})) \{")
2931 v.add("{res} = {name};")
2932 v.add("\} else \{")
2933 var i = v.expr(self.n_expr, mtype)
2934 v.add("{res} = {i};")
2935 v.add("{name} = {res};")
2936 v.add("{guard} = 1;")
2937 v.add("\}")
2938 return res
2939 end
2940 end
2941
2942 redef class ASendExpr
2943 redef fun expr(v)
2944 do
2945 var recv = v.expr(self.n_expr, null)
2946 var callsite = self.callsite.as(not null)
2947 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2948 return v.compile_callsite(callsite, args)
2949 end
2950 end
2951
2952 redef class ASendReassignFormExpr
2953 redef fun stmt(v)
2954 do
2955 var recv = v.expr(self.n_expr, null)
2956 var callsite = self.callsite.as(not null)
2957 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2958
2959 var value = v.expr(self.n_value, null)
2960
2961 var left = v.compile_callsite(callsite, args)
2962 assert left != null
2963
2964 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2965 assert res != null
2966
2967 args.add(res)
2968 v.compile_callsite(self.write_callsite.as(not null), args)
2969 end
2970 end
2971
2972 redef class ASuperExpr
2973 redef fun expr(v)
2974 do
2975 var recv = v.frame.arguments.first
2976
2977 var callsite = self.callsite
2978 if callsite != null then
2979 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2980
2981 # Add additional arguments for the super init call
2982 if args.length == 1 then
2983 for i in [0..callsite.msignature.arity[ do
2984 args.add(v.frame.arguments[i+1])
2985 end
2986 end
2987 # Super init call
2988 var res = v.compile_callsite(callsite, args)
2989 return res
2990 end
2991
2992 var mpropdef = self.mpropdef.as(not null)
2993 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2994 if args.length == 1 then
2995 args = v.frame.arguments
2996 end
2997
2998 # stantard call-next-method
2999 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3000 end
3001 end
3002
3003 redef class ANewExpr
3004 redef fun expr(v)
3005 do
3006 var mtype = self.recvtype
3007 assert mtype != null
3008
3009 if mtype.mclass.name == "NativeArray" then
3010 assert self.n_args.n_exprs.length == 1
3011 var l = v.expr(self.n_args.n_exprs.first, null)
3012 assert mtype isa MGenericType
3013 var elttype = mtype.arguments.first
3014 return v.native_array_instance(elttype, l)
3015 end
3016
3017 var recv = v.init_instance_or_extern(mtype)
3018
3019 var callsite = self.callsite.as(not null)
3020 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
3021 var res2 = v.compile_callsite(callsite, args)
3022 if res2 != null then
3023 #self.debug("got {res2} from {mproperty}. drop {recv}")
3024 return res2
3025 end
3026 return recv
3027 end
3028 end
3029
3030 redef class AAttrExpr
3031 redef fun expr(v)
3032 do
3033 var recv = v.expr(self.n_expr, null)
3034 var mproperty = self.mproperty.as(not null)
3035 return v.read_attribute(mproperty, recv)
3036 end
3037 end
3038
3039 redef class AAttrAssignExpr
3040 redef fun expr(v)
3041 do
3042 var recv = v.expr(self.n_expr, null)
3043 var i = v.expr(self.n_value, null)
3044 var mproperty = self.mproperty.as(not null)
3045 v.write_attribute(mproperty, recv, i)
3046 return i
3047 end
3048 end
3049
3050 redef class AAttrReassignExpr
3051 redef fun stmt(v)
3052 do
3053 var recv = v.expr(self.n_expr, null)
3054 var value = v.expr(self.n_value, null)
3055 var mproperty = self.mproperty.as(not null)
3056 var attr = v.read_attribute(mproperty, recv)
3057 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3058 assert res != null
3059 v.write_attribute(mproperty, recv, res)
3060 end
3061 end
3062
3063 redef class AIssetAttrExpr
3064 redef fun expr(v)
3065 do
3066 var recv = v.expr(self.n_expr, null)
3067 var mproperty = self.mproperty.as(not null)
3068 return v.isset_attribute(mproperty, recv)
3069 end
3070 end
3071
3072 redef class ADebugTypeExpr
3073 redef fun stmt(v)
3074 do
3075 # do nothing
3076 end
3077 end
3078
3079 # Utils
3080
3081 redef class Array[E]
3082 # Return a new `Array` with the elements only contened in self and not in `o`
3083 fun -(o: Array[E]): Array[E] do
3084 var res = new Array[E]
3085 for e in self do if not o.has(e) then res.add(e)
3086 return res
3087 end
3088 end
3089
3090 redef class MModule
3091 # All `MProperty` associated to all `MClassDef` of `mclass`
3092 fun properties(mclass: MClass): Set[MProperty] do
3093 if not self.properties_cache.has_key(mclass) then
3094 var properties = new HashSet[MProperty]
3095 var parents = new Array[MClass]
3096 if self.flatten_mclass_hierarchy.has(mclass) then
3097 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3098 end
3099 for parent in parents do
3100 properties.add_all(self.properties(parent))
3101 end
3102 for mclassdef in mclass.mclassdefs do
3103 if not self.in_importation <= mclassdef.mmodule then continue
3104 for mprop in mclassdef.intro_mproperties do
3105 properties.add(mprop)
3106 end
3107 end
3108 self.properties_cache[mclass] = properties
3109 end
3110 return properties_cache[mclass]
3111 end
3112 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3113
3114 # Write FFI and nitni results to file
3115 fun finalize_ffi(c: AbstractCompiler) do end
3116
3117 # Give requided addinional system libraries (as given to LD_LIBS)
3118 # Note: can return null instead of an empty set
3119 fun collect_linker_libs: nullable Array[String] do return null
3120 end
3121
3122 # Create a tool context to handle options and paths
3123 var toolcontext = new ToolContext
3124
3125 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3126
3127 # We do not add other options, so process them now!
3128 toolcontext.process_options(args)
3129
3130 # We need a model to collect stufs
3131 var model = new Model
3132 # An a model builder to parse files
3133 var modelbuilder = new ModelBuilder(model, toolcontext)
3134
3135 var arguments = toolcontext.option_context.rest
3136 if arguments.length > 1 and toolcontext.opt_output.value != null then
3137 print "Error: --output needs a single source file. Do you prefer --dir?"
3138 exit 1
3139 end
3140
3141 # Here we load an process all modules passed on the command line
3142 var mmodules = modelbuilder.parse(arguments)
3143
3144 if mmodules.is_empty then return
3145 modelbuilder.run_phases
3146
3147 for mmodule in mmodules do
3148 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3149 var ms = [mmodule]
3150 toolcontext.run_global_phases(ms)
3151 end