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