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