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