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