Merge: Simplify management of primitive types
[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 ctype == "val*" 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 end
1789
1790 redef class MClassType
1791
1792 redef fun ctype: String
1793 do
1794 if mclass.name == "Int" then
1795 return "long"
1796 else if mclass.name == "Bool" then
1797 return "short int"
1798 else if mclass.name == "Char" then
1799 return "char"
1800 else if mclass.name == "Float" then
1801 return "double"
1802 else if mclass.name == "NativeString" then
1803 return "char*"
1804 else if mclass.name == "NativeArray" then
1805 return "val*"
1806 else
1807 return "val*"
1808 end
1809 end
1810
1811 redef fun ctype_extern: String
1812 do
1813 if mclass.kind == extern_kind then
1814 return "void*"
1815 else
1816 return ctype
1817 end
1818 end
1819
1820 redef fun ctypename: String
1821 do
1822 if mclass.name == "Int" then
1823 return "l"
1824 else if mclass.name == "Bool" then
1825 return "s"
1826 else if mclass.name == "Char" then
1827 return "c"
1828 else if mclass.name == "Float" then
1829 return "d"
1830 else if mclass.name == "NativeString" then
1831 return "str"
1832 else if mclass.name == "NativeArray" then
1833 #return "{self.arguments.first.ctype}*"
1834 return "val"
1835 else
1836 return "val"
1837 end
1838 end
1839 end
1840
1841 redef class MPropDef
1842 type VISITOR: AbstractCompilerVisitor
1843 end
1844
1845 redef class MMethodDef
1846 # Can the body be inlined?
1847 fun can_inline(v: VISITOR): Bool
1848 do
1849 if is_abstract then return true
1850 var modelbuilder = v.compiler.modelbuilder
1851 var node = modelbuilder.mpropdef2node(self)
1852 if node isa APropdef then
1853 return node.can_inline
1854 else if node isa AClassdef then
1855 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1856 return true
1857 else
1858 abort
1859 end
1860 end
1861
1862 # Inline the body in another visitor
1863 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1864 do
1865 var modelbuilder = v.compiler.modelbuilder
1866 var val = constant_value
1867 var node = modelbuilder.mpropdef2node(self)
1868
1869 if is_abstract then
1870 var cn = v.class_name_string(arguments.first)
1871 v.current_node = node
1872 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1873 v.add_raw_abort
1874 return null
1875 end
1876
1877 if node isa APropdef then
1878 var oldnode = v.current_node
1879 v.current_node = node
1880 self.compile_parameter_check(v, arguments)
1881 node.compile_to_c(v, self, arguments)
1882 v.current_node = oldnode
1883 else if node isa AClassdef 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 val != null then
1890 v.ret(v.value_instance(val))
1891 else
1892 abort
1893 end
1894 return null
1895 end
1896
1897 # Generate type checks in the C code to check covariant parameters
1898 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1899 do
1900 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1901
1902 for i in [0..msignature.arity[ do
1903 # skip test for vararg since the array is instantiated with the correct polymorphic type
1904 if msignature.vararg_rank == i then continue
1905
1906 # skip if the cast is not required
1907 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1908 if not origmtype.need_anchor then continue
1909
1910 # get the parameter type
1911 var mtype = self.msignature.mparameters[i].mtype
1912
1913 # generate the cast
1914 # note that v decides if and how to implements the cast
1915 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1916 v.add_cast(arguments[i+1], mtype, "covariance")
1917 end
1918 end
1919 end
1920
1921 # Node visit
1922
1923 redef class APropdef
1924 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1925 do
1926 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1927 debug("Not yet implemented")
1928 end
1929
1930 fun can_inline: Bool do return true
1931 end
1932
1933 redef class AMethPropdef
1934 redef fun compile_to_c(v, mpropdef, arguments)
1935 do
1936 # Call the implicit super-init
1937 var auto_super_inits = self.auto_super_inits
1938 if auto_super_inits != null then
1939 var args = [arguments.first]
1940 for auto_super_init in auto_super_inits do
1941 assert auto_super_init.mproperty != mpropdef.mproperty
1942 args.clear
1943 for i in [0..auto_super_init.msignature.arity+1[ do
1944 args.add(arguments[i])
1945 end
1946 assert auto_super_init.mproperty != mpropdef.mproperty
1947 v.compile_callsite(auto_super_init, args)
1948 end
1949 end
1950 if auto_super_call then
1951 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1952 end
1953
1954 # Try special compilation
1955 if mpropdef.is_intern then
1956 if compile_intern_to_c(v, mpropdef, arguments) then return
1957 else if mpropdef.is_extern then
1958 if mpropdef.mproperty.is_init then
1959 if compile_externinit_to_c(v, mpropdef, arguments) then return
1960 else
1961 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1962 end
1963 end
1964
1965 # Compile block if any
1966 var n_block = n_block
1967 if n_block != null then
1968 for i in [0..mpropdef.msignature.arity[ do
1969 var variable = self.n_signature.n_params[i].variable.as(not null)
1970 v.assign(v.variable(variable), arguments[i+1])
1971 end
1972 v.stmt(n_block)
1973 return
1974 end
1975
1976 # We have a problem
1977 var cn = v.class_name_string(arguments.first)
1978 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1979 v.add_raw_abort
1980 end
1981
1982 redef fun can_inline
1983 do
1984 if self.auto_super_inits != null then return false
1985 var nblock = self.n_block
1986 if nblock == null then return true
1987 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1988 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1989 return false
1990 end
1991
1992 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1993 do
1994 var pname = mpropdef.mproperty.name
1995 var cname = mpropdef.mclassdef.mclass.name
1996 var ret = mpropdef.msignature.return_mtype
1997 if ret != null then
1998 ret = v.resolve_for(ret, arguments.first)
1999 end
2000 if pname != "==" and pname != "!=" then
2001 v.adapt_signature(mpropdef, arguments)
2002 v.unbox_signature_extern(mpropdef, arguments)
2003 end
2004 if cname == "Int" then
2005 if pname == "output" then
2006 v.add("printf(\"%ld\\n\", {arguments.first});")
2007 return true
2008 else if pname == "object_id" then
2009 v.ret(arguments.first)
2010 return true
2011 else if pname == "+" then
2012 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2013 return true
2014 else if pname == "-" then
2015 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2016 return true
2017 else if pname == "unary -" then
2018 v.ret(v.new_expr("-{arguments[0]}", 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 == "/" then
2024 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", 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 == "lshift" then
2030 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2031 return true
2032 else if pname == "rshift" then
2033 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2034 return true
2035 else if pname == "==" then
2036 v.ret(v.equal_test(arguments[0], arguments[1]))
2037 return true
2038 else if pname == "!=" then
2039 var res = v.equal_test(arguments[0], arguments[1])
2040 v.ret(v.new_expr("!{res}", ret.as(not null)))
2041 return true
2042 else if pname == "<" then
2043 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2044 return true
2045 else if pname == ">" then
2046 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", 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 == "to_f" then
2055 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2056 return true
2057 else if pname == "ascii" then
2058 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2059 return true
2060 end
2061 else if cname == "Char" then
2062 if pname == "output" then
2063 v.add("printf(\"%c\", {arguments.first});")
2064 return true
2065 else if pname == "object_id" then
2066 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2067 return true
2068 else if pname == "successor" then
2069 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2070 return true
2071 else if pname == "predecessor" then
2072 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2073 return true
2074 else if pname == "==" then
2075 v.ret(v.equal_test(arguments[0], arguments[1]))
2076 return true
2077 else if pname == "!=" then
2078 var res = v.equal_test(arguments[0], arguments[1])
2079 v.ret(v.new_expr("!{res}", ret.as(not null)))
2080 return true
2081 else if pname == "<" then
2082 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2083 return true
2084 else if pname == ">" then
2085 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2086 return true
2087 else if pname == "<=" then
2088 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2089 return true
2090 else if pname == ">=" then
2091 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2092 return true
2093 else if pname == "to_i" then
2094 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2095 return true
2096 else if pname == "ascii" then
2097 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2098 return true
2099 end
2100 else if cname == "Bool" then
2101 if pname == "output" then
2102 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2103 return true
2104 else if pname == "object_id" then
2105 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2106 return true
2107 else if pname == "==" then
2108 v.ret(v.equal_test(arguments[0], arguments[1]))
2109 return true
2110 else if pname == "!=" then
2111 var res = v.equal_test(arguments[0], arguments[1])
2112 v.ret(v.new_expr("!{res}", ret.as(not null)))
2113 return true
2114 end
2115 else if cname == "Float" then
2116 if pname == "output" then
2117 v.add("printf(\"%f\\n\", {arguments.first});")
2118 return true
2119 else if pname == "object_id" then
2120 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2121 return true
2122 else if pname == "+" then
2123 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2124 return true
2125 else if pname == "-" then
2126 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2127 return true
2128 else if pname == "unary -" then
2129 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2130 return true
2131 else if pname == "succ" then
2132 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2133 return true
2134 else if pname == "prec" then
2135 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2136 return true
2137 else if pname == "*" then
2138 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2139 return true
2140 else if pname == "/" then
2141 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2142 return true
2143 else if pname == "==" then
2144 v.ret(v.equal_test(arguments[0], arguments[1]))
2145 return true
2146 else if pname == "!=" then
2147 var res = v.equal_test(arguments[0], arguments[1])
2148 v.ret(v.new_expr("!{res}", ret.as(not null)))
2149 return true
2150 else if pname == "<" then
2151 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2152 return true
2153 else if pname == ">" then
2154 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", 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 == "to_i" then
2163 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2164 return true
2165 end
2166 else if cname == "NativeString" then
2167 if pname == "[]" then
2168 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2169 return true
2170 else if pname == "[]=" then
2171 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2172 return true
2173 else if pname == "copy_to" then
2174 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2175 return true
2176 else if pname == "atoi" then
2177 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2178 return true
2179 else if pname == "new" then
2180 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2181 return true
2182 end
2183 else if cname == "NativeArray" then
2184 v.native_array_def(pname, ret, arguments)
2185 return true
2186 end
2187 if pname == "exit" then
2188 v.add("exit({arguments[1]});")
2189 return true
2190 else if pname == "sys" then
2191 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2192 return true
2193 else if pname == "calloc_string" then
2194 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2195 return true
2196 else if pname == "calloc_array" then
2197 v.calloc_array(ret.as(not null), arguments)
2198 return true
2199 else if pname == "object_id" then
2200 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2201 return true
2202 else if pname == "is_same_type" then
2203 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2204 return true
2205 else if pname == "is_same_instance" then
2206 v.ret(v.equal_test(arguments[0], arguments[1]))
2207 return true
2208 else if pname == "output_class_name" then
2209 var nat = v.class_name_string(arguments.first)
2210 v.add("printf(\"%s\\n\", {nat});")
2211 return true
2212 else if pname == "native_class_name" then
2213 var nat = v.class_name_string(arguments.first)
2214 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2215 return true
2216 else if pname == "force_garbage_collection" then
2217 v.add("nit_gcollect();")
2218 return true
2219 else if pname == "native_argc" then
2220 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2221 return true
2222 else if pname == "native_argv" then
2223 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2224 return true
2225 end
2226 return false
2227 end
2228
2229 # Compile an extern method
2230 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2231 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2232 do
2233 var externname
2234 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2235 if at != null and at.n_args.length == 1 then
2236 externname = at.arg_as_string(v.compiler.modelbuilder)
2237 if externname == null then return false
2238 else
2239 return false
2240 end
2241 v.add_extern(mpropdef.mclassdef.mmodule)
2242 var res: nullable RuntimeVariable = null
2243 var ret = mpropdef.msignature.return_mtype
2244 if ret != null then
2245 ret = v.resolve_for(ret, arguments.first)
2246 res = v.new_var_extern(ret)
2247 end
2248 v.adapt_signature(mpropdef, arguments)
2249 v.unbox_signature_extern(mpropdef, arguments)
2250
2251 if res == null then
2252 v.add("{externname}({arguments.join(", ")});")
2253 else
2254 v.add("{res} = {externname}({arguments.join(", ")});")
2255 res = v.box_extern(res, ret.as(not null))
2256 v.ret(res)
2257 end
2258 return true
2259 end
2260
2261 # Compile an extern factory
2262 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2263 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2264 do
2265 var externname
2266 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2267 if at != null then
2268 externname = at.arg_as_string(v.compiler.modelbuilder)
2269 if externname == null then return false
2270 else
2271 return false
2272 end
2273 v.add_extern(mpropdef.mclassdef.mmodule)
2274 v.adapt_signature(mpropdef, arguments)
2275 v.unbox_signature_extern(mpropdef, arguments)
2276 var ret = arguments.first.mtype
2277 var res = v.new_var_extern(ret)
2278
2279 arguments.shift
2280
2281 v.add("{res} = {externname}({arguments.join(", ")});")
2282 res = v.box_extern(res, ret)
2283 v.ret(res)
2284 return true
2285 end
2286 end
2287
2288 redef class AAttrPropdef
2289 redef fun can_inline: Bool do return not is_lazy
2290
2291 redef fun compile_to_c(v, mpropdef, arguments)
2292 do
2293 if mpropdef == mreadpropdef then
2294 assert arguments.length == 1
2295 var recv = arguments.first
2296 var res
2297 if is_lazy then
2298 var set
2299 var ret = self.mpropdef.static_mtype
2300 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2301 var guard = self.mlazypropdef.mproperty
2302 if useiset then
2303 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2304 else
2305 set = v.read_attribute(guard, recv)
2306 end
2307 v.add("if(likely({set})) \{")
2308 res = v.read_attribute(self.mpropdef.mproperty, recv)
2309 v.add("\} else \{")
2310
2311 var value = evaluate_expr(v, recv)
2312
2313 v.assign(res, value)
2314 if not useiset then
2315 var true_v = v.bool_instance(true)
2316 v.write_attribute(guard, arguments.first, true_v)
2317 end
2318 v.add("\}")
2319 else
2320 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2321 end
2322 v.assign(v.frame.returnvar.as(not null), res)
2323 else if mpropdef == mwritepropdef then
2324 assert arguments.length == 2
2325 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2326 if is_lazy then
2327 var ret = self.mpropdef.static_mtype
2328 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2329 if not useiset then
2330 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
2331 end
2332 end
2333 else
2334 abort
2335 end
2336 end
2337
2338 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2339 do
2340 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2341 end
2342
2343 # Evaluate, store and return the default value of the attribute
2344 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2345 do
2346 var oldnode = v.current_node
2347 v.current_node = self
2348 var old_frame = v.frame
2349 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2350 v.frame = frame
2351
2352 var value
2353 var mtype = self.mpropdef.static_mtype
2354 assert mtype != null
2355
2356 var nexpr = self.n_expr
2357 var nblock = self.n_block
2358 if nexpr != null then
2359 value = v.expr(nexpr, mtype)
2360 else if nblock != null then
2361 value = v.new_var(mtype)
2362 frame.returnvar = value
2363 frame.returnlabel = v.get_name("RET_LABEL")
2364 v.add("\{")
2365 v.stmt(nblock)
2366 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2367 v.add("\}")
2368 else
2369 abort
2370 end
2371
2372 v.write_attribute(self.mpropdef.mproperty, recv, value)
2373
2374 v.frame = old_frame
2375 v.current_node = oldnode
2376
2377 return value
2378 end
2379
2380 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2381 do
2382 var nexpr = self.n_expr
2383 if nexpr != null then return
2384
2385 var oldnode = v.current_node
2386 v.current_node = self
2387 var old_frame = v.frame
2388 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2389 v.frame = frame
2390 # Force read to check the initialization
2391 v.read_attribute(self.mpropdef.mproperty, recv)
2392 v.frame = old_frame
2393 v.current_node = oldnode
2394 end
2395 end
2396
2397 redef class AClassdef
2398 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2399 do
2400 if mpropdef == self.mfree_init then
2401 assert mpropdef.mproperty.is_root_init
2402 assert arguments.length == 1
2403 if not mpropdef.is_intro then
2404 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2405 end
2406 return
2407 else
2408 abort
2409 end
2410 end
2411 end
2412
2413 redef class AExpr
2414 # Try to compile self as an expression
2415 # Do not call this method directly, use `v.expr` instead
2416 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2417 do
2418 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2419 var mtype = self.mtype
2420 if mtype == null then
2421 return null
2422 else
2423 var res = v.new_var(mtype)
2424 v.add("/* {res} = NOT YET {class_name} */")
2425 return res
2426 end
2427 end
2428
2429 # Try to compile self as a statement
2430 # Do not call this method directly, use `v.stmt` instead
2431 private fun stmt(v: AbstractCompilerVisitor)
2432 do
2433 expr(v)
2434 end
2435 end
2436
2437 redef class ABlockExpr
2438 redef fun stmt(v)
2439 do
2440 for e in self.n_expr do v.stmt(e)
2441 end
2442 redef fun expr(v)
2443 do
2444 var last = self.n_expr.last
2445 for e in self.n_expr do
2446 if e == last then break
2447 v.stmt(e)
2448 end
2449 return v.expr(last, null)
2450 end
2451 end
2452
2453 redef class AVardeclExpr
2454 redef fun stmt(v)
2455 do
2456 var variable = self.variable.as(not null)
2457 var ne = self.n_expr
2458 if ne != null then
2459 var i = v.expr(ne, variable.declared_type)
2460 v.assign(v.variable(variable), i)
2461 end
2462 end
2463 end
2464
2465 redef class AVarExpr
2466 redef fun expr(v)
2467 do
2468 var res = v.variable(self.variable.as(not null))
2469 var mtype = self.mtype.as(not null)
2470 return v.autoadapt(res, mtype)
2471 end
2472 end
2473
2474 redef class AVarAssignExpr
2475 redef fun expr(v)
2476 do
2477 var variable = self.variable.as(not null)
2478 var i = v.expr(self.n_value, variable.declared_type)
2479 v.assign(v.variable(variable), i)
2480 return i
2481 end
2482 end
2483
2484 redef class AVarReassignExpr
2485 redef fun stmt(v)
2486 do
2487 var variable = self.variable.as(not null)
2488 var vari = v.variable(variable)
2489 var value = v.expr(self.n_value, variable.declared_type)
2490 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2491 assert res != null
2492 v.assign(v.variable(variable), res)
2493 end
2494 end
2495
2496 redef class ASelfExpr
2497 redef fun expr(v) do return v.frame.arguments.first
2498 end
2499
2500 redef class AEscapeExpr
2501 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2502 end
2503
2504 redef class AReturnExpr
2505 redef fun stmt(v)
2506 do
2507 var nexpr = self.n_expr
2508 if nexpr != null then
2509 var returnvar = v.frame.returnvar.as(not null)
2510 var i = v.expr(nexpr, returnvar.mtype)
2511 v.assign(returnvar, i)
2512 end
2513 v.add("goto {v.frame.returnlabel.as(not null)};")
2514 end
2515 end
2516
2517 redef class AAbortExpr
2518 redef fun stmt(v) do v.add_abort("Aborted")
2519 end
2520
2521 redef class AIfExpr
2522 redef fun stmt(v)
2523 do
2524 var cond = v.expr_bool(self.n_expr)
2525 v.add("if ({cond})\{")
2526 v.stmt(self.n_then)
2527 v.add("\} else \{")
2528 v.stmt(self.n_else)
2529 v.add("\}")
2530 end
2531
2532 redef fun expr(v)
2533 do
2534 var res = v.new_var(self.mtype.as(not null))
2535 var cond = v.expr_bool(self.n_expr)
2536 v.add("if ({cond})\{")
2537 v.assign(res, v.expr(self.n_then.as(not null), null))
2538 v.add("\} else \{")
2539 v.assign(res, v.expr(self.n_else.as(not null), null))
2540 v.add("\}")
2541 return res
2542 end
2543 end
2544
2545 redef class AIfexprExpr
2546 redef fun expr(v)
2547 do
2548 var res = v.new_var(self.mtype.as(not null))
2549 var cond = v.expr_bool(self.n_expr)
2550 v.add("if ({cond})\{")
2551 v.assign(res, v.expr(self.n_then, null))
2552 v.add("\} else \{")
2553 v.assign(res, v.expr(self.n_else, null))
2554 v.add("\}")
2555 return res
2556 end
2557 end
2558
2559 redef class ADoExpr
2560 redef fun stmt(v)
2561 do
2562 v.stmt(self.n_block)
2563 v.add_escape_label(break_mark)
2564 end
2565 end
2566
2567 redef class AWhileExpr
2568 redef fun stmt(v)
2569 do
2570 v.add("for(;;) \{")
2571 var cond = v.expr_bool(self.n_expr)
2572 v.add("if (!{cond}) break;")
2573 v.stmt(self.n_block)
2574 v.add_escape_label(continue_mark)
2575 v.add("\}")
2576 v.add_escape_label(break_mark)
2577 end
2578 end
2579
2580 redef class ALoopExpr
2581 redef fun stmt(v)
2582 do
2583 v.add("for(;;) \{")
2584 v.stmt(self.n_block)
2585 v.add_escape_label(continue_mark)
2586 v.add("\}")
2587 v.add_escape_label(break_mark)
2588 end
2589 end
2590
2591 redef class AForExpr
2592 redef fun stmt(v)
2593 do
2594 var cl = v.expr(self.n_expr, null)
2595 var it_meth = self.method_iterator
2596 assert it_meth != null
2597 var it = v.compile_callsite(it_meth, [cl])
2598 assert it != null
2599 v.add("for(;;) \{")
2600 var isok_meth = self.method_is_ok
2601 assert isok_meth != null
2602 var ok = v.compile_callsite(isok_meth, [it])
2603 assert ok != null
2604 v.add("if(!{ok}) break;")
2605 if self.variables.length == 1 then
2606 var item_meth = self.method_item
2607 assert item_meth != null
2608 var i = v.compile_callsite(item_meth, [it])
2609 assert i != null
2610 v.assign(v.variable(variables.first), i)
2611 else if self.variables.length == 2 then
2612 var key_meth = self.method_key
2613 assert key_meth != null
2614 var i = v.compile_callsite(key_meth, [it])
2615 assert i != null
2616 v.assign(v.variable(variables[0]), i)
2617 var item_meth = self.method_item
2618 assert item_meth != null
2619 i = v.compile_callsite(item_meth, [it])
2620 assert i != null
2621 v.assign(v.variable(variables[1]), i)
2622 else
2623 abort
2624 end
2625 v.stmt(self.n_block)
2626 v.add_escape_label(continue_mark)
2627 var next_meth = self.method_next
2628 assert next_meth != null
2629 v.compile_callsite(next_meth, [it])
2630 v.add("\}")
2631 v.add_escape_label(break_mark)
2632
2633 var method_finish = self.method_finish
2634 if method_finish != null then
2635 # TODO: Find a way to call this also in long escape (e.g. return)
2636 v.compile_callsite(method_finish, [it])
2637 end
2638 end
2639 end
2640
2641 redef class AAssertExpr
2642 redef fun stmt(v)
2643 do
2644 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2645
2646 var cond = v.expr_bool(self.n_expr)
2647 v.add("if (unlikely(!{cond})) \{")
2648 v.stmt(self.n_else)
2649 var nid = self.n_id
2650 if nid != null then
2651 v.add_abort("Assert '{nid.text}' failed")
2652 else
2653 v.add_abort("Assert failed")
2654 end
2655 v.add("\}")
2656 end
2657 end
2658
2659 redef class AOrExpr
2660 redef fun expr(v)
2661 do
2662 var res = v.new_var(self.mtype.as(not null))
2663 var i1 = v.expr_bool(self.n_expr)
2664 v.add("if ({i1}) \{")
2665 v.add("{res} = 1;")
2666 v.add("\} else \{")
2667 var i2 = v.expr_bool(self.n_expr2)
2668 v.add("{res} = {i2};")
2669 v.add("\}")
2670 return res
2671 end
2672 end
2673
2674 redef class AImpliesExpr
2675 redef fun expr(v)
2676 do
2677 var res = v.new_var(self.mtype.as(not null))
2678 var i1 = v.expr_bool(self.n_expr)
2679 v.add("if (!{i1}) \{")
2680 v.add("{res} = 1;")
2681 v.add("\} else \{")
2682 var i2 = v.expr_bool(self.n_expr2)
2683 v.add("{res} = {i2};")
2684 v.add("\}")
2685 return res
2686 end
2687 end
2688
2689 redef class AAndExpr
2690 redef fun expr(v)
2691 do
2692 var res = v.new_var(self.mtype.as(not null))
2693 var i1 = v.expr_bool(self.n_expr)
2694 v.add("if (!{i1}) \{")
2695 v.add("{res} = 0;")
2696 v.add("\} else \{")
2697 var i2 = v.expr_bool(self.n_expr2)
2698 v.add("{res} = {i2};")
2699 v.add("\}")
2700 return res
2701 end
2702 end
2703
2704 redef class ANotExpr
2705 redef fun expr(v)
2706 do
2707 var cond = v.expr_bool(self.n_expr)
2708 return v.new_expr("!{cond}", self.mtype.as(not null))
2709 end
2710 end
2711
2712 redef class AOrElseExpr
2713 redef fun expr(v)
2714 do
2715 var res = v.new_var(self.mtype.as(not null))
2716 var i1 = v.expr(self.n_expr, null)
2717 v.add("if ({i1}!=NULL) \{")
2718 v.assign(res, i1)
2719 v.add("\} else \{")
2720 var i2 = v.expr(self.n_expr2, null)
2721 v.assign(res, i2)
2722 v.add("\}")
2723 return res
2724 end
2725 end
2726
2727 redef class AIntExpr
2728 redef fun expr(v) do return v.int_instance(self.value.as(not null))
2729 end
2730
2731 redef class AFloatExpr
2732 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
2733 end
2734
2735 redef class ACharExpr
2736 redef fun expr(v) do return v.char_instance(self.value.as(not null))
2737 end
2738
2739 redef class AArrayExpr
2740 redef fun expr(v)
2741 do
2742 var mtype = self.element_mtype.as(not null)
2743 var array = new Array[RuntimeVariable]
2744 var res = v.array_instance(array, mtype)
2745
2746 var old_comprehension = v.frame.comprehension
2747 v.frame.comprehension = res
2748 for nexpr in self.n_exprs do
2749 v.stmt(nexpr)
2750 end
2751 v.frame.comprehension = old_comprehension
2752
2753 return res
2754 end
2755 end
2756
2757 redef class AStringFormExpr
2758 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2759 end
2760
2761 redef class ASuperstringExpr
2762 redef fun expr(v)
2763 do
2764 var array = new Array[RuntimeVariable]
2765 for ne in self.n_exprs do
2766 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2767 var i = v.expr(ne, null)
2768 array.add(i)
2769 end
2770 var a = v.array_instance(array, v.object_type)
2771 var res = v.send(v.get_property("to_s", a.mtype), [a])
2772 return res
2773 end
2774 end
2775
2776 redef class ACrangeExpr
2777 redef fun expr(v)
2778 do
2779 var i1 = v.expr(self.n_expr, null)
2780 var i2 = v.expr(self.n_expr2, null)
2781 var mtype = self.mtype.as(MClassType)
2782 var res = v.init_instance(mtype)
2783 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2784 return res
2785 end
2786 end
2787
2788 redef class AOrangeExpr
2789 redef fun expr(v)
2790 do
2791 var i1 = v.expr(self.n_expr, null)
2792 var i2 = v.expr(self.n_expr2, null)
2793 var mtype = self.mtype.as(MClassType)
2794 var res = v.init_instance(mtype)
2795 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2796 return res
2797 end
2798 end
2799
2800 redef class ATrueExpr
2801 redef fun expr(v) do return v.bool_instance(true)
2802 end
2803
2804 redef class AFalseExpr
2805 redef fun expr(v) do return v.bool_instance(false)
2806 end
2807
2808 redef class ANullExpr
2809 redef fun expr(v) do return v.null_instance
2810 end
2811
2812 redef class AIsaExpr
2813 redef fun expr(v)
2814 do
2815 var i = v.expr(self.n_expr, null)
2816 return v.type_test(i, self.cast_type.as(not null), "isa")
2817 end
2818 end
2819
2820 redef class AAsCastExpr
2821 redef fun expr(v)
2822 do
2823 var i = v.expr(self.n_expr, null)
2824 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2825
2826 v.add_cast(i, self.mtype.as(not null), "as")
2827 return i
2828 end
2829 end
2830
2831 redef class AAsNotnullExpr
2832 redef fun expr(v)
2833 do
2834 var i = v.expr(self.n_expr, null)
2835 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2836
2837 if i.mtype.ctype != "val*" then return i
2838
2839 v.add("if (unlikely({i} == NULL)) \{")
2840 v.add_abort("Cast failed")
2841 v.add("\}")
2842 return i
2843 end
2844 end
2845
2846 redef class AParExpr
2847 redef fun expr(v) do return v.expr(self.n_expr, null)
2848 end
2849
2850 redef class AOnceExpr
2851 redef fun expr(v)
2852 do
2853 var mtype = self.mtype.as(not null)
2854 var name = v.get_name("varonce")
2855 var guard = v.get_name(name + "_guard")
2856 v.add_decl("static {mtype.ctype} {name};")
2857 v.add_decl("static int {guard};")
2858 var res = v.new_var(mtype)
2859 v.add("if (likely({guard})) \{")
2860 v.add("{res} = {name};")
2861 v.add("\} else \{")
2862 var i = v.expr(self.n_expr, mtype)
2863 v.add("{res} = {i};")
2864 v.add("{name} = {res};")
2865 v.add("{guard} = 1;")
2866 v.add("\}")
2867 return res
2868 end
2869 end
2870
2871 redef class ASendExpr
2872 redef fun expr(v)
2873 do
2874 var recv = v.expr(self.n_expr, null)
2875 var callsite = self.callsite.as(not null)
2876 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2877 return v.compile_callsite(callsite, args)
2878 end
2879 end
2880
2881 redef class ASendReassignFormExpr
2882 redef fun stmt(v)
2883 do
2884 var recv = v.expr(self.n_expr, null)
2885 var callsite = self.callsite.as(not null)
2886 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2887
2888 var value = v.expr(self.n_value, null)
2889
2890 var left = v.compile_callsite(callsite, args)
2891 assert left != null
2892
2893 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2894 assert res != null
2895
2896 args.add(res)
2897 v.compile_callsite(self.write_callsite.as(not null), args)
2898 end
2899 end
2900
2901 redef class ASuperExpr
2902 redef fun expr(v)
2903 do
2904 var recv = v.frame.arguments.first
2905
2906 var callsite = self.callsite
2907 if callsite != null then
2908 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2909
2910 # Add additional arguments for the super init call
2911 if args.length == 1 then
2912 for i in [0..callsite.msignature.arity[ do
2913 args.add(v.frame.arguments[i+1])
2914 end
2915 end
2916 # Super init call
2917 var res = v.compile_callsite(callsite, args)
2918 return res
2919 end
2920
2921 var mpropdef = self.mpropdef.as(not null)
2922 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2923 if args.length == 1 then
2924 args = v.frame.arguments
2925 end
2926
2927 # stantard call-next-method
2928 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
2929 end
2930 end
2931
2932 redef class ANewExpr
2933 redef fun expr(v)
2934 do
2935 var mtype = self.recvtype
2936 assert mtype != null
2937
2938 if mtype.mclass.name == "NativeArray" then
2939 assert self.n_args.n_exprs.length == 1
2940 var l = v.expr(self.n_args.n_exprs.first, null)
2941 assert mtype isa MGenericType
2942 var elttype = mtype.arguments.first
2943 return v.native_array_instance(elttype, l)
2944 end
2945
2946 var recv = v.init_instance_or_extern(mtype)
2947
2948 var callsite = self.callsite.as(not null)
2949 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2950 var res2 = v.compile_callsite(callsite, args)
2951 if res2 != null then
2952 #self.debug("got {res2} from {mproperty}. drop {recv}")
2953 return res2
2954 end
2955 return recv
2956 end
2957 end
2958
2959 redef class AAttrExpr
2960 redef fun expr(v)
2961 do
2962 var recv = v.expr(self.n_expr, null)
2963 var mproperty = self.mproperty.as(not null)
2964 return v.read_attribute(mproperty, recv)
2965 end
2966 end
2967
2968 redef class AAttrAssignExpr
2969 redef fun expr(v)
2970 do
2971 var recv = v.expr(self.n_expr, null)
2972 var i = v.expr(self.n_value, null)
2973 var mproperty = self.mproperty.as(not null)
2974 v.write_attribute(mproperty, recv, i)
2975 return i
2976 end
2977 end
2978
2979 redef class AAttrReassignExpr
2980 redef fun stmt(v)
2981 do
2982 var recv = v.expr(self.n_expr, null)
2983 var value = v.expr(self.n_value, null)
2984 var mproperty = self.mproperty.as(not null)
2985 var attr = v.read_attribute(mproperty, recv)
2986 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2987 assert res != null
2988 v.write_attribute(mproperty, recv, res)
2989 end
2990 end
2991
2992 redef class AIssetAttrExpr
2993 redef fun expr(v)
2994 do
2995 var recv = v.expr(self.n_expr, null)
2996 var mproperty = self.mproperty.as(not null)
2997 return v.isset_attribute(mproperty, recv)
2998 end
2999 end
3000
3001 redef class ADebugTypeExpr
3002 redef fun stmt(v)
3003 do
3004 # do nothing
3005 end
3006 end
3007
3008 # Utils
3009
3010 redef class Array[E]
3011 # Return a new `Array` with the elements only contened in self and not in `o`
3012 fun -(o: Array[E]): Array[E] do
3013 var res = new Array[E]
3014 for e in self do if not o.has(e) then res.add(e)
3015 return res
3016 end
3017 end
3018
3019 redef class MModule
3020 # All `MProperty` associated to all `MClassDef` of `mclass`
3021 fun properties(mclass: MClass): Set[MProperty] do
3022 if not self.properties_cache.has_key(mclass) then
3023 var properties = new HashSet[MProperty]
3024 var parents = new Array[MClass]
3025 if self.flatten_mclass_hierarchy.has(mclass) then
3026 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3027 end
3028 for parent in parents do
3029 properties.add_all(self.properties(parent))
3030 end
3031 for mclassdef in mclass.mclassdefs do
3032 if not self.in_importation <= mclassdef.mmodule then continue
3033 for mprop in mclassdef.intro_mproperties do
3034 properties.add(mprop)
3035 end
3036 end
3037 self.properties_cache[mclass] = properties
3038 end
3039 return properties_cache[mclass]
3040 end
3041 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3042
3043 # Write FFI and nitni results to file
3044 fun finalize_ffi(c: AbstractCompiler) do end
3045
3046 # Give requided addinional system libraries (as given to LD_LIBS)
3047 # Note: can return null instead of an empty set
3048 fun collect_linker_libs: nullable Array[String] do return null
3049 end
3050
3051 # Create a tool context to handle options and paths
3052 var toolcontext = new ToolContext
3053
3054 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3055
3056 # We do not add other options, so process them now!
3057 toolcontext.process_options(args)
3058
3059 # We need a model to collect stufs
3060 var model = new Model
3061 # An a model builder to parse files
3062 var modelbuilder = new ModelBuilder(model, toolcontext)
3063
3064 var arguments = toolcontext.option_context.rest
3065 if arguments.length > 1 and toolcontext.opt_output.value != null then
3066 print "Error: --output needs a single source file. Do you prefer --dir?"
3067 exit 1
3068 end
3069
3070 # Here we load an process all modules passed on the command line
3071 var mmodules = modelbuilder.parse(arguments)
3072
3073 if mmodules.is_empty then return
3074 modelbuilder.run_phases
3075
3076 for mmodule in mmodules do
3077 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3078 var ms = [mmodule]
3079 toolcontext.run_global_phases(ms)
3080 end