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