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