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