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