compiler: remove --clib-path and NIT_CC_PATH
[nit.git] / src / compiler / abstract_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract compiler
18 module abstract_compiler
19
20 import literal
21 import semantize
22 import platform
23 import c_tools
24 private import annotation
25 import mixin
26
27 # Add compiling options
28 redef class ToolContext
29 # --output
30 var opt_output = new OptionString("Output file", "-o", "--output")
31 # --dir
32 var opt_dir = new OptionString("Output directory", "--dir")
33 # --no-cc
34 var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
35 # --no-main
36 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
37 # --make-flags
38 var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
39 # --max-c-lines
40 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
41 # --group-c-files
42 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
43 # --compile-dir
44 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
45 # --hardening
46 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
47 # --no-check-covariance
48 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
49 # --no-check-attr-isset
50 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
51 # --no-check-assert
52 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
53 # --no-check-autocast
54 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
55 # --no-check-null
56 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
57 # --no-check-all
58 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
59 # --typing-test-metrics
60 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
61 # --invocation-metrics
62 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
63 # --isset-checks-metrics
64 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
65 # --stacktrace
66 var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
67 # --no-gcc-directives
68 var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
69 # --release
70 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
71
72 redef init
73 do
74 super
75 self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
76 self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
77 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
78 self.option_context.add_option(self.opt_stacktrace)
79 self.option_context.add_option(self.opt_no_gcc_directive)
80 self.option_context.add_option(self.opt_release)
81 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
82 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
153 redef fun write_and_make(compiler)
154 do
155 var compile_dir = compile_dir
156
157 # Generate the .h and .c files
158 # A single C file regroups many compiled rumtime functions
159 # 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
160 var time0 = get_time
161 self.toolcontext.info("*** WRITING C ***", 1)
162
163 compile_dir.mkdir
164
165 var cfiles = new Array[String]
166 write_files(compiler, compile_dir, cfiles)
167
168 # Generate the Makefile
169
170 write_makefile(compiler, compile_dir, cfiles)
171
172 var time1 = get_time
173 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
174
175 # Execute the Makefile
176
177 if self.toolcontext.opt_no_cc.value then return
178
179 time0 = time1
180 self.toolcontext.info("*** COMPILING C ***", 1)
181
182 compile_c_code(compiler, compile_dir)
183
184 time1 = get_time
185 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
186 end
187
188 fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
189 do
190 var platform = compiler.mainmodule.target_platform
191 if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
192 var cc_opt_with_libgc = "-DWITH_LIBGC `pkg-config --cflags bdw-gc`"
193 if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
194
195 # Add gc_choser.h to aditionnal bodies
196 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
197 compiler.extern_bodies.add(gc_chooser)
198 var clib = toolcontext.nit_dir / "clib"
199 compiler.files_to_copy.add "{clib}/gc_chooser.c"
200 compiler.files_to_copy.add "{clib}/gc_chooser.h"
201
202 # FFI
203 for m in compiler.mainmodule.in_importation.greaters do
204 compiler.finalize_ffi_for_module(m)
205 end
206
207 # Copy original .[ch] files to compile_dir
208 for src in compiler.files_to_copy do
209 var basename = src.basename("")
210 var dst = "{compile_dir}/{basename}"
211 src.file_copy_to dst
212 end
213
214 var hfilename = compiler.header.file.name + ".h"
215 var hfilepath = "{compile_dir}/{hfilename}"
216 var h = new OFStream.open(hfilepath)
217 for l in compiler.header.decl_lines do
218 h.write l
219 h.write "\n"
220 end
221 for l in compiler.header.lines do
222 h.write l
223 h.write "\n"
224 end
225 h.close
226
227 var max_c_lines = toolcontext.opt_max_c_lines.value
228 for f in compiler.files do
229 var i = 0
230 var count = 0
231 var file: nullable OFStream = null
232 for vis in f.writers do
233 if vis == compiler.header then continue
234 var total_lines = vis.lines.length + vis.decl_lines.length
235 if total_lines == 0 then continue
236 count += total_lines
237 if file == null or (count > max_c_lines and max_c_lines > 0) then
238 i += 1
239 if file != null then file.close
240 var cfilename = "{f.name}.{i}.c"
241 var cfilepath = "{compile_dir}/{cfilename}"
242 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
243 cfiles.add(cfilename)
244 file = new OFStream.open(cfilepath)
245 file.write "#include \"{f.name}.0.h\"\n"
246 count = total_lines
247 end
248 for l in vis.decl_lines do
249 file.write l
250 file.write "\n"
251 end
252 for l in vis.lines do
253 file.write l
254 file.write "\n"
255 end
256 end
257 if file == null then continue
258 file.close
259
260 var cfilename = "{f.name}.0.h"
261 var cfilepath = "{compile_dir}/{cfilename}"
262 var hfile: nullable OFStream = null
263 hfile = new OFStream.open(cfilepath)
264 hfile.write "#include \"{hfilename}\"\n"
265 for key in f.required_declarations do
266 if not compiler.provided_declarations.has_key(key) then
267 var node = compiler.requirers_of_declarations.get_or_null(key)
268 if node != null then
269 node.debug "No provided declaration for {key}"
270 else
271 print "No provided declaration for {key}"
272 end
273 abort
274 end
275 hfile.write compiler.provided_declarations[key]
276 hfile.write "\n"
277 end
278 hfile.close
279 end
280
281 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
282 end
283
284 fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
285
286 fun default_outname(mainmodule: MModule): String
287 do
288 # Search a non fictive module
289 var res = mainmodule.name
290 while mainmodule.is_fictive do
291 mainmodule = mainmodule.in_importation.direct_greaters.first
292 res = mainmodule.name
293 end
294 return res
295 end
296
297 # Combine options and platform informations to get the final path of the outfile
298 fun outfile(mainmodule: MModule): String
299 do
300 var res = self.toolcontext.opt_output.value
301 if res != null then return res
302 res = default_outname(mainmodule)
303 var dir = self.toolcontext.opt_dir.value
304 if dir != null then return dir.join_path(res)
305 return res
306 end
307
308 fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
309 do
310 var mainmodule = compiler.mainmodule
311 var platform = compiler.mainmodule.target_platform
312
313 var outname = outfile(mainmodule)
314
315 var outpath = compile_dir.relpath(outname)
316 var makename = makefile_name(mainmodule)
317 var makepath = "{compile_dir}/{makename}"
318 var makefile = new OFStream.open(makepath)
319
320 var linker_options = new HashSet[String]
321 for m in mainmodule.in_importation.greaters do
322 var libs = m.collect_linker_libs
323 if libs != null then linker_options.add_all(libs)
324 end
325
326 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm `pkg-config --libs bdw-gc` {linker_options.join(" ")}\n\n")
327
328 var ost = toolcontext.opt_stacktrace.value
329 if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
330
331 # Dynamic adaptations
332 # While `platform` enable complex toolchains, they are statically applied
333 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
334
335 # Check and adapt the targeted system
336 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
337 makefile.write("ifeq ($(uname_S),Darwin)\n")
338 # remove -lunwind since it is already included on macosx
339 makefile.write("\tNEED_LIBUNWIND :=\n")
340 makefile.write("endif\n\n")
341
342 # Check and adapt for the compiler used
343 # clang need an additionnal `-Qunused-arguments`
344 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")
345
346 makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
347
348 makefile.write("all: {outpath}\n\n")
349
350 var ofiles = new Array[String]
351 var dep_rules = new Array[String]
352 # Compile each generated file
353 for f in cfiles do
354 var o = f.strip_extension(".c") + ".o"
355 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
356 ofiles.add(o)
357 dep_rules.add(o)
358 end
359
360 var java_files = new Array[ExternFile]
361
362 # Compile each required extern body into a specific .o
363 for f in compiler.extern_bodies do
364 var o = f.makefile_rule_name
365 var ff = f.filename.basename("")
366 makefile.write("{o}: {ff}\n")
367 makefile.write("\t{f.makefile_rule_content}\n\n")
368 dep_rules.add(f.makefile_rule_name)
369
370 if f.compiles_to_o_file then ofiles.add(o)
371 if f.add_to_jar then java_files.add(f)
372 end
373
374 if not java_files.is_empty then
375 var jar_file = "{outpath}.jar"
376
377 var class_files_array = new Array[String]
378 for f in java_files do class_files_array.add(f.makefile_rule_name)
379 var class_files = class_files_array.join(" ")
380
381 makefile.write("{jar_file}: {class_files}\n")
382 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
383 dep_rules.add jar_file
384 end
385
386 # Link edition
387 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n")
388 # Clean
389 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
390 makefile.close
391 self.toolcontext.info("Generated makefile: {makepath}", 2)
392
393 makepath.file_copy_to "{compile_dir}/Makefile"
394 end
395
396 fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
397 do
398 var makename = makefile_name(compiler.mainmodule)
399
400 var makeflags = self.toolcontext.opt_make_flags.value
401 if makeflags == null then makeflags = ""
402 self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
403
404 var res
405 if self.toolcontext.verbose_level >= 3 then
406 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
407 else
408 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
409 end
410 if res != 0 then
411 toolcontext.error(null, "make failed! Error code: {res}.")
412 end
413 end
414 end
415
416 # Singleton that store the knowledge about the compilation process
417 abstract class AbstractCompiler
418 type VISITOR: AbstractCompilerVisitor
419
420 # Table corresponding c_names to nit names (methods)
421 var names = new HashMap[String, String]
422
423 # The main module of the program currently compiled
424 # Is assigned during the separate compilation
425 var mainmodule: MModule is writable
426
427 # The real main module of the program
428 var realmainmodule: MModule
429
430 # The modelbuilder used to know the model and the AST
431 var modelbuilder: ModelBuilder is protected writable
432
433 # Is hardening asked? (see --hardening)
434 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
435
436 init(mainmodule: MModule, modelbuilder: ModelBuilder)
437 do
438 self.mainmodule = mainmodule
439 self.realmainmodule = mainmodule
440 self.modelbuilder = modelbuilder
441 end
442
443 # Force the creation of a new file
444 # The point is to avoid contamination between must-be-compiled-separately files
445 fun new_file(name: String): CodeFile
446 do
447 if modelbuilder.toolcontext.opt_group_c_files.value then
448 if self.files.is_empty then
449 var f = new CodeFile(mainmodule.name)
450 self.files.add(f)
451 end
452 return self.files.first
453 end
454 var f = new CodeFile(name)
455 self.files.add(f)
456 return f
457 end
458
459 # The list of all associated files
460 # Used to generate .c files
461 var files = new List[CodeFile]
462
463 # Initialize a visitor specific for a compiler engine
464 fun new_visitor: VISITOR is abstract
465
466 # Where global declaration are stored (the main .h)
467 var header: CodeWriter is writable
468
469 # Provide a declaration that can be requested (before or latter) by a visitor
470 fun provide_declaration(key: String, s: String)
471 do
472 if self.provided_declarations.has_key(key) then
473 assert self.provided_declarations[key] == s
474 end
475 self.provided_declarations[key] = s
476 end
477
478 private var provided_declarations = new HashMap[String, String]
479
480 private var requirers_of_declarations = new HashMap[String, ANode]
481
482 # Builds the .c and .h files to be used when generating a Stack Trace
483 # Binds the generated C function names to Nit function names
484 fun build_c_to_nit_bindings
485 do
486 var compile_dir = modelbuilder.compile_dir
487
488 var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
489 stream.write("#include <string.h>\n")
490 stream.write("#include <stdlib.h>\n")
491 stream.write("#include \"c_functions_hash.h\"\n")
492 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
493 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
494 stream.write("char* procname = malloc(len+1);")
495 stream.write("memcpy(procname, procproc, len);")
496 stream.write("procname[len] = '\\0';")
497 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
498 for i in names.keys do
499 stream.write("\{\"")
500 stream.write(i)
501 stream.write("\",\"")
502 stream.write(names[i])
503 stream.write("\"\},\n")
504 end
505 stream.write("\};\n")
506 stream.write("int i;")
507 stream.write("for(i = 0; i < {names.length}; i++)\{")
508 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
509 stream.write("free(procname);")
510 stream.write("return map[i].nit_name;")
511 stream.write("\}")
512 stream.write("\}")
513 stream.write("free(procname);")
514 stream.write("return NULL;")
515 stream.write("\}\n")
516 stream.close
517
518 stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
519 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
520 stream.close
521
522 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
523 end
524
525 # Compile C headers
526 # This method call compile_header_strucs method that has to be refined
527 fun compile_header do
528 self.header.add_decl("#include <stdlib.h>")
529 self.header.add_decl("#include <stdio.h>")
530 self.header.add_decl("#include <string.h>")
531 self.header.add_decl("#include \"gc_chooser.h\"")
532 self.header.add_decl("#ifdef ANDROID")
533 self.header.add_decl(" #include <android/log.h>")
534 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
535 self.header.add_decl("#else")
536 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
537 self.header.add_decl("#endif")
538
539 compile_header_structs
540 compile_nitni_structs
541
542 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
543 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
544 # Signal handler function prototype
545 self.header.add_decl("void show_backtrace(int);")
546 else
547 self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
548 end
549
550 if gccd_disable.has("likely") or gccd_disable.has("all") then
551 self.header.add_decl("#define likely(x) (x)")
552 self.header.add_decl("#define unlikely(x) (x)")
553 else if gccd_disable.has("correct-likely") then
554 # invert the `likely` definition
555 # Used by masochists to bench the worst case
556 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
557 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
558 else
559 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
560 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
561 end
562
563 # Global variable used by intern methods
564 self.header.add_decl("extern int glob_argc;")
565 self.header.add_decl("extern char **glob_argv;")
566 self.header.add_decl("extern val *glob_sys;")
567 end
568
569 # Declaration of structures for live Nit types
570 protected fun compile_header_structs is abstract
571
572 # Declaration of structures for nitni undelying the FFI
573 protected fun compile_nitni_structs
574 do
575 self.header.add_decl """
576 /* Native reference to Nit objects */
577 /* This structure is used to represent every Nit type in extern methods and custom C code. */
578 struct nitni_ref {
579 struct nitni_ref *next,
580 *prev; /* adjacent global references in global list */
581 int count; /* number of time this global reference has been marked */
582 };
583
584 /* List of global references from C code to Nit objects */
585 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
586 struct nitni_global_ref_list_t {
587 struct nitni_ref *head, *tail;
588 };
589 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
590
591 /* Initializer of global reference list */
592 extern void nitni_global_ref_list_init();
593
594 /* Intern function to add a global reference to the list */
595 extern void nitni_global_ref_add( struct nitni_ref *ref );
596
597 /* Intern function to remove a global reference from the list */
598 extern void nitni_global_ref_remove( struct nitni_ref *ref );
599
600 /* Increase count on an existing global reference */
601 extern void nitni_global_ref_incr( struct nitni_ref *ref );
602
603 /* Decrease count on an existing global reference */
604 extern void nitni_global_ref_decr( struct nitni_ref *ref );
605 """
606 end
607
608 fun compile_finalizer_function
609 do
610 var finalizable_type = mainmodule.finalizable_type
611 if finalizable_type == null then return
612
613 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
614
615 if finalize_meth == null then
616 modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
617 return
618 end
619
620 var v = self.new_visitor
621 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
622 var recv = v.new_expr("obj", finalizable_type)
623 v.send(finalize_meth, [recv])
624 v.add "\}"
625 end
626
627 # Generate the main C function.
628 #
629 # This function:
630 #
631 # * allocate the Sys object if it exists
632 # * call init if is exists
633 # * call main if it exists
634 fun compile_main_function
635 do
636 var v = self.new_visitor
637 v.add_decl("#include <signal.h>")
638 var ost = modelbuilder.toolcontext.opt_stacktrace.value
639 var platform = mainmodule.target_platform
640
641 if platform != null and not platform.supports_libunwind then ost = "none"
642
643 var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
644
645 if ost == "nitstack" or ost == "libunwind" then
646 v.add_decl("#define UNW_LOCAL_ONLY")
647 v.add_decl("#include <libunwind.h>")
648 if ost == "nitstack" then
649 v.add_decl("#include \"c_functions_hash.h\"")
650 end
651 end
652 v.add_decl("int glob_argc;")
653 v.add_decl("char **glob_argv;")
654 v.add_decl("val *glob_sys;")
655
656 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
657 for tag in count_type_test_tags do
658 v.add_decl("long count_type_test_resolved_{tag};")
659 v.add_decl("long count_type_test_unresolved_{tag};")
660 v.add_decl("long count_type_test_skipped_{tag};")
661 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
662 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
663 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
664 end
665 end
666
667 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
668 v.add_decl("long count_invoke_by_tables;")
669 v.add_decl("long count_invoke_by_direct;")
670 v.add_decl("long count_invoke_by_inline;")
671 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
672 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
673 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
674 end
675
676 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
677 v.add_decl("long count_attr_reads = 0;")
678 v.add_decl("long count_isset_checks = 0;")
679 v.compiler.header.add_decl("extern long count_attr_reads;")
680 v.compiler.header.add_decl("extern long count_isset_checks;")
681 end
682
683 v.add_decl("void sig_handler(int signo)\{")
684 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
685 v.add_decl("show_backtrace(signo);")
686 v.add_decl("\}")
687
688 v.add_decl("void show_backtrace (int signo) \{")
689 if ost == "nitstack" or ost == "libunwind" then
690 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
691 v.add_decl("unw_cursor_t cursor;")
692 v.add_decl("if(opt==NULL)\{")
693 v.add_decl("unw_context_t uc;")
694 v.add_decl("unw_word_t ip;")
695 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
696 v.add_decl("unw_getcontext(&uc);")
697 v.add_decl("unw_init_local(&cursor, &uc);")
698 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
699 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
700 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
701 v.add_decl("while (unw_step(&cursor) > 0) \{")
702 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
703 if ost == "nitstack" then
704 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
705 v.add_decl(" if (recv != NULL)\{")
706 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
707 v.add_decl(" \}else\{")
708 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
709 v.add_decl(" \}")
710 else
711 v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
712 end
713 v.add_decl("\}")
714 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
715 v.add_decl("free(procname);")
716 v.add_decl("\}")
717 end
718 v.add_decl("exit(signo);")
719 v.add_decl("\}")
720
721 if no_main then
722 v.add_decl("int nit_main(int argc, char** argv) \{")
723 else
724 v.add_decl("int main(int argc, char** argv) \{")
725 end
726
727 v.add("signal(SIGABRT, sig_handler);")
728 v.add("signal(SIGFPE, sig_handler);")
729 v.add("signal(SIGILL, sig_handler);")
730 v.add("signal(SIGINT, sig_handler);")
731 v.add("signal(SIGTERM, sig_handler);")
732 v.add("signal(SIGSEGV, sig_handler);")
733 v.add("signal(SIGPIPE, sig_handler);")
734
735 v.add("glob_argc = argc; glob_argv = argv;")
736 v.add("initialize_gc_option();")
737
738 v.add "initialize_nitni_global_refs();"
739
740 var main_type = mainmodule.sys_type
741 if main_type != null then
742 var mainmodule = v.compiler.mainmodule
743 var glob_sys = v.init_instance(main_type)
744 v.add("glob_sys = {glob_sys};")
745 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
746 if main_init != null then
747 v.send(main_init, [glob_sys])
748 end
749 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
750 mainmodule.try_get_primitive_method("main", main_type.mclass)
751 if main_method != null then
752 v.send(main_method, [glob_sys])
753 end
754 end
755
756 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
757 v.add_decl("long count_type_test_resolved_total = 0;")
758 v.add_decl("long count_type_test_unresolved_total = 0;")
759 v.add_decl("long count_type_test_skipped_total = 0;")
760 v.add_decl("long count_type_test_total_total = 0;")
761 for tag in count_type_test_tags do
762 v.add_decl("long count_type_test_total_{tag};")
763 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
764 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
765 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
766 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
767 v.add("count_type_test_total_total += count_type_test_total_{tag};")
768 end
769 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
770 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
771 var tags = count_type_test_tags.to_a
772 tags.add("total")
773 for tag in tags do
774 v.add("printf(\"{tag}\");")
775 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
776 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
777 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
778 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
779 end
780 end
781
782 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
783 v.add_decl("long count_invoke_total;")
784 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
785 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
786 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
787 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
788 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
789 end
790
791 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
792 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
793 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
794 end
795
796 v.add("return 0;")
797 v.add("\}")
798 end
799
800 # Copile all C functions related to the [incr|decr]_ref features of the FFI
801 fun compile_nitni_global_ref_functions
802 do
803 var v = self.new_visitor
804 v.add """
805 struct nitni_global_ref_list_t *nitni_global_ref_list;
806 void initialize_nitni_global_refs() {
807 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
808 nitni_global_ref_list->head = NULL;
809 nitni_global_ref_list->tail = NULL;
810 }
811
812 void nitni_global_ref_add( struct nitni_ref *ref ) {
813 if ( nitni_global_ref_list->head == NULL ) {
814 nitni_global_ref_list->head = ref;
815 ref->prev = NULL;
816 } else {
817 nitni_global_ref_list->tail->next = ref;
818 ref->prev = nitni_global_ref_list->tail;
819 }
820 nitni_global_ref_list->tail = ref;
821
822 ref->next = NULL;
823 }
824
825 void nitni_global_ref_remove( struct nitni_ref *ref ) {
826 if ( ref->prev == NULL ) {
827 nitni_global_ref_list->head = ref->next;
828 } else {
829 ref->prev->next = ref->next;
830 }
831
832 if ( ref->next == NULL ) {
833 nitni_global_ref_list->tail = ref->prev;
834 } else {
835 ref->next->prev = ref->prev;
836 }
837 }
838
839 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
840 if ( ref->count == 0 ) /* not registered */
841 {
842 /* add to list */
843 nitni_global_ref_add( ref );
844 }
845
846 ref->count ++;
847 }
848
849 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
850 if ( ref->count == 1 ) /* was last reference */
851 {
852 /* remove from list */
853 nitni_global_ref_remove( ref );
854 }
855
856 ref->count --;
857 }
858 """
859 end
860
861 # List of additional files required to compile (FFI)
862 var extern_bodies = new Array[ExternFile]
863
864 # List of source files to copy over to the compile dir
865 var files_to_copy = new Array[String]
866
867 # This is used to avoid adding an extern file more than once
868 private var seen_extern = new ArraySet[String]
869
870 # Generate code that initialize the attributes on a new instance
871 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
872 do
873 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
874 self.mainmodule.linearize_mclassdefs(cds)
875 for cd in cds do
876 if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
877 var n = self.modelbuilder.mclassdef2nclassdef[cd]
878 for npropdef in n.n_propdefs do
879 if npropdef isa AAttrPropdef then
880 npropdef.init_expr(v, recv)
881 end
882 end
883 end
884 end
885
886 # Generate code that check if an attribute is correctly initialized
887 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
888 do
889 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
890 self.mainmodule.linearize_mclassdefs(cds)
891 for cd in cds do
892 if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
893 var n = self.modelbuilder.mclassdef2nclassdef[cd]
894 for npropdef in n.n_propdefs do
895 if npropdef isa AAttrPropdef then
896 npropdef.check_expr(v, recv)
897 end
898 end
899 end
900 end
901
902 # stats
903
904 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
905 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
906 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
907 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
908
909 protected fun init_count_type_test_tags: HashMap[String, Int]
910 do
911 var res = new HashMap[String, Int]
912 for tag in count_type_test_tags do
913 res[tag] = 0
914 end
915 return res
916 end
917
918 # Display stats about compilation process
919 #
920 # Metrics used:
921 #
922 # * type tests against resolved types (`x isa Collection[Animal]`)
923 # * type tests against unresolved types (`x isa Collection[E]`)
924 # * type tests skipped
925 # * type tests total
926 fun display_stats
927 do
928 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
929 print "# static count_type_test"
930 print "\tresolved:\tunresolved\tskipped\ttotal"
931 var count_type_test_total = init_count_type_test_tags
932 count_type_test_resolved["total"] = 0
933 count_type_test_unresolved["total"] = 0
934 count_type_test_skipped["total"] = 0
935 count_type_test_total["total"] = 0
936 for tag in count_type_test_tags do
937 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
938 count_type_test_resolved["total"] += count_type_test_resolved[tag]
939 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
940 count_type_test_skipped["total"] += count_type_test_skipped[tag]
941 count_type_test_total["total"] += count_type_test_total[tag]
942 end
943 var count_type_test = count_type_test_total["total"]
944 var tags = count_type_test_tags.to_a
945 tags.add("total")
946 for tag in tags do
947 printn tag
948 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
949 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
950 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
951 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
952 print ""
953 end
954 end
955 end
956
957 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
958
959 # Division facility
960 # Avoid division by zero by returning the string "n/a"
961 fun div(a,b:Int):String
962 do
963 if b == 0 then return "n/a"
964 return ((a*10000/b).to_f / 100.0).to_precision(2)
965 end
966 end
967
968 # A file unit (may be more than one file if
969 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
970 class CodeFile
971 var name: String
972 var writers = new Array[CodeWriter]
973 var required_declarations = new HashSet[String]
974 end
975
976 # Where to store generated lines
977 class CodeWriter
978 var file: CodeFile
979 var lines: List[String] = new List[String]
980 var decl_lines: List[String] = new List[String]
981
982 # Add a line in the main part of the generated C
983 fun add(s: String) do self.lines.add(s)
984
985 # Add a line in the
986 # (used for local or global declaration)
987 fun add_decl(s: String) do self.decl_lines.add(s)
988
989 init(file: CodeFile)
990 do
991 self.file = file
992 file.writers.add(self)
993 end
994 end
995
996 # A visitor on the AST of property definition that generate the C code.
997 abstract class AbstractCompilerVisitor
998
999 type COMPILER: AbstractCompiler
1000
1001 # The associated compiler
1002 var compiler: COMPILER
1003
1004 # The current visited AST node
1005 var current_node: nullable ANode = null is writable
1006
1007 # The current `Frame`
1008 var frame: nullable Frame is writable
1009
1010 # Alias for self.compiler.mainmodule.object_type
1011 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1012
1013 # Alias for self.compiler.mainmodule.bool_type
1014 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1015
1016 var writer: CodeWriter
1017
1018 init(compiler: COMPILER)
1019 do
1020 self.compiler = compiler
1021 self.writer = new CodeWriter(compiler.files.last)
1022 end
1023
1024 # Force to get the primitive class named `name` or abort
1025 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
1026
1027 # Force to get the primitive property named `name` in the instance `recv` or abort
1028 fun get_property(name: String, recv: MType): MMethod
1029 do
1030 assert recv isa MClassType
1031 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1032 end
1033
1034 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1035 do
1036 var initializers = callsite.mpropdef.initializers
1037 if not initializers.is_empty then
1038 var recv = arguments.first
1039
1040 var i = 1
1041 for p in initializers do
1042 if p isa MMethod then
1043 var args = [recv]
1044 for x in p.intro.msignature.mparameters do
1045 args.add arguments[i]
1046 i += 1
1047 end
1048 self.send(p, args)
1049 else if p isa MAttribute then
1050 self.write_attribute(p, recv, arguments[i])
1051 i += 1
1052 else abort
1053 end
1054 assert i == arguments.length
1055
1056 return self.send(callsite.mproperty, [recv])
1057 end
1058
1059 return self.send(callsite.mproperty, arguments)
1060 end
1061
1062 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1063
1064 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1065
1066 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1067
1068 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1069 # This method is used to manage varargs in signatures and returns the real array
1070 # of runtime variables to use in the call.
1071 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1072 do
1073 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1074 var res = new Array[RuntimeVariable]
1075 res.add(recv)
1076
1077 if args.is_empty then return res
1078
1079 var vararg_rank = msignature.vararg_rank
1080 var vararg_len = args.length - msignature.arity
1081 if vararg_len < 0 then vararg_len = 0
1082
1083 for i in [0..msignature.arity[ do
1084 if i == vararg_rank then
1085 var ne = args[i]
1086 if ne isa AVarargExpr then
1087 var e = self.expr(ne.n_expr, null)
1088 res.add(e)
1089 continue
1090 end
1091 var vararg = new Array[RuntimeVariable]
1092 for j in [vararg_rank..vararg_rank+vararg_len] do
1093 var e = self.expr(args[j], null)
1094 vararg.add(e)
1095 end
1096 var elttype = msignature.mparameters[vararg_rank].mtype
1097 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1098 res.add(arg)
1099 else
1100 var j = i
1101 if i > vararg_rank then j += vararg_len
1102 var e = self.expr(args[j], null)
1103 res.add(e)
1104 end
1105 end
1106 return res
1107 end
1108
1109 # Type handling
1110
1111 # Anchor a type to the main module and the current receiver
1112 fun anchor(mtype: MType): MType
1113 do
1114 if not mtype.need_anchor then return mtype
1115 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1116 end
1117
1118 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1119 do
1120 if not mtype.need_anchor then return mtype
1121 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1122 end
1123
1124 # Unsafely cast a value to a new type
1125 # ie the result share the same C variable but my have a different mcasttype
1126 # NOTE: if the adaptation is useless then `value` is returned as it.
1127 # ENSURE: `result.name == value.name`
1128 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1129 do
1130 mtype = self.anchor(mtype)
1131 var valmtype = value.mcasttype
1132 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1133 return value
1134 end
1135
1136 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1137 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1138 return res
1139 else
1140 var res = new RuntimeVariable(value.name, valmtype, mtype)
1141 return res
1142 end
1143 end
1144
1145 # Generate a super call from a method definition
1146 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1147
1148 # Adapt the arguments of a method according to targetted `MMethodDef`
1149 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1150
1151 # Unbox all the arguments of a method when implemented `extern` or `intern`
1152 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1153
1154 # Box or unbox a value to another type iff a C type conversion is needed
1155 # ENSURE: `result.mtype.ctype == mtype.ctype`
1156 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1157
1158 # Box extern classes to be used in the generated code
1159 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1160
1161 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1162 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1163
1164 # Generate a polymorphic subtype test
1165 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1166
1167 # Generate the code required to dynamically check if 2 objects share the same runtime type
1168 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1169
1170 # Generate a Nit "is" for two runtime_variables
1171 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1172
1173 # Sends
1174
1175 # Generate a static call on a method definition
1176 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1177
1178 # Generate a polymorphic send for the method `m` and the arguments `args`
1179 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1180
1181 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1182 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1183 do
1184 assert t isa MClassType
1185 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1186 return self.call(propdef, t, args)
1187 end
1188
1189 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1190 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1191 do
1192 assert t isa MClassType
1193 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1194 return self.call(m, t, args)
1195 end
1196
1197 # Attributes handling
1198
1199 # Generate a polymorphic attribute is_set test
1200 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1201
1202 # Generate a polymorphic attribute read
1203 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1204
1205 # Generate a polymorphic attribute write
1206 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1207
1208 # Checks
1209
1210 # Add a check and an abort for a null receiver if needed
1211 fun check_recv_notnull(recv: RuntimeVariable)
1212 do
1213 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1214
1215 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1216 if maybenull then
1217 self.add("if (unlikely({recv} == NULL)) \{")
1218 self.add_abort("Receiver is null")
1219 self.add("\}")
1220 end
1221 end
1222
1223 # Names handling
1224
1225 private var names = new HashSet[String]
1226 private var last: Int = 0
1227
1228 # Return a new name based on `s` and unique in the visitor
1229 fun get_name(s: String): String
1230 do
1231 if not self.names.has(s) then
1232 self.names.add(s)
1233 return s
1234 end
1235 var i = self.last + 1
1236 loop
1237 var s2 = s + i.to_s
1238 if not self.names.has(s2) then
1239 self.last = i
1240 self.names.add(s2)
1241 return s2
1242 end
1243 i = i + 1
1244 end
1245 end
1246
1247 # Return an unique and stable identifier associated with an escapemark
1248 fun escapemark_name(e: nullable EscapeMark): String
1249 do
1250 assert e != null
1251 if escapemark_names.has_key(e) then return escapemark_names[e]
1252 var name = e.name
1253 if name == null then name = "label"
1254 name = get_name(name)
1255 escapemark_names[e] = name
1256 return name
1257 end
1258
1259 # Insert a C label for associated with an escapemark
1260 fun add_escape_label(e: nullable EscapeMark)
1261 do
1262 if e == null then return
1263 if e.escapes.is_empty then return
1264 add("BREAK_{escapemark_name(e)}: (void)0;")
1265 end
1266
1267 private var escapemark_names = new HashMap[EscapeMark, String]
1268
1269 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1270 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1271 fun class_name_string(value: RuntimeVariable): String is abstract
1272
1273 # Variables handling
1274
1275 protected var variables = new HashMap[Variable, RuntimeVariable]
1276
1277 # Return the local runtime_variable associated to a Nit local variable
1278 fun variable(variable: Variable): RuntimeVariable
1279 do
1280 if self.variables.has_key(variable) then
1281 return self.variables[variable]
1282 else
1283 var name = self.get_name("var_{variable.name}")
1284 var mtype = variable.declared_type.as(not null)
1285 mtype = self.anchor(mtype)
1286 var res = new RuntimeVariable(name, mtype, mtype)
1287 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1288 self.variables[variable] = res
1289 return res
1290 end
1291 end
1292
1293 # Return a new uninitialized local runtime_variable
1294 fun new_var(mtype: MType): RuntimeVariable
1295 do
1296 mtype = self.anchor(mtype)
1297 var name = self.get_name("var")
1298 var res = new RuntimeVariable(name, mtype, mtype)
1299 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1300 return res
1301 end
1302
1303 # The difference with `new_var` is the C static type of the local variable
1304 fun new_var_extern(mtype: MType): RuntimeVariable
1305 do
1306 mtype = self.anchor(mtype)
1307 var name = self.get_name("var")
1308 var res = new RuntimeVariable(name, mtype, mtype)
1309 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1310 return res
1311 end
1312
1313 # Return a new uninitialized named runtime_variable
1314 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1315 do
1316 mtype = self.anchor(mtype)
1317 var res = new RuntimeVariable(name, mtype, mtype)
1318 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1319 return res
1320 end
1321
1322 # Correctly assign a left and a right value
1323 # Boxing and unboxing is performed if required
1324 fun assign(left, right: RuntimeVariable)
1325 do
1326 right = self.autobox(right, left.mtype)
1327 self.add("{left} = {right};")
1328 end
1329
1330 # Generate instances
1331
1332 # Generate a alloc-instance + init-attributes
1333 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1334
1335 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1336 fun set_finalizer(recv: RuntimeVariable)
1337 do
1338 var mtype = recv.mtype
1339 var finalizable_type = compiler.mainmodule.finalizable_type
1340 if finalizable_type != null and not mtype.need_anchor and
1341 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1342 add "gc_register_finalizer({recv});"
1343 end
1344 end
1345
1346 # Generate an integer value
1347 fun int_instance(value: Int): RuntimeVariable
1348 do
1349 var res = self.new_var(self.get_class("Int").mclass_type)
1350 self.add("{res} = {value};")
1351 return res
1352 end
1353
1354 # Generate an integer value
1355 fun bool_instance(value: Bool): RuntimeVariable
1356 do
1357 var res = self.new_var(self.get_class("Bool").mclass_type)
1358 if value then
1359 self.add("{res} = 1;")
1360 else
1361 self.add("{res} = 0;")
1362 end
1363 return res
1364 end
1365
1366 # Generate a string value
1367 fun string_instance(string: String): RuntimeVariable
1368 do
1369 var mtype = self.get_class("String").mclass_type
1370 var name = self.get_name("varonce")
1371 self.add_decl("static {mtype.ctype} {name};")
1372 var res = self.new_var(mtype)
1373 self.add("if ({name}) \{")
1374 self.add("{res} = {name};")
1375 self.add("\} else \{")
1376 var native_mtype = self.get_class("NativeString").mclass_type
1377 var nat = self.new_var(native_mtype)
1378 self.add("{nat} = \"{string.escape_to_c}\";")
1379 var length = self.int_instance(string.length)
1380 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1381 self.add("{name} = {res};")
1382 self.add("\}")
1383 return res
1384 end
1385
1386 fun value_instance(object: Object): RuntimeVariable
1387 do
1388 if object isa Int then
1389 return int_instance(object)
1390 else if object isa Bool then
1391 return bool_instance(object)
1392 else if object isa String then
1393 return string_instance(object)
1394 else
1395 abort
1396 end
1397 end
1398
1399 # Generate an array value
1400 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1401
1402 # Get an instance of a array for a vararg
1403 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1404
1405 # Code generation
1406
1407 # Add a line in the main part of the generated C
1408 fun add(s: String) do self.writer.lines.add(s)
1409
1410 # Add a line in the
1411 # (used for local or global declaration)
1412 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1413
1414 # Request the presence of a global declaration
1415 fun require_declaration(key: String)
1416 do
1417 var reqs = self.writer.file.required_declarations
1418 if reqs.has(key) then return
1419 reqs.add(key)
1420 var node = current_node
1421 if node != null then compiler.requirers_of_declarations[key] = node
1422 end
1423
1424 # Add a declaration in the local-header
1425 # The declaration is ensured to be present once
1426 fun declare_once(s: String)
1427 do
1428 self.compiler.provide_declaration(s, s)
1429 self.require_declaration(s)
1430 end
1431
1432 # look for a needed .h and .c file for a given .nit source-file
1433 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1434 fun add_extern(file: String)
1435 do
1436 file = file.strip_extension(".nit")
1437 var tryfile = file + ".nit.h"
1438 if tryfile.file_exists then
1439 self.declare_once("#include \"{tryfile.basename("")}\"")
1440 self.compiler.files_to_copy.add(tryfile)
1441 end
1442 tryfile = file + "_nit.h"
1443 if tryfile.file_exists then
1444 self.declare_once("#include \"{tryfile.basename("")}\"")
1445 self.compiler.files_to_copy.add(tryfile)
1446 end
1447
1448 if self.compiler.seen_extern.has(file) then return
1449 self.compiler.seen_extern.add(file)
1450 tryfile = file + ".nit.c"
1451 if not tryfile.file_exists then
1452 tryfile = file + "_nit.c"
1453 if not tryfile.file_exists then return
1454 end
1455 var f = new ExternCFile(tryfile.basename(""), "")
1456 self.compiler.extern_bodies.add(f)
1457 self.compiler.files_to_copy.add(tryfile)
1458 end
1459
1460 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1461 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1462 do
1463 var res = new_var(mtype)
1464 self.add("{res} = {cexpr};")
1465 return res
1466 end
1467
1468 # Generate generic abort
1469 # used by aborts, asserts, casts, etc.
1470 fun add_abort(message: String)
1471 do
1472 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1473 add_raw_abort
1474 end
1475
1476 fun add_raw_abort
1477 do
1478 if self.current_node != null and self.current_node.location.file != null then
1479 self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1480 else
1481 self.add("PRINT_ERROR(\"\\n\");")
1482 end
1483 self.add("show_backtrace(1);")
1484 end
1485
1486 # Add a dynamic cast
1487 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1488 do
1489 var res = self.type_test(value, mtype, tag)
1490 self.add("if (unlikely(!{res})) \{")
1491 var cn = self.class_name_string(value)
1492 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1493 self.add_raw_abort
1494 self.add("\}")
1495 end
1496
1497 # Generate a return with the value `s`
1498 fun ret(s: RuntimeVariable)
1499 do
1500 self.assign(self.frame.returnvar.as(not null), s)
1501 self.add("goto {self.frame.returnlabel.as(not null)};")
1502 end
1503
1504 # Compile a statement (if any)
1505 fun stmt(nexpr: nullable AExpr)
1506 do
1507 if nexpr == null then return
1508 var old = self.current_node
1509 self.current_node = nexpr
1510 nexpr.stmt(self)
1511 self.current_node = old
1512 end
1513
1514 # Compile an expression an return its result
1515 # `mtype` is the expected return type, pass null if no specific type is expected.
1516 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1517 do
1518 var old = self.current_node
1519 self.current_node = nexpr
1520 var res = nexpr.expr(self).as(not null)
1521 if mtype != null then
1522 mtype = self.anchor(mtype)
1523 res = self.autobox(res, mtype)
1524 end
1525 res = autoadapt(res, nexpr.mtype.as(not null))
1526 var implicit_cast_to = nexpr.implicit_cast_to
1527 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1528 add_cast(res, implicit_cast_to, "auto")
1529 res = autoadapt(res, implicit_cast_to)
1530 end
1531 self.current_node = old
1532 return res
1533 end
1534
1535 # Alias for `self.expr(nexpr, self.bool_type)`
1536 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1537
1538 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1539 fun debug(message: String)
1540 do
1541 var node = self.current_node
1542 if node == null then
1543 print "?: {message}"
1544 else
1545 node.debug(message)
1546 end
1547 self.add("/* DEBUG: {message} */")
1548 end
1549 end
1550
1551 # A C function associated to a Nit method
1552 # Because of customization, a given Nit method can be compiler more that once
1553 abstract class AbstractRuntimeFunction
1554
1555 type COMPILER: AbstractCompiler
1556 type VISITOR: AbstractCompilerVisitor
1557
1558 # The associated Nit method
1559 var mmethoddef: MMethodDef
1560
1561 # The mangled c name of the runtime_function
1562 # Subclasses should redefine `build_c_name` instead
1563 fun c_name: String
1564 do
1565 var res = self.c_name_cache
1566 if res != null then return res
1567 res = self.build_c_name
1568 self.c_name_cache = res
1569 return res
1570 end
1571
1572 # Non cached version of `c_name`
1573 protected fun build_c_name: String is abstract
1574
1575 protected var c_name_cache: nullable String = null is writable
1576
1577 # Implements a call of the runtime_function
1578 # May inline the body or generate a C function call
1579 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1580
1581 # Generate the code for the `AbstractRuntimeFunction`
1582 # Warning: compile more than once compilation makes CC unhappy
1583 fun compile_to_c(compiler: COMPILER) is abstract
1584 end
1585
1586 # A runtime variable hold a runtime value in C.
1587 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1588 #
1589 # 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.
1590 class RuntimeVariable
1591 # The name of the variable in the C code
1592 var name: String
1593
1594 # The static type of the variable (as declard in C)
1595 var mtype: MType
1596
1597 # The current casted type of the variable (as known in Nit)
1598 var mcasttype: MType is writable
1599
1600 # If the variable exaclty a mcasttype?
1601 # false (usual value) means that the variable is a mcasttype or a subtype.
1602 var is_exact: Bool = false is writable
1603
1604 init(name: String, mtype: MType, mcasttype: MType)
1605 do
1606 self.name = name
1607 self.mtype = mtype
1608 self.mcasttype = mcasttype
1609 assert not mtype.need_anchor
1610 assert not mcasttype.need_anchor
1611 end
1612
1613 redef fun to_s do return name
1614
1615 redef fun inspect
1616 do
1617 var exact_str
1618 if self.is_exact then
1619 exact_str = " exact"
1620 else
1621 exact_str = ""
1622 end
1623 var type_str
1624 if self.mtype == self.mcasttype then
1625 type_str = "{mtype}{exact_str}"
1626 else
1627 type_str = "{mtype}({mcasttype}{exact_str})"
1628 end
1629 return "<{name}:{type_str}>"
1630 end
1631 end
1632
1633 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1634 class Frame
1635
1636 type VISITOR: AbstractCompilerVisitor
1637
1638 # The associated visitor
1639 var visitor: VISITOR
1640
1641 # The executed property.
1642 # A Method in case of a call, an attribute in case of a default initialization.
1643 var mpropdef: MPropDef
1644
1645 # The static type of the receiver
1646 var receiver: MClassType
1647
1648 # Arguments of the method (the first is the receiver)
1649 var arguments: Array[RuntimeVariable]
1650
1651 # The runtime_variable associated to the return (in a function)
1652 var returnvar: nullable RuntimeVariable = null is writable
1653
1654 # The label at the end of the property
1655 var returnlabel: nullable String = null is writable
1656 end
1657
1658 redef class MType
1659 # Return the C type associated to a given Nit static type
1660 fun ctype: String do return "val*"
1661
1662 # C type outside of the compiler code and in boxes
1663 fun ctype_extern: String do return "val*"
1664
1665 # Short name of the `ctype` to use in unions
1666 fun ctypename: String do return "val"
1667
1668 # Return the name of the C structure associated to a Nit live type
1669 fun c_name: String is abstract
1670 protected var c_name_cache: nullable String is protected writable
1671 end
1672
1673 redef class MClassType
1674 redef fun c_name
1675 do
1676 var res = self.c_name_cache
1677 if res != null then return res
1678 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1679 self.c_name_cache = res
1680 return res
1681 end
1682
1683 redef fun ctype: String
1684 do
1685 if mclass.name == "Int" then
1686 return "long"
1687 else if mclass.name == "Bool" then
1688 return "short int"
1689 else if mclass.name == "Char" then
1690 return "char"
1691 else if mclass.name == "Float" then
1692 return "double"
1693 else if mclass.name == "NativeString" then
1694 return "char*"
1695 else if mclass.name == "NativeArray" then
1696 return "val*"
1697 else
1698 return "val*"
1699 end
1700 end
1701
1702 redef fun ctype_extern: String
1703 do
1704 if mclass.kind == extern_kind then
1705 return "void*"
1706 else
1707 return ctype
1708 end
1709 end
1710
1711 redef fun ctypename: String
1712 do
1713 if mclass.name == "Int" then
1714 return "l"
1715 else if mclass.name == "Bool" then
1716 return "s"
1717 else if mclass.name == "Char" then
1718 return "c"
1719 else if mclass.name == "Float" then
1720 return "d"
1721 else if mclass.name == "NativeString" then
1722 return "str"
1723 else if mclass.name == "NativeArray" then
1724 #return "{self.arguments.first.ctype}*"
1725 return "val"
1726 else
1727 return "val"
1728 end
1729 end
1730 end
1731
1732 redef class MGenericType
1733 redef fun c_name
1734 do
1735 var res = self.c_name_cache
1736 if res != null then return res
1737 res = super
1738 for t in self.arguments do
1739 res = res + t.c_name
1740 end
1741 self.c_name_cache = res
1742 return res
1743 end
1744 end
1745
1746 redef class MParameterType
1747 redef fun c_name
1748 do
1749 var res = self.c_name_cache
1750 if res != null then return res
1751 res = "{self.mclass.c_name}_FT{self.rank}"
1752 self.c_name_cache = res
1753 return res
1754 end
1755 end
1756
1757 redef class MVirtualType
1758 redef fun c_name
1759 do
1760 var res = self.c_name_cache
1761 if res != null then return res
1762 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1763 self.c_name_cache = res
1764 return res
1765 end
1766 end
1767
1768 redef class MNullableType
1769 redef fun c_name
1770 do
1771 var res = self.c_name_cache
1772 if res != null then return res
1773 res = "nullable_{self.mtype.c_name}"
1774 self.c_name_cache = res
1775 return res
1776 end
1777 end
1778
1779 redef class MClass
1780 # Return the name of the C structure associated to a Nit class
1781 fun c_name: String do
1782 var res = self.c_name_cache
1783 if res != null then return res
1784 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1785 self.c_name_cache = res
1786 return res
1787 end
1788 private var c_name_cache: nullable String
1789 end
1790
1791 redef class MProperty
1792 fun c_name: String do
1793 var res = self.c_name_cache
1794 if res != null then return res
1795 res = "{self.intro.c_name}"
1796 self.c_name_cache = res
1797 return res
1798 end
1799 private var c_name_cache: nullable String
1800 end
1801
1802 redef class MPropDef
1803 type VISITOR: AbstractCompilerVisitor
1804
1805 private var c_name_cache: nullable String
1806
1807 # The mangled name associated to the property
1808 fun c_name: String
1809 do
1810 var res = self.c_name_cache
1811 if res != null then return res
1812 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1813 self.c_name_cache = res
1814 return res
1815 end
1816 end
1817
1818 redef class MMethodDef
1819 # Can the body be inlined?
1820 fun can_inline(v: VISITOR): Bool
1821 do
1822 if is_abstract then return true
1823 var modelbuilder = v.compiler.modelbuilder
1824 if modelbuilder.mpropdef2npropdef.has_key(self) then
1825 var npropdef = modelbuilder.mpropdef2npropdef[self]
1826 return npropdef.can_inline
1827 else if self.mproperty.is_root_init then
1828 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1829 return true
1830 else
1831 abort
1832 end
1833 end
1834
1835 # Inline the body in another visitor
1836 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1837 do
1838 var modelbuilder = v.compiler.modelbuilder
1839 var val = constant_value
1840 if modelbuilder.mpropdef2npropdef.has_key(self) then
1841 var npropdef = modelbuilder.mpropdef2npropdef[self]
1842 var oldnode = v.current_node
1843 v.current_node = npropdef
1844 self.compile_parameter_check(v, arguments)
1845 npropdef.compile_to_c(v, self, arguments)
1846 v.current_node = oldnode
1847 else if self.mproperty.is_root_init then
1848 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1849 var oldnode = v.current_node
1850 v.current_node = nclassdef
1851 self.compile_parameter_check(v, arguments)
1852 nclassdef.compile_to_c(v, self, arguments)
1853 v.current_node = oldnode
1854 else if val != null then
1855 v.ret(v.value_instance(val))
1856 else
1857 abort
1858 end
1859 return null
1860 end
1861
1862 # Generate type checks in the C code to check covariant parameters
1863 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1864 do
1865 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1866
1867 for i in [0..msignature.arity[ do
1868 # skip test for vararg since the array is instantiated with the correct polymorphic type
1869 if msignature.vararg_rank == i then continue
1870
1871 # skip if the cast is not required
1872 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1873 if not origmtype.need_anchor then continue
1874
1875 # get the parameter type
1876 var mtype = self.msignature.mparameters[i].mtype
1877
1878 # generate the cast
1879 # note that v decides if and how to implements the cast
1880 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1881 v.add_cast(arguments[i+1], mtype, "covariance")
1882 end
1883 end
1884 end
1885
1886 # Node visit
1887
1888 redef class APropdef
1889 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1890 do
1891 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1892 debug("Not yet implemented")
1893 end
1894
1895 fun can_inline: Bool do return true
1896 end
1897
1898 redef class AMethPropdef
1899 redef fun compile_to_c(v, mpropdef, arguments)
1900 do
1901 if mpropdef.is_abstract then
1902 var cn = v.class_name_string(arguments.first)
1903 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1904 v.add_raw_abort
1905 return
1906 end
1907
1908 # Call the implicit super-init
1909 var auto_super_inits = self.auto_super_inits
1910 if auto_super_inits != null then
1911 var args = [arguments.first]
1912 for auto_super_init in auto_super_inits do
1913 assert auto_super_init.mproperty != mpropdef.mproperty
1914 args.clear
1915 for i in [0..auto_super_init.msignature.arity+1[ do
1916 args.add(arguments[i])
1917 end
1918 assert auto_super_init.mproperty != mpropdef.mproperty
1919 v.compile_callsite(auto_super_init, args)
1920 end
1921 end
1922 if auto_super_call then
1923 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1924 end
1925
1926 # Try special compilation
1927 if mpropdef.is_intern then
1928 if compile_intern_to_c(v, mpropdef, arguments) then return
1929 else if mpropdef.is_extern then
1930 if mpropdef.mproperty.is_init then
1931 if compile_externinit_to_c(v, mpropdef, arguments) then return
1932 else
1933 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1934 end
1935 end
1936
1937 # Compile block if any
1938 var n_block = n_block
1939 if n_block != null then
1940 for i in [0..mpropdef.msignature.arity[ do
1941 var variable = self.n_signature.n_params[i].variable.as(not null)
1942 v.assign(v.variable(variable), arguments[i+1])
1943 end
1944 v.stmt(n_block)
1945 return
1946 end
1947
1948 # We have a problem
1949 var cn = v.class_name_string(arguments.first)
1950 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1951 v.add_raw_abort
1952 end
1953
1954 redef fun can_inline
1955 do
1956 if self.auto_super_inits != null then return false
1957 var nblock = self.n_block
1958 if nblock == null then return true
1959 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1960 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1961 return false
1962 end
1963
1964 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1965 do
1966 var pname = mpropdef.mproperty.name
1967 var cname = mpropdef.mclassdef.mclass.name
1968 var ret = mpropdef.msignature.return_mtype
1969 if ret != null then
1970 ret = v.resolve_for(ret, arguments.first)
1971 end
1972 if pname != "==" and pname != "!=" then
1973 v.adapt_signature(mpropdef, arguments)
1974 v.unbox_signature_extern(mpropdef, arguments)
1975 end
1976 if cname == "Int" then
1977 if pname == "output" then
1978 v.add("printf(\"%ld\\n\", {arguments.first});")
1979 return true
1980 else if pname == "object_id" then
1981 v.ret(arguments.first)
1982 return true
1983 else if pname == "+" then
1984 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1985 return true
1986 else if pname == "-" then
1987 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1988 return true
1989 else if pname == "unary -" then
1990 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1991 return true
1992 else if pname == "*" then
1993 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1994 return true
1995 else if pname == "/" then
1996 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1997 return true
1998 else if pname == "%" then
1999 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2000 return true
2001 else if pname == "lshift" then
2002 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2003 return true
2004 else if pname == "rshift" then
2005 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2006 return true
2007 else if pname == "==" then
2008 v.ret(v.equal_test(arguments[0], arguments[1]))
2009 return true
2010 else if pname == "!=" then
2011 var res = v.equal_test(arguments[0], arguments[1])
2012 v.ret(v.new_expr("!{res}", ret.as(not null)))
2013 return true
2014 else if pname == "<" then
2015 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2016 return true
2017 else if pname == ">" then
2018 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2019 return true
2020 else if pname == "<=" then
2021 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2022 return true
2023 else if pname == ">=" then
2024 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2025 return true
2026 else if pname == "to_f" then
2027 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2028 return true
2029 else if pname == "ascii" then
2030 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2031 return true
2032 end
2033 else if cname == "Char" then
2034 if pname == "output" then
2035 v.add("printf(\"%c\", {arguments.first});")
2036 return true
2037 else if pname == "object_id" then
2038 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2039 return true
2040 else if pname == "successor" then
2041 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2042 return true
2043 else if pname == "predecessor" 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.equal_test(arguments[0], arguments[1]))
2048 return true
2049 else if pname == "!=" then
2050 var res = v.equal_test(arguments[0], arguments[1])
2051 v.ret(v.new_expr("!{res}", ret.as(not null)))
2052 return true
2053 else if pname == "<" then
2054 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2055 return true
2056 else if pname == ">" then
2057 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2058 return true
2059 else if pname == "<=" then
2060 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2061 return true
2062 else if pname == ">=" then
2063 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2064 return true
2065 else if pname == "to_i" then
2066 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2067 return true
2068 else if pname == "ascii" then
2069 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2070 return true
2071 end
2072 else if cname == "Bool" then
2073 if pname == "output" then
2074 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2075 return true
2076 else if pname == "object_id" then
2077 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2078 return true
2079 else if pname == "==" then
2080 v.ret(v.equal_test(arguments[0], arguments[1]))
2081 return true
2082 else if pname == "!=" then
2083 var res = v.equal_test(arguments[0], arguments[1])
2084 v.ret(v.new_expr("!{res}", ret.as(not null)))
2085 return true
2086 end
2087 else if cname == "Float" then
2088 if pname == "output" then
2089 v.add("printf(\"%f\\n\", {arguments.first});")
2090 return true
2091 else if pname == "object_id" then
2092 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2093 return true
2094 else if pname == "+" then
2095 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2096 return true
2097 else if pname == "-" then
2098 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2099 return true
2100 else if pname == "unary -" then
2101 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2102 return true
2103 else if pname == "succ" then
2104 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2105 return true
2106 else if pname == "prec" then
2107 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2108 return true
2109 else if pname == "*" then
2110 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2111 return true
2112 else if pname == "/" then
2113 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2114 return true
2115 else if pname == "==" then
2116 v.ret(v.equal_test(arguments[0], arguments[1]))
2117 return true
2118 else if pname == "!=" then
2119 var res = v.equal_test(arguments[0], arguments[1])
2120 v.ret(v.new_expr("!{res}", ret.as(not null)))
2121 return true
2122 else if pname == "<" then
2123 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2124 return true
2125 else if pname == ">" then
2126 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2127 return true
2128 else if pname == "<=" then
2129 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2130 return true
2131 else if pname == ">=" then
2132 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2133 return true
2134 else if pname == "to_i" then
2135 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2136 return true
2137 end
2138 else if cname == "NativeString" then
2139 if pname == "[]" then
2140 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2141 return true
2142 else if pname == "[]=" then
2143 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2144 return true
2145 else if pname == "copy_to" then
2146 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2147 return true
2148 else if pname == "atoi" then
2149 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2150 return true
2151 else if pname == "new" then
2152 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2153 return true
2154 end
2155 else if cname == "NativeArray" then
2156 v.native_array_def(pname, ret, arguments)
2157 return true
2158 end
2159 if pname == "exit" then
2160 v.add("exit({arguments[1]});")
2161 return true
2162 else if pname == "sys" then
2163 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2164 return true
2165 else if pname == "calloc_string" then
2166 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2167 return true
2168 else if pname == "calloc_array" then
2169 v.calloc_array(ret.as(not null), arguments)
2170 return true
2171 else if pname == "object_id" then
2172 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2173 return true
2174 else if pname == "is_same_type" then
2175 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2176 return true
2177 else if pname == "is_same_instance" then
2178 v.ret(v.equal_test(arguments[0], arguments[1]))
2179 return true
2180 else if pname == "output_class_name" then
2181 var nat = v.class_name_string(arguments.first)
2182 v.add("printf(\"%s\\n\", {nat});")
2183 return true
2184 else if pname == "native_class_name" then
2185 var nat = v.class_name_string(arguments.first)
2186 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2187 return true
2188 else if pname == "force_garbage_collection" then
2189 v.add("nit_gcollect();")
2190 return true
2191 else if pname == "native_argc" then
2192 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2193 return true
2194 else if pname == "native_argv" then
2195 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2196 return true
2197 end
2198 return false
2199 end
2200
2201 # Compile an extern method
2202 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2203 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2204 do
2205 var externname
2206 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2207 if at != null then
2208 externname = at.arg_as_string(v.compiler.modelbuilder)
2209 if externname == null then return false
2210 else
2211 return false
2212 end
2213 if location.file != null then
2214 var file = location.file.filename
2215 v.add_extern(file)
2216 end
2217 var res: nullable RuntimeVariable = null
2218 var ret = mpropdef.msignature.return_mtype
2219 if ret != null then
2220 ret = v.resolve_for(ret, arguments.first)
2221 res = v.new_var_extern(ret)
2222 end
2223 v.adapt_signature(mpropdef, arguments)
2224 v.unbox_signature_extern(mpropdef, arguments)
2225
2226 if res == null then
2227 v.add("{externname}({arguments.join(", ")});")
2228 else
2229 v.add("{res} = {externname}({arguments.join(", ")});")
2230 res = v.box_extern(res, ret.as(not null))
2231 v.ret(res)
2232 end
2233 return true
2234 end
2235
2236 # Compile an extern factory
2237 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2238 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2239 do
2240 var externname
2241 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2242 if at != null then
2243 externname = at.arg_as_string(v.compiler.modelbuilder)
2244 if externname == null then return false
2245 else
2246 return false
2247 end
2248 if location.file != null then
2249 var file = location.file.filename
2250 v.add_extern(file)
2251 end
2252 v.adapt_signature(mpropdef, arguments)
2253 v.unbox_signature_extern(mpropdef, arguments)
2254 var ret = arguments.first.mtype
2255 var res = v.new_var_extern(ret)
2256
2257 arguments.shift
2258
2259 v.add("{res} = {externname}({arguments.join(", ")});")
2260 res = v.box_extern(res, ret)
2261 v.ret(res)
2262 return true
2263 end
2264 end
2265
2266 redef class AAttrPropdef
2267 redef fun compile_to_c(v, mpropdef, arguments)
2268 do
2269 if mpropdef == mreadpropdef then
2270 assert arguments.length == 1
2271 var recv = arguments.first
2272 var res
2273 if is_lazy then
2274 var set
2275 var ret = self.mpropdef.static_mtype
2276 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2277 var guard = self.mlazypropdef.mproperty
2278 if useiset then
2279 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2280 else
2281 set = v.read_attribute(guard, recv)
2282 end
2283 v.add("if(likely({set})) \{")
2284 res = v.read_attribute(self.mpropdef.mproperty, recv)
2285 v.add("\} else \{")
2286
2287 var value = evaluate_expr(v, recv)
2288
2289 v.assign(res, value)
2290 if not useiset then
2291 var true_v = v.new_expr("1", v.bool_type)
2292 v.write_attribute(guard, arguments.first, true_v)
2293 end
2294 v.add("\}")
2295 else
2296 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2297 end
2298 v.assign(v.frame.returnvar.as(not null), res)
2299 else if mpropdef == mwritepropdef then
2300 assert arguments.length == 2
2301 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2302 if is_lazy then
2303 var ret = self.mpropdef.static_mtype
2304 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2305 if not useiset then
2306 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2307 end
2308 end
2309 else
2310 abort
2311 end
2312 end
2313
2314 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2315 do
2316 if has_value and not is_lazy then evaluate_expr(v, recv)
2317 end
2318
2319 # Evaluate, store and return the default value of the attribute
2320 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2321 do
2322 var oldnode = v.current_node
2323 v.current_node = self
2324 var old_frame = v.frame
2325 var frame = new Frame(v, self.mpropdef.as(not null), recv.mcasttype.as(MClassType), [recv])
2326 v.frame = frame
2327
2328 var value
2329 var mtype = self.mpropdef.static_mtype
2330 assert mtype != null
2331
2332 var nexpr = self.n_expr
2333 var nblock = self.n_block
2334 if nexpr != null then
2335 value = v.expr(nexpr, mtype)
2336 else if nblock != null then
2337 value = v.new_var(mtype)
2338 frame.returnvar = value
2339 frame.returnlabel = v.get_name("RET_LABEL")
2340 v.add("\{")
2341 v.stmt(nblock)
2342 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2343 v.add("\}")
2344 else
2345 abort
2346 end
2347
2348 v.write_attribute(self.mpropdef.mproperty, recv, value)
2349
2350 v.frame = old_frame
2351 v.current_node = oldnode
2352
2353 return value
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