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