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