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