c3952e3cac33787dae41d4b44ea65b22c1a14f92
[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 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1130 # This method is used to manage varargs in signatures and returns the real array
1131 # of runtime variables to use in the call.
1132 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1133 do
1134 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1135 var res = new Array[RuntimeVariable]
1136 res.add(recv)
1137
1138 if args.is_empty then return res
1139
1140 var vararg_rank = msignature.vararg_rank
1141 var vararg_len = args.length - msignature.arity
1142 if vararg_len < 0 then vararg_len = 0
1143
1144 for i in [0..msignature.arity[ do
1145 if i == vararg_rank then
1146 var ne = args[i]
1147 if ne isa AVarargExpr then
1148 var e = self.expr(ne.n_expr, null)
1149 res.add(e)
1150 continue
1151 end
1152 var vararg = new Array[RuntimeVariable]
1153 for j in [vararg_rank..vararg_rank+vararg_len] do
1154 var e = self.expr(args[j], null)
1155 vararg.add(e)
1156 end
1157 var elttype = msignature.mparameters[vararg_rank].mtype
1158 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1159 res.add(arg)
1160 else
1161 var j = i
1162 if i > vararg_rank then j += vararg_len
1163 var e = self.expr(args[j], null)
1164 res.add(e)
1165 end
1166 end
1167 return res
1168 end
1169
1170 # Type handling
1171
1172 # Anchor a type to the main module and the current receiver
1173 fun anchor(mtype: MType): MType
1174 do
1175 if not mtype.need_anchor then return mtype
1176 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1177 end
1178
1179 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1180 do
1181 if not mtype.need_anchor then return mtype
1182 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1183 end
1184
1185 # Unsafely cast a value to a new type
1186 # ie the result share the same C variable but my have a different mcasttype
1187 # NOTE: if the adaptation is useless then `value` is returned as it.
1188 # ENSURE: `result.name == value.name`
1189 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1190 do
1191 mtype = self.anchor(mtype)
1192 var valmtype = value.mcasttype
1193 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1194 return value
1195 end
1196
1197 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1198 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1199 return res
1200 else
1201 var res = new RuntimeVariable(value.name, valmtype, mtype)
1202 return res
1203 end
1204 end
1205
1206 # Generate a super call from a method definition
1207 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1208
1209 # Adapt the arguments of a method according to targetted `MMethodDef`
1210 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1211
1212 # Unbox all the arguments of a method when implemented `extern` or `intern`
1213 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1214
1215 # Box or unbox a value to another type iff a C type conversion is needed
1216 # ENSURE: `result.mtype.ctype == mtype.ctype`
1217 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1218
1219 # Box extern classes to be used in the generated code
1220 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1221
1222 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1223 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1224
1225 # Generate a polymorphic subtype test
1226 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1227
1228 # Generate the code required to dynamically check if 2 objects share the same runtime type
1229 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1230
1231 # Generate a Nit "is" for two runtime_variables
1232 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1233
1234 # Sends
1235
1236 # Generate a static call on a method definition
1237 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1238
1239 # Generate a polymorphic send for the method `m` and the arguments `args`
1240 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1241
1242 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1243 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1244 do
1245 assert t isa MClassType
1246 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1247 return self.call(propdef, t, args)
1248 end
1249
1250 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1251 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1252 do
1253 assert t isa MClassType
1254 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1255 return self.call(m, t, args)
1256 end
1257
1258 # Attributes handling
1259
1260 # Generate a polymorphic attribute is_set test
1261 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1262
1263 # Generate a polymorphic attribute read
1264 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1265
1266 # Generate a polymorphic attribute write
1267 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1268
1269 # Checks
1270
1271 # Add a check and an abort for a null receiver if needed
1272 fun check_recv_notnull(recv: RuntimeVariable)
1273 do
1274 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1275
1276 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1277 if maybenull then
1278 self.add("if (unlikely({recv} == NULL)) \{")
1279 self.add_abort("Receiver is null")
1280 self.add("\}")
1281 end
1282 end
1283
1284 # Names handling
1285
1286 private var names = new HashSet[String]
1287 private var last: Int = 0
1288
1289 # Return a new name based on `s` and unique in the visitor
1290 fun get_name(s: String): String
1291 do
1292 if not self.names.has(s) then
1293 self.names.add(s)
1294 return s
1295 end
1296 var i = self.last + 1
1297 loop
1298 var s2 = s + i.to_s
1299 if not self.names.has(s2) then
1300 self.last = i
1301 self.names.add(s2)
1302 return s2
1303 end
1304 i = i + 1
1305 end
1306 end
1307
1308 # Return an unique and stable identifier associated with an escapemark
1309 fun escapemark_name(e: nullable EscapeMark): String
1310 do
1311 assert e != null
1312 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1313 var name = e.name
1314 if name == null then name = "label"
1315 name = get_name(name)
1316 frame.escapemark_names[e] = name
1317 return name
1318 end
1319
1320 # Insert a C label for associated with an escapemark
1321 fun add_escape_label(e: nullable EscapeMark)
1322 do
1323 if e == null then return
1324 if e.escapes.is_empty then return
1325 add("BREAK_{escapemark_name(e)}: (void)0;")
1326 end
1327
1328 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1329 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1330 fun class_name_string(value: RuntimeVariable): String is abstract
1331
1332 # Variables handling
1333
1334 protected var variables = new HashMap[Variable, RuntimeVariable]
1335
1336 # Return the local runtime_variable associated to a Nit local variable
1337 fun variable(variable: Variable): RuntimeVariable
1338 do
1339 if self.variables.has_key(variable) then
1340 return self.variables[variable]
1341 else
1342 var name = self.get_name("var_{variable.name}")
1343 var mtype = variable.declared_type.as(not null)
1344 mtype = self.anchor(mtype)
1345 var res = new RuntimeVariable(name, mtype, mtype)
1346 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1347 self.variables[variable] = res
1348 return res
1349 end
1350 end
1351
1352 # Return a new uninitialized local runtime_variable
1353 fun new_var(mtype: MType): RuntimeVariable
1354 do
1355 mtype = self.anchor(mtype)
1356 var name = self.get_name("var")
1357 var res = new RuntimeVariable(name, mtype, mtype)
1358 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1359 return res
1360 end
1361
1362 # The difference with `new_var` is the C static type of the local variable
1363 fun new_var_extern(mtype: MType): RuntimeVariable
1364 do
1365 mtype = self.anchor(mtype)
1366 var name = self.get_name("var")
1367 var res = new RuntimeVariable(name, mtype, mtype)
1368 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1369 return res
1370 end
1371
1372 # Return a new uninitialized named runtime_variable
1373 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1374 do
1375 mtype = self.anchor(mtype)
1376 var res = new RuntimeVariable(name, mtype, mtype)
1377 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1378 return res
1379 end
1380
1381 # Correctly assign a left and a right value
1382 # Boxing and unboxing is performed if required
1383 fun assign(left, right: RuntimeVariable)
1384 do
1385 right = self.autobox(right, left.mtype)
1386 self.add("{left} = {right};")
1387 end
1388
1389 # Generate instances
1390
1391 # Generate a alloc-instance + init-attributes
1392 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1393
1394 # Allocate and init attributes of an instance of a standard or extern class
1395 #
1396 # Does not support universals and the pseudo-internal `NativeArray` class.
1397 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1398 do
1399 var recv
1400 var ctype = mtype.ctype
1401 assert mtype.mclass.name != "NativeArray"
1402 if not mtype.is_c_primitive then
1403 recv = init_instance(mtype)
1404 else if ctype == "char*" then
1405 recv = new_expr("NULL/*special!*/", mtype)
1406 else
1407 recv = new_expr("({ctype})0/*special!*/", mtype)
1408 end
1409 return recv
1410 end
1411
1412 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1413 fun set_finalizer(recv: RuntimeVariable)
1414 do
1415 var mtype = recv.mtype
1416 var finalizable_type = compiler.mainmodule.finalizable_type
1417 if finalizable_type != null and not mtype.need_anchor and
1418 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1419 add "gc_register_finalizer({recv});"
1420 end
1421 end
1422
1423 # The currently processed module
1424 #
1425 # alias for `compiler.mainmodule`
1426 fun mmodule: MModule do return compiler.mainmodule
1427
1428 # Generate an integer value
1429 fun int_instance(value: Int): RuntimeVariable
1430 do
1431 var t = mmodule.int_type
1432 var res = new RuntimeVariable("{value.to_s}l", t, t)
1433 return res
1434 end
1435
1436 # Generate a char value
1437 fun char_instance(value: Char): RuntimeVariable
1438 do
1439 var t = mmodule.char_type
1440 var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1441 return res
1442 end
1443
1444 # Generate a float value
1445 #
1446 # FIXME pass a Float, not a string
1447 fun float_instance(value: String): RuntimeVariable
1448 do
1449 var t = mmodule.float_type
1450 var res = new RuntimeVariable("{value}", t, t)
1451 return res
1452 end
1453
1454 # Generate an integer value
1455 fun bool_instance(value: Bool): RuntimeVariable
1456 do
1457 var s = if value then "1" else "0"
1458 var res = new RuntimeVariable(s, bool_type, bool_type)
1459 return res
1460 end
1461
1462 # Generate the `null` value
1463 fun null_instance: RuntimeVariable
1464 do
1465 var t = compiler.mainmodule.model.null_type
1466 var res = new RuntimeVariable("((val*)NULL)", t, t)
1467 return res
1468 end
1469
1470 # Generate a string value
1471 fun string_instance(string: String): RuntimeVariable
1472 do
1473 var mtype = mmodule.string_type
1474 var name = self.get_name("varonce")
1475 self.add_decl("static {mtype.ctype} {name};")
1476 var res = self.new_var(mtype)
1477 self.add("if (likely({name}!=NULL)) \{")
1478 self.add("{res} = {name};")
1479 self.add("\} else \{")
1480 var native_mtype = mmodule.native_string_type
1481 var nat = self.new_var(native_mtype)
1482 self.add("{nat} = \"{string.escape_to_c}\";")
1483 var length = self.int_instance(string.length)
1484 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1485 self.add("{name} = {res};")
1486 self.add("\}")
1487 return res
1488 end
1489
1490 fun value_instance(object: Object): RuntimeVariable
1491 do
1492 if object isa Int then
1493 return int_instance(object)
1494 else if object isa Bool then
1495 return bool_instance(object)
1496 else if object isa String then
1497 return string_instance(object)
1498 else
1499 abort
1500 end
1501 end
1502
1503 # Generate an array value
1504 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1505
1506 # Get an instance of a array for a vararg
1507 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1508
1509 # Code generation
1510
1511 # Add a line in the main part of the generated C
1512 fun add(s: String) do self.writer.lines.add(s)
1513
1514 # Add a line in the
1515 # (used for local or global declaration)
1516 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1517
1518 # Request the presence of a global declaration
1519 fun require_declaration(key: String)
1520 do
1521 var reqs = self.writer.file.required_declarations
1522 if reqs.has(key) then return
1523 reqs.add(key)
1524 var node = current_node
1525 if node != null then compiler.requirers_of_declarations[key] = node
1526 end
1527
1528 # Add a declaration in the local-header
1529 # The declaration is ensured to be present once
1530 fun declare_once(s: String)
1531 do
1532 self.compiler.provide_declaration(s, s)
1533 self.require_declaration(s)
1534 end
1535
1536 # Look for a needed .h and .c file for a given module
1537 # This is used for the legacy FFI
1538 fun add_extern(mmodule: MModule)
1539 do
1540 var file = mmodule.location.file.filename
1541 file = file.strip_extension(".nit")
1542 var tryfile = file + ".nit.h"
1543 if tryfile.file_exists then
1544 self.declare_once("#include \"{tryfile.basename("")}\"")
1545 self.compiler.files_to_copy.add(tryfile)
1546 end
1547 tryfile = file + "_nit.h"
1548 if tryfile.file_exists then
1549 self.declare_once("#include \"{tryfile.basename("")}\"")
1550 self.compiler.files_to_copy.add(tryfile)
1551 end
1552
1553 if self.compiler.seen_extern.has(file) then return
1554 self.compiler.seen_extern.add(file)
1555 tryfile = file + ".nit.c"
1556 if not tryfile.file_exists then
1557 tryfile = file + "_nit.c"
1558 if not tryfile.file_exists then return
1559 end
1560 var f = new ExternCFile(tryfile.basename(""), "")
1561 self.compiler.extern_bodies.add(f)
1562 self.compiler.files_to_copy.add(tryfile)
1563 end
1564
1565 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1566 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1567 do
1568 var res = new_var(mtype)
1569 self.add("{res} = {cexpr};")
1570 return res
1571 end
1572
1573 # Generate generic abort
1574 # used by aborts, asserts, casts, etc.
1575 fun add_abort(message: String)
1576 do
1577 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1578 add_raw_abort
1579 end
1580
1581 fun add_raw_abort
1582 do
1583 if self.current_node != null and self.current_node.location.file != null and
1584 self.current_node.location.file.mmodule != null then
1585 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1586 self.require_declaration(f)
1587 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1588 else
1589 self.add("PRINT_ERROR(\"\\n\");")
1590 end
1591 self.add("fatal_exit(1);")
1592 end
1593
1594 # Add a dynamic cast
1595 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1596 do
1597 var res = self.type_test(value, mtype, tag)
1598 self.add("if (unlikely(!{res})) \{")
1599 var cn = self.class_name_string(value)
1600 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1601 self.add_raw_abort
1602 self.add("\}")
1603 end
1604
1605 # Generate a return with the value `s`
1606 fun ret(s: RuntimeVariable)
1607 do
1608 self.assign(self.frame.returnvar.as(not null), s)
1609 self.add("goto {self.frame.returnlabel.as(not null)};")
1610 end
1611
1612 # Compile a statement (if any)
1613 fun stmt(nexpr: nullable AExpr)
1614 do
1615 if nexpr == null then return
1616
1617 var narray = nexpr.comprehension
1618 if narray != null then
1619 var recv = frame.comprehension.as(not null)
1620 var val = expr(nexpr, narray.element_mtype)
1621 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1622 return
1623 end
1624
1625 var old = self.current_node
1626 self.current_node = nexpr
1627 nexpr.stmt(self)
1628 self.current_node = old
1629 end
1630
1631 # Compile an expression an return its result
1632 # `mtype` is the expected return type, pass null if no specific type is expected.
1633 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1634 do
1635 var old = self.current_node
1636 self.current_node = nexpr
1637 var res = nexpr.expr(self).as(not null)
1638 if mtype != null then
1639 mtype = self.anchor(mtype)
1640 res = self.autobox(res, mtype)
1641 end
1642 res = autoadapt(res, nexpr.mtype.as(not null))
1643 var implicit_cast_to = nexpr.implicit_cast_to
1644 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1645 add_cast(res, implicit_cast_to, "auto")
1646 res = autoadapt(res, implicit_cast_to)
1647 end
1648 self.current_node = old
1649 return res
1650 end
1651
1652 # Alias for `self.expr(nexpr, self.bool_type)`
1653 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1654
1655 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1656 fun debug(message: String)
1657 do
1658 var node = self.current_node
1659 if node == null then
1660 print "?: {message}"
1661 else
1662 node.debug(message)
1663 end
1664 self.add("/* DEBUG: {message} */")
1665 end
1666 end
1667
1668 # A C function associated to a Nit method
1669 # Because of customization, a given Nit method can be compiler more that once
1670 abstract class AbstractRuntimeFunction
1671
1672 type COMPILER: AbstractCompiler
1673 type VISITOR: AbstractCompilerVisitor
1674
1675 # The associated Nit method
1676 var mmethoddef: MMethodDef
1677
1678 # The mangled c name of the runtime_function
1679 # Subclasses should redefine `build_c_name` instead
1680 fun c_name: String
1681 do
1682 var res = self.c_name_cache
1683 if res != null then return res
1684 res = self.build_c_name
1685 self.c_name_cache = res
1686 return res
1687 end
1688
1689 # Non cached version of `c_name`
1690 protected fun build_c_name: String is abstract
1691
1692 protected var c_name_cache: nullable String = null is writable
1693
1694 # Implements a call of the runtime_function
1695 # May inline the body or generate a C function call
1696 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1697
1698 # Generate the code for the `AbstractRuntimeFunction`
1699 # Warning: compile more than once compilation makes CC unhappy
1700 fun compile_to_c(compiler: COMPILER) is abstract
1701 end
1702
1703 # A runtime variable hold a runtime value in C.
1704 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1705 #
1706 # 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.
1707 class RuntimeVariable
1708 # The name of the variable in the C code
1709 var name: String
1710
1711 # The static type of the variable (as declard in C)
1712 var mtype: MType
1713
1714 # The current casted type of the variable (as known in Nit)
1715 var mcasttype: MType is writable
1716
1717 # If the variable exaclty a mcasttype?
1718 # false (usual value) means that the variable is a mcasttype or a subtype.
1719 var is_exact: Bool = false is writable
1720
1721 init
1722 do
1723 assert not mtype.need_anchor
1724 assert not mcasttype.need_anchor
1725 end
1726
1727 redef fun to_s do return name
1728
1729 redef fun inspect
1730 do
1731 var exact_str
1732 if self.is_exact then
1733 exact_str = " exact"
1734 else
1735 exact_str = ""
1736 end
1737 var type_str
1738 if self.mtype == self.mcasttype then
1739 type_str = "{mtype}{exact_str}"
1740 else
1741 type_str = "{mtype}({mcasttype}{exact_str})"
1742 end
1743 return "<{name}:{type_str}>"
1744 end
1745 end
1746
1747 # The static context of a visited property in a `AbstractCompilerVisitor`
1748 class StaticFrame
1749
1750 type VISITOR: AbstractCompilerVisitor
1751
1752 # The associated visitor
1753 var visitor: VISITOR
1754
1755 # The executed property.
1756 # A Method in case of a call, an attribute in case of a default initialization.
1757 var mpropdef: MPropDef
1758
1759 # The static type of the receiver
1760 var receiver: MClassType
1761
1762 # Arguments of the method (the first is the receiver)
1763 var arguments: Array[RuntimeVariable]
1764
1765 # The runtime_variable associated to the return (in a function)
1766 var returnvar: nullable RuntimeVariable = null is writable
1767
1768 # The label at the end of the property
1769 var returnlabel: nullable String = null is writable
1770
1771 # Labels associated to a each escapemarks.
1772 # Because of inlinings, escape-marks must be associated to their context (the frame)
1773 private var escapemark_names = new HashMap[EscapeMark, String]
1774
1775 # The array comprehension currently filled, if any
1776 private var comprehension: nullable RuntimeVariable = null
1777 end
1778
1779 redef class MType
1780 # Return the C type associated to a given Nit static type
1781 fun ctype: String do return "val*"
1782
1783 # C type outside of the compiler code and in boxes
1784 fun ctype_extern: String do return "val*"
1785
1786 # Short name of the `ctype` to use in unions
1787 fun ctypename: String do return "val"
1788
1789 # Is the associated C type a primitive one?
1790 #
1791 # ENSURE `result == (ctype != "val*")`
1792 fun is_c_primitive: Bool do return false
1793 end
1794
1795 redef class MClassType
1796
1797 redef var ctype is lazy do
1798 if mclass.name == "Int" then
1799 return "long"
1800 else if mclass.name == "Bool" then
1801 return "short int"
1802 else if mclass.name == "Char" then
1803 return "char"
1804 else if mclass.name == "Float" then
1805 return "double"
1806 else if mclass.name == "NativeString" then
1807 return "char*"
1808 else if mclass.name == "NativeArray" then
1809 return "val*"
1810 else
1811 return "val*"
1812 end
1813 end
1814
1815 redef var is_c_primitive is lazy do return ctype != "val*"
1816
1817 redef fun ctype_extern: String
1818 do
1819 if mclass.kind == extern_kind then
1820 return "void*"
1821 else
1822 return ctype
1823 end
1824 end
1825
1826 redef fun ctypename: String
1827 do
1828 if mclass.name == "Int" then
1829 return "l"
1830 else if mclass.name == "Bool" then
1831 return "s"
1832 else if mclass.name == "Char" then
1833 return "c"
1834 else if mclass.name == "Float" then
1835 return "d"
1836 else if mclass.name == "NativeString" then
1837 return "str"
1838 else if mclass.name == "NativeArray" then
1839 #return "{self.arguments.first.ctype}*"
1840 return "val"
1841 else
1842 return "val"
1843 end
1844 end
1845 end
1846
1847 redef class MPropDef
1848 type VISITOR: AbstractCompilerVisitor
1849 end
1850
1851 redef class MMethodDef
1852 # Can the body be inlined?
1853 fun can_inline(v: VISITOR): Bool
1854 do
1855 if is_abstract then return true
1856 var modelbuilder = v.compiler.modelbuilder
1857 var node = modelbuilder.mpropdef2node(self)
1858 if node isa APropdef then
1859 return node.can_inline
1860 else if node isa AClassdef then
1861 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1862 return true
1863 else
1864 abort
1865 end
1866 end
1867
1868 # Inline the body in another visitor
1869 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1870 do
1871 var modelbuilder = v.compiler.modelbuilder
1872 var val = constant_value
1873 var node = modelbuilder.mpropdef2node(self)
1874
1875 if is_abstract then
1876 var cn = v.class_name_string(arguments.first)
1877 v.current_node = node
1878 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1879 v.add_raw_abort
1880 return null
1881 end
1882
1883 if node isa APropdef then
1884 var oldnode = v.current_node
1885 v.current_node = node
1886 self.compile_parameter_check(v, arguments)
1887 node.compile_to_c(v, self, arguments)
1888 v.current_node = oldnode
1889 else if node isa AClassdef then
1890 var oldnode = v.current_node
1891 v.current_node = node
1892 self.compile_parameter_check(v, arguments)
1893 node.compile_to_c(v, self, arguments)
1894 v.current_node = oldnode
1895 else if val != null then
1896 v.ret(v.value_instance(val))
1897 else
1898 abort
1899 end
1900 return null
1901 end
1902
1903 # Generate type checks in the C code to check covariant parameters
1904 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1905 do
1906 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1907
1908 for i in [0..msignature.arity[ do
1909 # skip test for vararg since the array is instantiated with the correct polymorphic type
1910 if msignature.vararg_rank == i then continue
1911
1912 # skip if the cast is not required
1913 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1914 if not origmtype.need_anchor then continue
1915
1916 # get the parameter type
1917 var mtype = self.msignature.mparameters[i].mtype
1918
1919 # generate the cast
1920 # note that v decides if and how to implements the cast
1921 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1922 v.add_cast(arguments[i+1], mtype, "covariance")
1923 end
1924 end
1925 end
1926
1927 # Node visit
1928
1929 redef class APropdef
1930 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1931 do
1932 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1933 debug("Not yet implemented")
1934 end
1935
1936 fun can_inline: Bool do return true
1937 end
1938
1939 redef class AMethPropdef
1940 redef fun compile_to_c(v, mpropdef, arguments)
1941 do
1942 # Call the implicit super-init
1943 var auto_super_inits = self.auto_super_inits
1944 if auto_super_inits != null then
1945 var args = [arguments.first]
1946 for auto_super_init in auto_super_inits do
1947 assert auto_super_init.mproperty != mpropdef.mproperty
1948 args.clear
1949 for i in [0..auto_super_init.msignature.arity+1[ do
1950 args.add(arguments[i])
1951 end
1952 assert auto_super_init.mproperty != mpropdef.mproperty
1953 v.compile_callsite(auto_super_init, args)
1954 end
1955 end
1956 if auto_super_call then
1957 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1958 end
1959
1960 # Try special compilation
1961 if mpropdef.is_intern then
1962 if compile_intern_to_c(v, mpropdef, arguments) then return
1963 else if mpropdef.is_extern then
1964 if mpropdef.mproperty.is_init then
1965 if compile_externinit_to_c(v, mpropdef, arguments) then return
1966 else
1967 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1968 end
1969 end
1970
1971 # Compile block if any
1972 var n_block = n_block
1973 if n_block != null then
1974 for i in [0..mpropdef.msignature.arity[ do
1975 var variable = self.n_signature.n_params[i].variable.as(not null)
1976 v.assign(v.variable(variable), arguments[i+1])
1977 end
1978 v.stmt(n_block)
1979 return
1980 end
1981
1982 # We have a problem
1983 var cn = v.class_name_string(arguments.first)
1984 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1985 v.add_raw_abort
1986 end
1987
1988 redef fun can_inline
1989 do
1990 if self.auto_super_inits != null then return false
1991 var nblock = self.n_block
1992 if nblock == null then return true
1993 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1994 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1995 return false
1996 end
1997
1998 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1999 do
2000 var pname = mpropdef.mproperty.name
2001 var cname = mpropdef.mclassdef.mclass.name
2002 var ret = mpropdef.msignature.return_mtype
2003 if ret != null then
2004 ret = v.resolve_for(ret, arguments.first)
2005 end
2006 if pname != "==" and pname != "!=" then
2007 v.adapt_signature(mpropdef, arguments)
2008 v.unbox_signature_extern(mpropdef, arguments)
2009 end
2010 if cname == "Int" then
2011 if pname == "output" then
2012 v.add("printf(\"%ld\\n\", {arguments.first});")
2013 return true
2014 else if pname == "object_id" then
2015 v.ret(arguments.first)
2016 return true
2017 else if pname == "+" then
2018 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2019 return true
2020 else if pname == "-" then
2021 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2022 return true
2023 else if pname == "unary -" then
2024 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2025 return true
2026 else if pname == "*" then
2027 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2028 return true
2029 else if pname == "/" then
2030 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2031 return true
2032 else if pname == "%" then
2033 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2034 return true
2035 else if pname == "lshift" then
2036 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2037 return true
2038 else if pname == "rshift" then
2039 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2040 return true
2041 else if pname == "==" then
2042 v.ret(v.equal_test(arguments[0], arguments[1]))
2043 return true
2044 else if pname == "!=" then
2045 var res = v.equal_test(arguments[0], arguments[1])
2046 v.ret(v.new_expr("!{res}", ret.as(not null)))
2047 return true
2048 else if pname == "<" then
2049 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2050 return true
2051 else if pname == ">" then
2052 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2053 return true
2054 else if pname == "<=" then
2055 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2056 return true
2057 else if pname == ">=" then
2058 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2059 return true
2060 else if pname == "to_f" then
2061 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2062 return true
2063 else if pname == "ascii" then
2064 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2065 return true
2066 end
2067 else if cname == "Char" then
2068 if pname == "output" then
2069 v.add("printf(\"%c\", {arguments.first});")
2070 return true
2071 else if pname == "object_id" then
2072 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2073 return true
2074 else if pname == "successor" then
2075 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2076 return true
2077 else if pname == "predecessor" then
2078 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2079 return true
2080 else if pname == "==" then
2081 v.ret(v.equal_test(arguments[0], arguments[1]))
2082 return true
2083 else if pname == "!=" then
2084 var res = v.equal_test(arguments[0], arguments[1])
2085 v.ret(v.new_expr("!{res}", ret.as(not null)))
2086 return true
2087 else if pname == "<" then
2088 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2089 return true
2090 else if pname == ">" then
2091 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2092 return true
2093 else if pname == "<=" then
2094 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2095 return true
2096 else if pname == ">=" then
2097 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2098 return true
2099 else if pname == "to_i" then
2100 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2101 return true
2102 else if pname == "ascii" then
2103 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2104 return true
2105 end
2106 else if cname == "Bool" then
2107 if pname == "output" then
2108 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2109 return true
2110 else if pname == "object_id" then
2111 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2112 return true
2113 else if pname == "==" then
2114 v.ret(v.equal_test(arguments[0], arguments[1]))
2115 return true
2116 else if pname == "!=" then
2117 var res = v.equal_test(arguments[0], arguments[1])
2118 v.ret(v.new_expr("!{res}", ret.as(not null)))
2119 return true
2120 end
2121 else if cname == "Float" then
2122 if pname == "output" then
2123 v.add("printf(\"%f\\n\", {arguments.first});")
2124 return true
2125 else if pname == "object_id" then
2126 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2127 return true
2128 else if pname == "+" then
2129 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2130 return true
2131 else if pname == "-" then
2132 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2133 return true
2134 else if pname == "unary -" then
2135 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2136 return true
2137 else if pname == "succ" then
2138 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2139 return true
2140 else if pname == "prec" then
2141 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2142 return true
2143 else if pname == "*" then
2144 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2145 return true
2146 else if pname == "/" then
2147 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2148 return true
2149 else if pname == "==" then
2150 v.ret(v.equal_test(arguments[0], arguments[1]))
2151 return true
2152 else if pname == "!=" then
2153 var res = v.equal_test(arguments[0], arguments[1])
2154 v.ret(v.new_expr("!{res}", ret.as(not null)))
2155 return true
2156 else if pname == "<" then
2157 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2158 return true
2159 else if pname == ">" then
2160 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2161 return true
2162 else if pname == "<=" then
2163 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2164 return true
2165 else if pname == ">=" then
2166 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2167 return true
2168 else if pname == "to_i" then
2169 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2170 return true
2171 end
2172 else if cname == "NativeString" then
2173 if pname == "[]" then
2174 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2175 return true
2176 else if pname == "[]=" then
2177 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2178 return true
2179 else if pname == "copy_to" then
2180 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2181 return true
2182 else if pname == "atoi" then
2183 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2184 return true
2185 else if pname == "new" then
2186 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2187 return true
2188 end
2189 else if cname == "NativeArray" then
2190 v.native_array_def(pname, ret, arguments)
2191 return true
2192 end
2193 if pname == "exit" then
2194 v.add("exit({arguments[1]});")
2195 return true
2196 else if pname == "sys" then
2197 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2198 return true
2199 else if pname == "calloc_string" then
2200 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2201 return true
2202 else if pname == "calloc_array" then
2203 v.calloc_array(ret.as(not null), arguments)
2204 return true
2205 else if pname == "object_id" then
2206 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2207 return true
2208 else if pname == "is_same_type" then
2209 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2210 return true
2211 else if pname == "is_same_instance" then
2212 v.ret(v.equal_test(arguments[0], arguments[1]))
2213 return true
2214 else if pname == "output_class_name" then
2215 var nat = v.class_name_string(arguments.first)
2216 v.add("printf(\"%s\\n\", {nat});")
2217 return true
2218 else if pname == "native_class_name" then
2219 var nat = v.class_name_string(arguments.first)
2220 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2221 return true
2222 else if pname == "force_garbage_collection" then
2223 v.add("nit_gcollect();")
2224 return true
2225 else if pname == "native_argc" then
2226 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2227 return true
2228 else if pname == "native_argv" then
2229 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2230 return true
2231 end
2232 return false
2233 end
2234
2235 # Compile an extern method
2236 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2237 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2238 do
2239 var externname
2240 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2241 if at != null and at.n_args.length == 1 then
2242 externname = at.arg_as_string(v.compiler.modelbuilder)
2243 if externname == null then return false
2244 else
2245 return false
2246 end
2247 v.add_extern(mpropdef.mclassdef.mmodule)
2248 var res: nullable RuntimeVariable = null
2249 var ret = mpropdef.msignature.return_mtype
2250 if ret != null then
2251 ret = v.resolve_for(ret, arguments.first)
2252 res = v.new_var_extern(ret)
2253 end
2254 v.adapt_signature(mpropdef, arguments)
2255 v.unbox_signature_extern(mpropdef, arguments)
2256
2257 if res == null then
2258 v.add("{externname}({arguments.join(", ")});")
2259 else
2260 v.add("{res} = {externname}({arguments.join(", ")});")
2261 res = v.box_extern(res, ret.as(not null))
2262 v.ret(res)
2263 end
2264 return true
2265 end
2266
2267 # Compile an extern factory
2268 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2269 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2270 do
2271 var externname
2272 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2273 if at != null then
2274 externname = at.arg_as_string(v.compiler.modelbuilder)
2275 if externname == null then return false
2276 else
2277 return false
2278 end
2279 v.add_extern(mpropdef.mclassdef.mmodule)
2280 v.adapt_signature(mpropdef, arguments)
2281 v.unbox_signature_extern(mpropdef, arguments)
2282 var ret = arguments.first.mtype
2283 var res = v.new_var_extern(ret)
2284
2285 arguments.shift
2286
2287 v.add("{res} = {externname}({arguments.join(", ")});")
2288 res = v.box_extern(res, ret)
2289 v.ret(res)
2290 return true
2291 end
2292 end
2293
2294 redef class AAttrPropdef
2295 redef fun can_inline: Bool do return not is_lazy
2296
2297 redef fun compile_to_c(v, mpropdef, arguments)
2298 do
2299 if mpropdef == mreadpropdef then
2300 assert arguments.length == 1
2301 var recv = arguments.first
2302 var res
2303 if is_lazy then
2304 var set
2305 var ret = self.mpropdef.static_mtype
2306 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2307 var guard = self.mlazypropdef.mproperty
2308 if useiset then
2309 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2310 else
2311 set = v.read_attribute(guard, recv)
2312 end
2313 v.add("if(likely({set})) \{")
2314 res = v.read_attribute(self.mpropdef.mproperty, recv)
2315 v.add("\} else \{")
2316
2317 var value = evaluate_expr(v, recv)
2318
2319 v.assign(res, value)
2320 if not useiset then
2321 var true_v = v.bool_instance(true)
2322 v.write_attribute(guard, arguments.first, true_v)
2323 end
2324 v.add("\}")
2325 else
2326 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2327 end
2328 v.assign(v.frame.returnvar.as(not null), res)
2329 else if mpropdef == mwritepropdef then
2330 assert arguments.length == 2
2331 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2332 if is_lazy then
2333 var ret = self.mpropdef.static_mtype
2334 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2335 if not useiset then
2336 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2337 end
2338 end
2339 else
2340 abort
2341 end
2342 end
2343
2344 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2345 do
2346 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2347 end
2348
2349 # Evaluate, store and return the default value of the attribute
2350 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2351 do
2352 var oldnode = v.current_node
2353 v.current_node = self
2354 var old_frame = v.frame
2355 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2356 v.frame = frame
2357
2358 var value
2359 var mtype = self.mpropdef.static_mtype
2360 assert mtype != null
2361
2362 var nexpr = self.n_expr
2363 var nblock = self.n_block
2364 if nexpr != null then
2365 value = v.expr(nexpr, mtype)
2366 else if nblock != null then
2367 value = v.new_var(mtype)
2368 frame.returnvar = value
2369 frame.returnlabel = v.get_name("RET_LABEL")
2370 v.add("\{")
2371 v.stmt(nblock)
2372 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2373 v.add("\}")
2374 else
2375 abort
2376 end
2377
2378 v.write_attribute(self.mpropdef.mproperty, recv, value)
2379
2380 v.frame = old_frame
2381 v.current_node = oldnode
2382
2383 return value
2384 end
2385
2386 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2387 do
2388 var nexpr = self.n_expr
2389 if nexpr != null then return
2390
2391 var oldnode = v.current_node
2392 v.current_node = self
2393 var old_frame = v.frame
2394 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2395 v.frame = frame
2396 # Force read to check the initialization
2397 v.read_attribute(self.mpropdef.mproperty, recv)
2398 v.frame = old_frame
2399 v.current_node = oldnode
2400 end
2401 end
2402
2403 redef class AClassdef
2404 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2405 do
2406 if mpropdef == self.mfree_init then
2407 assert mpropdef.mproperty.is_root_init
2408 assert arguments.length == 1
2409 if not mpropdef.is_intro then
2410 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2411 end
2412 return
2413 else
2414 abort
2415 end
2416 end
2417 end
2418
2419 redef class AExpr
2420 # Try to compile self as an expression
2421 # Do not call this method directly, use `v.expr` instead
2422 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2423 do
2424 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2425 var mtype = self.mtype
2426 if mtype == null then
2427 return null
2428 else
2429 var res = v.new_var(mtype)
2430 v.add("/* {res} = NOT YET {class_name} */")
2431 return res
2432 end
2433 end
2434
2435 # Try to compile self as a statement
2436 # Do not call this method directly, use `v.stmt` instead
2437 private fun stmt(v: AbstractCompilerVisitor)
2438 do
2439 expr(v)
2440 end
2441 end
2442
2443 redef class ABlockExpr
2444 redef fun stmt(v)
2445 do
2446 for e in self.n_expr do v.stmt(e)
2447 end
2448 redef fun expr(v)
2449 do
2450 var last = self.n_expr.last
2451 for e in self.n_expr do
2452 if e == last then break
2453 v.stmt(e)
2454 end
2455 return v.expr(last, null)
2456 end
2457 end
2458
2459 redef class AVardeclExpr
2460 redef fun stmt(v)
2461 do
2462 var variable = self.variable.as(not null)
2463 var ne = self.n_expr
2464 if ne != null then
2465 var i = v.expr(ne, variable.declared_type)
2466 v.assign(v.variable(variable), i)
2467 end
2468 end
2469 end
2470
2471 redef class AVarExpr
2472 redef fun expr(v)
2473 do
2474 var res = v.variable(self.variable.as(not null))
2475 var mtype = self.mtype.as(not null)
2476 return v.autoadapt(res, mtype)
2477 end
2478 end
2479
2480 redef class AVarAssignExpr
2481 redef fun expr(v)
2482 do
2483 var variable = self.variable.as(not null)
2484 var i = v.expr(self.n_value, variable.declared_type)
2485 v.assign(v.variable(variable), i)
2486 return i
2487 end
2488 end
2489
2490 redef class AVarReassignExpr
2491 redef fun stmt(v)
2492 do
2493 var variable = self.variable.as(not null)
2494 var vari = v.variable(variable)
2495 var value = v.expr(self.n_value, variable.declared_type)
2496 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2497 assert res != null
2498 v.assign(v.variable(variable), res)
2499 end
2500 end
2501
2502 redef class ASelfExpr
2503 redef fun expr(v) do return v.frame.arguments.first
2504 end
2505
2506 redef class AEscapeExpr
2507 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2508 end
2509
2510 redef class AReturnExpr
2511 redef fun stmt(v)
2512 do
2513 var nexpr = self.n_expr
2514 if nexpr != null then
2515 var returnvar = v.frame.returnvar.as(not null)
2516 var i = v.expr(nexpr, returnvar.mtype)
2517 v.assign(returnvar, i)
2518 end
2519 v.add("goto {v.frame.returnlabel.as(not null)};")
2520 end
2521 end
2522
2523 redef class AAbortExpr
2524 redef fun stmt(v) do v.add_abort("Aborted")
2525 end
2526
2527 redef class AIfExpr
2528 redef fun stmt(v)
2529 do
2530 var cond = v.expr_bool(self.n_expr)
2531 v.add("if ({cond})\{")
2532 v.stmt(self.n_then)
2533 v.add("\} else \{")
2534 v.stmt(self.n_else)
2535 v.add("\}")
2536 end
2537
2538 redef fun expr(v)
2539 do
2540 var res = v.new_var(self.mtype.as(not null))
2541 var cond = v.expr_bool(self.n_expr)
2542 v.add("if ({cond})\{")
2543 v.assign(res, v.expr(self.n_then.as(not null), null))
2544 v.add("\} else \{")
2545 v.assign(res, v.expr(self.n_else.as(not null), null))
2546 v.add("\}")
2547 return res
2548 end
2549 end
2550
2551 redef class AIfexprExpr
2552 redef fun expr(v)
2553 do
2554 var res = v.new_var(self.mtype.as(not null))
2555 var cond = v.expr_bool(self.n_expr)
2556 v.add("if ({cond})\{")
2557 v.assign(res, v.expr(self.n_then, null))
2558 v.add("\} else \{")
2559 v.assign(res, v.expr(self.n_else, null))
2560 v.add("\}")
2561 return res
2562 end
2563 end
2564
2565 redef class ADoExpr
2566 redef fun stmt(v)
2567 do
2568 v.stmt(self.n_block)
2569 v.add_escape_label(break_mark)
2570 end
2571 end
2572
2573 redef class AWhileExpr
2574 redef fun stmt(v)
2575 do
2576 v.add("for(;;) \{")
2577 var cond = v.expr_bool(self.n_expr)
2578 v.add("if (!{cond}) break;")
2579 v.stmt(self.n_block)
2580 v.add_escape_label(continue_mark)
2581 v.add("\}")
2582 v.add_escape_label(break_mark)
2583 end
2584 end
2585
2586 redef class ALoopExpr
2587 redef fun stmt(v)
2588 do
2589 v.add("for(;;) \{")
2590 v.stmt(self.n_block)
2591 v.add_escape_label(continue_mark)
2592 v.add("\}")
2593 v.add_escape_label(break_mark)
2594 end
2595 end
2596
2597 redef class AForExpr
2598 redef fun stmt(v)
2599 do
2600 var cl = v.expr(self.n_expr, null)
2601 var it_meth = self.method_iterator
2602 assert it_meth != null
2603 var it = v.compile_callsite(it_meth, [cl])
2604 assert it != null
2605 v.add("for(;;) \{")
2606 var isok_meth = self.method_is_ok
2607 assert isok_meth != null
2608 var ok = v.compile_callsite(isok_meth, [it])
2609 assert ok != null
2610 v.add("if(!{ok}) break;")
2611 if self.variables.length == 1 then
2612 var item_meth = self.method_item
2613 assert item_meth != null
2614 var i = v.compile_callsite(item_meth, [it])
2615 assert i != null
2616 v.assign(v.variable(variables.first), i)
2617 else if self.variables.length == 2 then
2618 var key_meth = self.method_key
2619 assert key_meth != null
2620 var i = v.compile_callsite(key_meth, [it])
2621 assert i != null
2622 v.assign(v.variable(variables[0]), i)
2623 var item_meth = self.method_item
2624 assert item_meth != null
2625 i = v.compile_callsite(item_meth, [it])
2626 assert i != null
2627 v.assign(v.variable(variables[1]), i)
2628 else
2629 abort
2630 end
2631 v.stmt(self.n_block)
2632 v.add_escape_label(continue_mark)
2633 var next_meth = self.method_next
2634 assert next_meth != null
2635 v.compile_callsite(next_meth, [it])
2636 v.add("\}")
2637 v.add_escape_label(break_mark)
2638
2639 var method_finish = self.method_finish
2640 if method_finish != null then
2641 # TODO: Find a way to call this also in long escape (e.g. return)
2642 v.compile_callsite(method_finish, [it])
2643 end
2644 end
2645 end
2646
2647 redef class AAssertExpr
2648 redef fun stmt(v)
2649 do
2650 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2651
2652 var cond = v.expr_bool(self.n_expr)
2653 v.add("if (unlikely(!{cond})) \{")
2654 v.stmt(self.n_else)
2655 var nid = self.n_id
2656 if nid != null then
2657 v.add_abort("Assert '{nid.text}' failed")
2658 else
2659 v.add_abort("Assert failed")
2660 end
2661 v.add("\}")
2662 end
2663 end
2664
2665 redef class AOrExpr
2666 redef fun expr(v)
2667 do
2668 var res = v.new_var(self.mtype.as(not null))
2669 var i1 = v.expr_bool(self.n_expr)
2670 v.add("if ({i1}) \{")
2671 v.add("{res} = 1;")
2672 v.add("\} else \{")
2673 var i2 = v.expr_bool(self.n_expr2)
2674 v.add("{res} = {i2};")
2675 v.add("\}")
2676 return res
2677 end
2678 end
2679
2680 redef class AImpliesExpr
2681 redef fun expr(v)
2682 do
2683 var res = v.new_var(self.mtype.as(not null))
2684 var i1 = v.expr_bool(self.n_expr)
2685 v.add("if (!{i1}) \{")
2686 v.add("{res} = 1;")
2687 v.add("\} else \{")
2688 var i2 = v.expr_bool(self.n_expr2)
2689 v.add("{res} = {i2};")
2690 v.add("\}")
2691 return res
2692 end
2693 end
2694
2695 redef class AAndExpr
2696 redef fun expr(v)
2697 do
2698 var res = v.new_var(self.mtype.as(not null))
2699 var i1 = v.expr_bool(self.n_expr)
2700 v.add("if (!{i1}) \{")
2701 v.add("{res} = 0;")
2702 v.add("\} else \{")
2703 var i2 = v.expr_bool(self.n_expr2)
2704 v.add("{res} = {i2};")
2705 v.add("\}")
2706 return res
2707 end
2708 end
2709
2710 redef class ANotExpr
2711 redef fun expr(v)
2712 do
2713 var cond = v.expr_bool(self.n_expr)
2714 return v.new_expr("!{cond}", self.mtype.as(not null))
2715 end
2716 end
2717
2718 redef class AOrElseExpr
2719 redef fun expr(v)
2720 do
2721 var res = v.new_var(self.mtype.as(not null))
2722 var i1 = v.expr(self.n_expr, null)
2723 v.add("if ({i1}!=NULL) \{")
2724 v.assign(res, i1)
2725 v.add("\} else \{")
2726 var i2 = v.expr(self.n_expr2, null)
2727 v.assign(res, i2)
2728 v.add("\}")
2729 return res
2730 end
2731 end
2732
2733 redef class AIntExpr
2734 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2735 end
2736
2737 redef class AFloatExpr
2738 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2739 end
2740
2741 redef class ACharExpr
2742 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2743 end
2744
2745 redef class AArrayExpr
2746 redef fun expr(v)
2747 do
2748 var mtype = self.element_mtype.as(not null)
2749 var array = new Array[RuntimeVariable]
2750 var res = v.array_instance(array, mtype)
2751
2752 var old_comprehension = v.frame.comprehension
2753 v.frame.comprehension = res
2754 for nexpr in self.n_exprs do
2755 v.stmt(nexpr)
2756 end
2757 v.frame.comprehension = old_comprehension
2758
2759 return res
2760 end
2761 end
2762
2763 redef class AStringFormExpr
2764 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2765 end
2766
2767 redef class ASuperstringExpr
2768 redef fun expr(v)
2769 do
2770 var array = new Array[RuntimeVariable]
2771 for ne in self.n_exprs do
2772 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2773 var i = v.expr(ne, null)
2774 array.add(i)
2775 end
2776 var a = v.array_instance(array, v.object_type)
2777 var res = v.send(v.get_property("to_s", a.mtype), [a])
2778 return res
2779 end
2780 end
2781
2782 redef class ACrangeExpr
2783 redef fun expr(v)
2784 do
2785 var i1 = v.expr(self.n_expr, null)
2786 var i2 = v.expr(self.n_expr2, null)
2787 var mtype = self.mtype.as(MClassType)
2788 var res = v.init_instance(mtype)
2789 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2790 return res
2791 end
2792 end
2793
2794 redef class AOrangeExpr
2795 redef fun expr(v)
2796 do
2797 var i1 = v.expr(self.n_expr, null)
2798 var i2 = v.expr(self.n_expr2, null)
2799 var mtype = self.mtype.as(MClassType)
2800 var res = v.init_instance(mtype)
2801 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2802 return res
2803 end
2804 end
2805
2806 redef class ATrueExpr
2807 redef fun expr(v) do return v.bool_instance(true)
2808 end
2809
2810 redef class AFalseExpr
2811 redef fun expr(v) do return v.bool_instance(false)
2812 end
2813
2814 redef class ANullExpr
2815 redef fun expr(v) do return v.null_instance
2816 end
2817
2818 redef class AIsaExpr
2819 redef fun expr(v)
2820 do
2821 var i = v.expr(self.n_expr, null)
2822 return v.type_test(i, self.cast_type.as(not null), "isa")
2823 end
2824 end
2825
2826 redef class AAsCastExpr
2827 redef fun expr(v)
2828 do
2829 var i = v.expr(self.n_expr, null)
2830 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2831
2832 v.add_cast(i, self.mtype.as(not null), "as")
2833 return i
2834 end
2835 end
2836
2837 redef class AAsNotnullExpr
2838 redef fun expr(v)
2839 do
2840 var i = v.expr(self.n_expr, null)
2841 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2842
2843 if i.mtype.is_c_primitive then return i
2844
2845 v.add("if (unlikely({i} == NULL)) \{")
2846 v.add_abort("Cast failed")
2847 v.add("\}")
2848 return i
2849 end
2850 end
2851
2852 redef class AParExpr
2853 redef fun expr(v) do return v.expr(self.n_expr, null)
2854 end
2855
2856 redef class AOnceExpr
2857 redef fun expr(v)
2858 do
2859 var mtype = self.mtype.as(not null)
2860 var name = v.get_name("varonce")
2861 var guard = v.get_name(name + "_guard")
2862 v.add_decl("static {mtype.ctype} {name};")
2863 v.add_decl("static int {guard};")
2864 var res = v.new_var(mtype)
2865 v.add("if (likely({guard})) \{")
2866 v.add("{res} = {name};")
2867 v.add("\} else \{")
2868 var i = v.expr(self.n_expr, mtype)
2869 v.add("{res} = {i};")
2870 v.add("{name} = {res};")
2871 v.add("{guard} = 1;")
2872 v.add("\}")
2873 return res
2874 end
2875 end
2876
2877 redef class ASendExpr
2878 redef fun expr(v)
2879 do
2880 var recv = v.expr(self.n_expr, null)
2881 var callsite = self.callsite.as(not null)
2882 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2883 return v.compile_callsite(callsite, args)
2884 end
2885 end
2886
2887 redef class ASendReassignFormExpr
2888 redef fun stmt(v)
2889 do
2890 var recv = v.expr(self.n_expr, null)
2891 var callsite = self.callsite.as(not null)
2892 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2893
2894 var value = v.expr(self.n_value, null)
2895
2896 var left = v.compile_callsite(callsite, args)
2897 assert left != null
2898
2899 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2900 assert res != null
2901
2902 args.add(res)
2903 v.compile_callsite(self.write_callsite.as(not null), args)
2904 end
2905 end
2906
2907 redef class ASuperExpr
2908 redef fun expr(v)
2909 do
2910 var recv = v.frame.arguments.first
2911
2912 var callsite = self.callsite
2913 if callsite != null then
2914 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2915
2916 # Add additional arguments for the super init call
2917 if args.length == 1 then
2918 for i in [0..callsite.msignature.arity[ do
2919 args.add(v.frame.arguments[i+1])
2920 end
2921 end
2922 # Super init call
2923 var res = v.compile_callsite(callsite, args)
2924 return res
2925 end
2926
2927 var mpropdef = self.mpropdef.as(not null)
2928 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2929 if args.length == 1 then
2930 args = v.frame.arguments
2931 end
2932
2933 # stantard call-next-method
2934 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
2935 end
2936 end
2937
2938 redef class ANewExpr
2939 redef fun expr(v)
2940 do
2941 var mtype = self.recvtype
2942 assert mtype != null
2943
2944 if mtype.mclass.name == "NativeArray" then
2945 assert self.n_args.n_exprs.length == 1
2946 var l = v.expr(self.n_args.n_exprs.first, null)
2947 assert mtype isa MGenericType
2948 var elttype = mtype.arguments.first
2949 return v.native_array_instance(elttype, l)
2950 end
2951
2952 var recv = v.init_instance_or_extern(mtype)
2953
2954 var callsite = self.callsite.as(not null)
2955 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2956 var res2 = v.compile_callsite(callsite, args)
2957 if res2 != null then
2958 #self.debug("got {res2} from {mproperty}. drop {recv}")
2959 return res2
2960 end
2961 return recv
2962 end
2963 end
2964
2965 redef class AAttrExpr
2966 redef fun expr(v)
2967 do
2968 var recv = v.expr(self.n_expr, null)
2969 var mproperty = self.mproperty.as(not null)
2970 return v.read_attribute(mproperty, recv)
2971 end
2972 end
2973
2974 redef class AAttrAssignExpr
2975 redef fun expr(v)
2976 do
2977 var recv = v.expr(self.n_expr, null)
2978 var i = v.expr(self.n_value, null)
2979 var mproperty = self.mproperty.as(not null)
2980 v.write_attribute(mproperty, recv, i)
2981 return i
2982 end
2983 end
2984
2985 redef class AAttrReassignExpr
2986 redef fun stmt(v)
2987 do
2988 var recv = v.expr(self.n_expr, null)
2989 var value = v.expr(self.n_value, null)
2990 var mproperty = self.mproperty.as(not null)
2991 var attr = v.read_attribute(mproperty, recv)
2992 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2993 assert res != null
2994 v.write_attribute(mproperty, recv, res)
2995 end
2996 end
2997
2998 redef class AIssetAttrExpr
2999 redef fun expr(v)
3000 do
3001 var recv = v.expr(self.n_expr, null)
3002 var mproperty = self.mproperty.as(not null)
3003 return v.isset_attribute(mproperty, recv)
3004 end
3005 end
3006
3007 redef class ADebugTypeExpr
3008 redef fun stmt(v)
3009 do
3010 # do nothing
3011 end
3012 end
3013
3014 # Utils
3015
3016 redef class Array[E]
3017 # Return a new `Array` with the elements only contened in self and not in `o`
3018 fun -(o: Array[E]): Array[E] do
3019 var res = new Array[E]
3020 for e in self do if not o.has(e) then res.add(e)
3021 return res
3022 end
3023 end
3024
3025 redef class MModule
3026 # All `MProperty` associated to all `MClassDef` of `mclass`
3027 fun properties(mclass: MClass): Set[MProperty] do
3028 if not self.properties_cache.has_key(mclass) then
3029 var properties = new HashSet[MProperty]
3030 var parents = new Array[MClass]
3031 if self.flatten_mclass_hierarchy.has(mclass) then
3032 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3033 end
3034 for parent in parents do
3035 properties.add_all(self.properties(parent))
3036 end
3037 for mclassdef in mclass.mclassdefs do
3038 if not self.in_importation <= mclassdef.mmodule then continue
3039 for mprop in mclassdef.intro_mproperties do
3040 properties.add(mprop)
3041 end
3042 end
3043 self.properties_cache[mclass] = properties
3044 end
3045 return properties_cache[mclass]
3046 end
3047 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3048
3049 # Write FFI and nitni results to file
3050 fun finalize_ffi(c: AbstractCompiler) do end
3051
3052 # Give requided addinional system libraries (as given to LD_LIBS)
3053 # Note: can return null instead of an empty set
3054 fun collect_linker_libs: nullable Array[String] do return null
3055 end
3056
3057 # Create a tool context to handle options and paths
3058 var toolcontext = new ToolContext
3059
3060 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3061
3062 # We do not add other options, so process them now!
3063 toolcontext.process_options(args)
3064
3065 # We need a model to collect stufs
3066 var model = new Model
3067 # An a model builder to parse files
3068 var modelbuilder = new ModelBuilder(model, toolcontext)
3069
3070 var arguments = toolcontext.option_context.rest
3071 if arguments.length > 1 and toolcontext.opt_output.value != null then
3072 print "Error: --output needs a single source file. Do you prefer --dir?"
3073 exit 1
3074 end
3075
3076 # Here we load an process all modules passed on the command line
3077 var mmodules = modelbuilder.parse(arguments)
3078
3079 if mmodules.is_empty then return
3080 modelbuilder.run_phases
3081
3082 for mmodule in mmodules do
3083 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3084 var ms = [mmodule]
3085 toolcontext.run_global_phases(ms)
3086 end