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