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