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