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