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