Merge branch 'dump_rta'
[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")
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}: {ofiles.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
571 v.add("glob_argc = argc; glob_argv = argv;")
572 v.add("initialize_gc_option();")
573 var main_type = mainmodule.sys_type
574 if main_type != null then
575 var mainmodule = v.compiler.mainmodule
576 var glob_sys = v.init_instance(main_type)
577 v.add("glob_sys = {glob_sys};")
578 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
579 if main_init != null then
580 v.send(main_init, [glob_sys])
581 end
582 var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass)
583 if main_method != null then
584 v.send(main_method, [glob_sys])
585 end
586 end
587
588 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
589 v.add_decl("long count_type_test_resolved_total = 0;")
590 v.add_decl("long count_type_test_unresolved_total = 0;")
591 v.add_decl("long count_type_test_skipped_total = 0;")
592 v.add_decl("long count_type_test_total_total = 0;")
593 for tag in count_type_test_tags do
594 v.add_decl("long count_type_test_total_{tag};")
595 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
596 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
597 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
598 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
599 v.add("count_type_test_total_total += count_type_test_total_{tag};")
600 end
601 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
602 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
603 var tags = count_type_test_tags.to_a
604 tags.add("total")
605 for tag in tags do
606 v.add("printf(\"{tag}\");")
607 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
608 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
609 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
610 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
611 end
612 end
613
614 v.add("return 0;")
615 v.add("\}")
616 end
617
618 # List of additional files required to compile (FFI)
619 var extern_bodies = new Array[ExternFile]
620
621 # List of source files to copy over to the compile dir
622 var files_to_copy = new Array[String]
623
624 # This is used to avoid adding an extern file more than once
625 private var seen_extern = new ArraySet[String]
626
627 # Generate code that initialize the attributes on a new instance
628 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
629 do
630 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
631 self.mainmodule.linearize_mclassdefs(cds)
632 for cd in cds do
633 var n = self.modelbuilder.mclassdef2nclassdef[cd]
634 for npropdef in n.n_propdefs do
635 if npropdef isa AAttrPropdef then
636 npropdef.init_expr(v, recv)
637 end
638 end
639 end
640 end
641
642 # Generate code that check if an attribute is correctly initialized
643 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
644 do
645 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
646 self.mainmodule.linearize_mclassdefs(cds)
647 for cd in cds do
648 var n = self.modelbuilder.mclassdef2nclassdef[cd]
649 for npropdef in n.n_propdefs do
650 if npropdef isa AAttrPropdef then
651 npropdef.check_expr(v, recv)
652 end
653 end
654 end
655 end
656
657 # stats
658
659 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
660 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
661 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
662 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
663
664 protected fun init_count_type_test_tags: HashMap[String, Int]
665 do
666 var res = new HashMap[String, Int]
667 for tag in count_type_test_tags do
668 res[tag] = 0
669 end
670 return res
671 end
672
673 # Display stats about compilation process
674 # Metrics used:
675 # * type tests against resolved types (`x isa Collection[Animal]`)
676 # * type tests against unresolved types (`x isa Collection[E]`)
677 # * type tests skipped
678 # * type tests total
679 # *
680 fun display_stats
681 do
682 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
683 print "# static count_type_test"
684 print "\tresolved:\tunresolved\tskipped\ttotal"
685 var count_type_test_total = init_count_type_test_tags
686 count_type_test_resolved["total"] = 0
687 count_type_test_unresolved["total"] = 0
688 count_type_test_skipped["total"] = 0
689 count_type_test_total["total"] = 0
690 for tag in count_type_test_tags do
691 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
692 count_type_test_resolved["total"] += count_type_test_resolved[tag]
693 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
694 count_type_test_skipped["total"] += count_type_test_skipped[tag]
695 count_type_test_total["total"] += count_type_test_total[tag]
696 end
697 var count_type_test = count_type_test_total["total"]
698 var tags = count_type_test_tags.to_a
699 tags.add("total")
700 for tag in tags do
701 printn tag
702 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
703 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
704 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
705 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
706 print ""
707 end
708 end
709 end
710
711 # Division facility
712 # Avoid division by zero by returning the string "n/a"
713 fun div(a,b:Int):String
714 do
715 if b == 0 then return "n/a"
716 return ((a*10000/b).to_f / 100.0).to_precision(2)
717 end
718
719 fun finalize_ffi_for_module(nmodule: AModule)
720 do
721 var visitor = new_visitor
722 nmodule.finalize_ffi(visitor, modelbuilder)
723 nmodule.finalize_nitni(visitor)
724 end
725 end
726
727 # A file unit (may be more than one file if
728 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
729 class CodeFile
730 var name: String
731 var writers = new Array[CodeWriter]
732 var required_declarations = new HashSet[String]
733 end
734
735 # Where to store generated lines
736 class CodeWriter
737 var file: CodeFile
738 var lines: List[String] = new List[String]
739 var decl_lines: List[String] = new List[String]
740
741 # Add a line in the main part of the generated C
742 fun add(s: String) do self.lines.add(s)
743
744 # Add a line in the
745 # (used for local or global declaration)
746 fun add_decl(s: String) do self.decl_lines.add(s)
747
748 init(file: CodeFile)
749 do
750 self.file = file
751 file.writers.add(self)
752 end
753 end
754
755 # A visitor on the AST of property definition that generate the C code.
756 abstract class AbstractCompilerVisitor
757
758 type COMPILER: AbstractCompiler
759
760 # The associated compiler
761 var compiler: COMPILER
762
763 # The current visited AST node
764 var current_node: nullable ANode writable = null
765
766 # The current `Frame`
767 var frame: nullable Frame writable
768
769 # Alias for self.compiler.mainmodule.object_type
770 fun object_type: MClassType do return self.compiler.mainmodule.object_type
771
772 # Alias for self.compiler.mainmodule.bool_type
773 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
774
775 var writer: CodeWriter
776
777 init(compiler: COMPILER)
778 do
779 self.compiler = compiler
780 self.writer = new CodeWriter(compiler.files.last)
781 end
782
783 # Force to get the primitive class named `name` or abort
784 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
785
786 # Force to get the primitive property named `name` in the instance `recv` or abort
787 fun get_property(name: String, recv: MType): MMethod
788 do
789 assert recv isa MClassType
790 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
791 end
792
793 fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
794 do
795 return self.send(callsite.mproperty, args)
796 end
797
798 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
799
800 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
801
802 # Transform varargs, in raw arguments, into a single argument of type `Array`
803 # Note: this method modify the given `args`
804 # If there is no vararg, then `args` is not modified.
805 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
806 do
807 var recv = args.first
808 var vararg_rank = msignature.vararg_rank
809 if vararg_rank >= 0 then
810 assert args.length >= msignature.arity + 1 # because of self
811 var rawargs = args
812 args = new Array[RuntimeVariable]
813
814 args.add(rawargs.first) # recv
815
816 for i in [0..vararg_rank[ do
817 args.add(rawargs[i+1])
818 end
819
820 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
821 var vararg = new Array[RuntimeVariable]
822 for i in [vararg_rank..vararg_lastrank] do
823 vararg.add(rawargs[i+1])
824 end
825
826 var elttype = msignature.mparameters[vararg_rank].mtype
827 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
828
829 for i in [vararg_lastrank+1..rawargs.length-1[ do
830 args.add(rawargs[i+1])
831 end
832 rawargs.clear
833 rawargs.add_all(args)
834 end
835 end
836
837 # Type handling
838
839 # Anchor a type to the main module and the current receiver
840 fun anchor(mtype: MType): MType
841 do
842 if not mtype.need_anchor then return mtype
843 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
844 end
845
846 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
847 do
848 if not mtype.need_anchor then return mtype
849 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
850 end
851
852 # Unsafely cast a value to a new type
853 # ie the result share the same C variable but my have a different mcasttype
854 # NOTE: if the adaptation is useless then `value` is returned as it.
855 # ENSURE: `result.name == value.name`
856 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
857 do
858 mtype = self.anchor(mtype)
859 var valmtype = value.mcasttype
860 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
861 return value
862 end
863
864 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
865 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
866 return res
867 else
868 var res = new RuntimeVariable(value.name, valmtype, mtype)
869 return res
870 end
871 end
872
873 # Generate a super call from a method definition
874 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
875
876 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
877
878 # Box or unbox a value to another type iff a C type conversion is needed
879 # ENSURE: `result.mtype.ctype == mtype.ctype`
880 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
881
882 # Generate a polymorphic subtype test
883 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
884
885 # Generate the code required to dynamically check if 2 objects share the same runtime type
886 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
887
888 # Generate a Nit "is" for two runtime_variables
889 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
890
891 # Sends
892
893 # Generate a static call on a method definition
894 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
895
896 # Generate a polymorphic send for the method `m` and the arguments `args`
897 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
898
899 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
900 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
901 do
902 assert t isa MClassType
903 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
904 return self.call(propdef, t, args)
905 end
906
907 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
908 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
909 do
910 assert t isa MClassType
911 m = m.lookup_next_definition(self.compiler.mainmodule, t)
912 return self.call(m, t, args)
913 end
914
915 # Attributes handling
916
917 # Generate a polymorphic attribute is_set test
918 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
919
920 # Generate a polymorphic attribute read
921 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
922
923 # Generate a polymorphic attribute write
924 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
925
926 # Checks
927
928 # Add a check and an abort for a null reciever if needed
929 fun check_recv_notnull(recv: RuntimeVariable)
930 do
931 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
932
933 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
934 if maybenull then
935 self.add("if ({recv} == NULL) \{")
936 self.add_abort("Receiver is null")
937 self.add("\}")
938 end
939 end
940
941 # Names handling
942
943 private var names: HashSet[String] = new HashSet[String]
944 private var last: Int = 0
945
946 # Return a new name based on `s` and unique in the visitor
947 fun get_name(s: String): String
948 do
949 if not self.names.has(s) then
950 self.names.add(s)
951 return s
952 end
953 var i = self.last + 1
954 loop
955 var s2 = s + i.to_s
956 if not self.names.has(s2) then
957 self.last = i
958 self.names.add(s2)
959 return s2
960 end
961 i = i + 1
962 end
963 end
964
965 # Return an unique and stable identifier associated with an escapemark
966 fun escapemark_name(e: nullable EscapeMark): String
967 do
968 assert e != null
969 if escapemark_names.has_key(e) then return escapemark_names[e]
970 var name = e.name
971 if name == null then name = "label"
972 name = get_name(name)
973 escapemark_names[e] = name
974 return name
975 end
976
977 private var escapemark_names = new HashMap[EscapeMark, String]
978
979 # Return a "const char*" variable associated to the classname of the dynamic type of an object
980 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
981 fun class_name_string(value: RuntimeVariable): String is abstract
982
983 # Variables handling
984
985 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
986
987 # Return the local runtime_variable associated to a Nit local variable
988 fun variable(variable: Variable): RuntimeVariable
989 do
990 if self.variables.has_key(variable) then
991 return self.variables[variable]
992 else
993 var name = self.get_name("var_{variable.name}")
994 var mtype = variable.declared_type.as(not null)
995 mtype = self.anchor(mtype)
996 var res = new RuntimeVariable(name, mtype, mtype)
997 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
998 self.variables[variable] = res
999 return res
1000 end
1001 end
1002
1003 # Return a new uninitialized local runtime_variable
1004 fun new_var(mtype: MType): RuntimeVariable
1005 do
1006 mtype = self.anchor(mtype)
1007 var name = self.get_name("var")
1008 var res = new RuntimeVariable(name, mtype, mtype)
1009 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1010 return res
1011 end
1012
1013 # Return a new uninitialized named runtime_variable
1014 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1015 do
1016 mtype = self.anchor(mtype)
1017 var res = new RuntimeVariable(name, mtype, mtype)
1018 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1019 return res
1020 end
1021
1022 # Correctly assign a left and a right value
1023 # Boxing and unboxing is performed if required
1024 fun assign(left, right: RuntimeVariable)
1025 do
1026 right = self.autobox(right, left.mtype)
1027 self.add("{left} = {right};")
1028 end
1029
1030 # Generate instances
1031
1032 # Generate a alloc-instance + init-attributes
1033 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1034
1035 # Generate an integer value
1036 fun int_instance(value: Int): RuntimeVariable
1037 do
1038 var res = self.new_var(self.get_class("Int").mclass_type)
1039 self.add("{res} = {value};")
1040 return res
1041 end
1042
1043 # Generate a string value
1044 fun string_instance(string: String): RuntimeVariable
1045 do
1046 var mtype = self.get_class("String").mclass_type
1047 var name = self.get_name("varonce")
1048 self.add_decl("static {mtype.ctype} {name};")
1049 var res = self.new_var(mtype)
1050 self.add("if ({name}) \{")
1051 self.add("{res} = {name};")
1052 self.add("\} else \{")
1053 var native_mtype = self.get_class("NativeString").mclass_type
1054 var nat = self.new_var(native_mtype)
1055 self.add("{nat} = \"{string.escape_to_c}\";")
1056 var length = self.int_instance(string.length)
1057 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1058 self.add("{name} = {res};")
1059 self.add("\}")
1060 return res
1061 end
1062
1063 # Generate an array value
1064 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1065
1066 # Get an instance of a array for a vararg
1067 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1068
1069 # Code generation
1070
1071 # Add a line in the main part of the generated C
1072 fun add(s: String) do self.writer.lines.add(s)
1073
1074 # Add a line in the
1075 # (used for local or global declaration)
1076 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1077
1078 # Request the presence of a global declaration
1079 fun require_declaration(key: String)
1080 do
1081 var reqs = self.writer.file.required_declarations
1082 if reqs.has(key) then return
1083 reqs.add(key)
1084 var node = current_node
1085 if node != null then compiler.requirers_of_declarations[key] = node
1086 end
1087
1088 # Add a declaration in the local-header
1089 # The declaration is ensured to be present once
1090 fun declare_once(s: String)
1091 do
1092 self.compiler.provide_declaration(s, s)
1093 self.require_declaration(s)
1094 end
1095
1096 # look for a needed .h and .c file for a given .nit source-file
1097 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1098 fun add_extern(file: String)
1099 do
1100 file = file.strip_extension(".nit")
1101 var tryfile = file + ".nit.h"
1102 if tryfile.file_exists then
1103 self.declare_once("#include \"{tryfile.basename("")}\"")
1104 self.compiler.files_to_copy.add(tryfile)
1105 end
1106 tryfile = file + "_nit.h"
1107 if tryfile.file_exists then
1108 self.declare_once("#include \"{tryfile.basename("")}\"")
1109 self.compiler.files_to_copy.add(tryfile)
1110 end
1111
1112 if self.compiler.seen_extern.has(file) then return
1113 self.compiler.seen_extern.add(file)
1114 tryfile = file + ".nit.c"
1115 if not tryfile.file_exists then
1116 tryfile = file + "_nit.c"
1117 if not tryfile.file_exists then return
1118 end
1119 var f = new ExternCFile(tryfile.basename(""), "")
1120 self.compiler.extern_bodies.add(f)
1121 self.compiler.files_to_copy.add(tryfile)
1122 end
1123
1124 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1125 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1126 do
1127 var res = new_var(mtype)
1128 self.add("{res} = {cexpr};")
1129 return res
1130 end
1131
1132 # Generate generic abort
1133 # used by aborts, asserts, casts, etc.
1134 fun add_abort(message: String)
1135 do
1136 self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");")
1137 add_raw_abort
1138 end
1139
1140 fun add_raw_abort
1141 do
1142 if self.current_node != null and self.current_node.location.file != null then
1143 self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1144 else
1145 self.add("fprintf(stderr, \"\\n\");")
1146 end
1147 self.add("show_backtrace(1);")
1148 end
1149
1150 # Add a dynamic cast
1151 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1152 do
1153 var res = self.type_test(value, mtype, tag)
1154 self.add("if (!{res}) \{")
1155 var cn = self.class_name_string(value)
1156 self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1157 self.add_raw_abort
1158 self.add("\}")
1159 end
1160
1161 # Generate a return with the value `s`
1162 fun ret(s: RuntimeVariable)
1163 do
1164 self.assign(self.frame.returnvar.as(not null), s)
1165 self.add("goto {self.frame.returnlabel.as(not null)};")
1166 end
1167
1168 # Compile a statement (if any)
1169 fun stmt(nexpr: nullable AExpr)
1170 do
1171 if nexpr == null then return
1172 var old = self.current_node
1173 self.current_node = nexpr
1174 nexpr.stmt(self)
1175 self.current_node = old
1176 end
1177
1178 # Compile an expression an return its result
1179 # `mtype` is the expected return type, pass null if no specific type is expected.
1180 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1181 do
1182 var old = self.current_node
1183 self.current_node = nexpr
1184 var res = nexpr.expr(self).as(not null)
1185 if mtype != null then
1186 mtype = self.anchor(mtype)
1187 res = self.autobox(res, mtype)
1188 end
1189 res = autoadapt(res, nexpr.mtype.as(not null))
1190 var implicit_cast_to = nexpr.implicit_cast_to
1191 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1192 add_cast(res, implicit_cast_to, "auto")
1193 res = autoadapt(res, implicit_cast_to)
1194 end
1195 self.current_node = old
1196 return res
1197 end
1198
1199 # Alias for `self.expr(nexpr, self.bool_type)`
1200 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1201
1202 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1203 fun debug(message: String)
1204 do
1205 var node = self.current_node
1206 if node == null then
1207 print "?: {message}"
1208 else
1209 node.debug(message)
1210 end
1211 self.add("/* DEBUG: {message} */")
1212 end
1213 end
1214
1215 # A C function associated to a Nit method
1216 # Because of customization, a given Nit method can be compiler more that once
1217 abstract class AbstractRuntimeFunction
1218
1219 type COMPILER: AbstractCompiler
1220 type VISITOR: AbstractCompilerVisitor
1221
1222 # The associated Nit method
1223 var mmethoddef: MMethodDef
1224
1225 # The mangled c name of the runtime_function
1226 # Subclasses should redefine `build_c_name` instead
1227 fun c_name: String
1228 do
1229 var res = self.c_name_cache
1230 if res != null then return res
1231 res = self.build_c_name
1232 self.c_name_cache = res
1233 return res
1234 end
1235
1236 # Non cached version of `c_name`
1237 protected fun build_c_name: String is abstract
1238
1239 protected var c_name_cache: nullable String writable = null
1240
1241 # Implements a call of the runtime_function
1242 # May inline the body or generate a C function call
1243 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1244
1245 # Generate the code for the `AbstractRuntimeFunction`
1246 # Warning: compile more than once compilation makes CC unhappy
1247 fun compile_to_c(compiler: COMPILER) is abstract
1248 end
1249
1250 # A runtime variable hold a runtime value in C.
1251 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1252 #
1253 # 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.
1254 class RuntimeVariable
1255 # The name of the variable in the C code
1256 var name: String
1257
1258 # The static type of the variable (as declard in C)
1259 var mtype: MType
1260
1261 # The current casted type of the variable (as known in Nit)
1262 var mcasttype: MType writable
1263
1264 # If the variable exaclty a mcasttype?
1265 # false (usual value) means that the variable is a mcasttype or a subtype.
1266 var is_exact: Bool writable = false
1267
1268 init(name: String, mtype: MType, mcasttype: MType)
1269 do
1270 self.name = name
1271 self.mtype = mtype
1272 self.mcasttype = mcasttype
1273 assert not mtype.need_anchor
1274 assert not mcasttype.need_anchor
1275 end
1276
1277 redef fun to_s do return name
1278
1279 redef fun inspect
1280 do
1281 var exact_str
1282 if self.is_exact then
1283 exact_str = " exact"
1284 else
1285 exact_str = ""
1286 end
1287 var type_str
1288 if self.mtype == self.mcasttype then
1289 type_str = "{mtype}{exact_str}"
1290 else
1291 type_str = "{mtype}({mcasttype}{exact_str})"
1292 end
1293 return "<{name}:{type_str}>"
1294 end
1295 end
1296
1297 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1298 class Frame
1299
1300 type VISITOR: AbstractCompilerVisitor
1301
1302 # The associated visitor
1303 var visitor: VISITOR
1304
1305 # The executed property.
1306 # A Method in case of a call, an attribute in case of a default initialization.
1307 var mpropdef: MPropDef
1308
1309 # The static type of the receiver
1310 var receiver: MClassType
1311
1312 # Arguments of the method (the first is the receiver)
1313 var arguments: Array[RuntimeVariable]
1314
1315 # The runtime_variable associated to the return (in a function)
1316 var returnvar: nullable RuntimeVariable writable = null
1317
1318 # The label at the end of the property
1319 var returnlabel: nullable String writable = null
1320 end
1321
1322 redef class MType
1323 # Return the C type associated to a given Nit static type
1324 fun ctype: String do return "val*"
1325
1326 fun ctypename: String do return "val"
1327
1328 # Return the name of the C structure associated to a Nit live type
1329 fun c_name: String is abstract
1330 protected var c_name_cache: nullable String protected writable
1331 end
1332
1333 redef class MClassType
1334 redef fun c_name
1335 do
1336 var res = self.c_name_cache
1337 if res != null then return res
1338 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1339 self.c_name_cache = res
1340 return res
1341 end
1342
1343 redef fun ctype: String
1344 do
1345 if mclass.name == "Int" then
1346 return "long"
1347 else if mclass.name == "Bool" then
1348 return "short int"
1349 else if mclass.name == "Char" then
1350 return "char"
1351 else if mclass.name == "Float" then
1352 return "double"
1353 else if mclass.name == "NativeString" then
1354 return "char*"
1355 else if mclass.name == "NativeArray" then
1356 return "val*"
1357 else if mclass.kind == extern_kind then
1358 return "void*"
1359 else
1360 return "val*"
1361 end
1362 end
1363
1364 redef fun ctypename: String
1365 do
1366 if mclass.name == "Int" then
1367 return "l"
1368 else if mclass.name == "Bool" then
1369 return "s"
1370 else if mclass.name == "Char" then
1371 return "c"
1372 else if mclass.name == "Float" then
1373 return "d"
1374 else if mclass.name == "NativeString" then
1375 return "str"
1376 else if mclass.name == "NativeArray" then
1377 #return "{self.arguments.first.ctype}*"
1378 return "val"
1379 else if mclass.kind == extern_kind then
1380 return "ptr"
1381 else
1382 return "val"
1383 end
1384 end
1385 end
1386
1387 redef class MGenericType
1388 redef fun c_name
1389 do
1390 var res = self.c_name_cache
1391 if res != null then return res
1392 res = super
1393 for t in self.arguments do
1394 res = res + t.c_name
1395 end
1396 self.c_name_cache = res
1397 return res
1398 end
1399 end
1400
1401 redef class MParameterType
1402 redef fun c_name
1403 do
1404 var res = self.c_name_cache
1405 if res != null then return res
1406 res = "{self.mclass.c_name}_FT{self.rank}"
1407 self.c_name_cache = res
1408 return res
1409 end
1410 end
1411
1412 redef class MVirtualType
1413 redef fun c_name
1414 do
1415 var res = self.c_name_cache
1416 if res != null then return res
1417 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1418 self.c_name_cache = res
1419 return res
1420 end
1421 end
1422
1423 redef class MNullableType
1424 redef fun c_name
1425 do
1426 var res = self.c_name_cache
1427 if res != null then return res
1428 res = "nullable_{self.mtype.c_name}"
1429 self.c_name_cache = res
1430 return res
1431 end
1432 end
1433
1434 redef class MClass
1435 # Return the name of the C structure associated to a Nit class
1436 fun c_name: String do
1437 var res = self.c_name_cache
1438 if res != null then return res
1439 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1440 self.c_name_cache = res
1441 return res
1442 end
1443 private var c_name_cache: nullable String
1444 end
1445
1446 redef class MProperty
1447 fun c_name: String do
1448 var res = self.c_name_cache
1449 if res != null then return res
1450 res = "{self.intro.c_name}"
1451 self.c_name_cache = res
1452 return res
1453 end
1454 private var c_name_cache: nullable String
1455 end
1456
1457 redef class MPropDef
1458 type VISITOR: AbstractCompilerVisitor
1459
1460 private var c_name_cache: nullable String
1461
1462 # The mangled name associated to the property
1463 fun c_name: String
1464 do
1465 var res = self.c_name_cache
1466 if res != null then return res
1467 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1468 self.c_name_cache = res
1469 return res
1470 end
1471 end
1472
1473 redef class MMethodDef
1474 # Can the body be inlined?
1475 fun can_inline(v: VISITOR): Bool
1476 do
1477 var modelbuilder = v.compiler.modelbuilder
1478 if modelbuilder.mpropdef2npropdef.has_key(self) then
1479 var npropdef = modelbuilder.mpropdef2npropdef[self]
1480 return npropdef.can_inline
1481 else if self.mproperty.name == "init" then
1482 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1483 return true
1484 else
1485 abort
1486 end
1487 end
1488
1489 # Inline the body in another visitor
1490 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1491 do
1492 var modelbuilder = v.compiler.modelbuilder
1493 if modelbuilder.mpropdef2npropdef.has_key(self) then
1494 var npropdef = modelbuilder.mpropdef2npropdef[self]
1495 var oldnode = v.current_node
1496 v.current_node = npropdef
1497 self.compile_parameter_check(v, arguments)
1498 npropdef.compile_to_c(v, self, arguments)
1499 v.current_node = oldnode
1500 else if self.mproperty.name == "init" then
1501 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1502 var oldnode = v.current_node
1503 v.current_node = nclassdef
1504 self.compile_parameter_check(v, arguments)
1505 nclassdef.compile_to_c(v, self, arguments)
1506 v.current_node = oldnode
1507 else
1508 abort
1509 end
1510 return null
1511 end
1512
1513 # Generate type checks in the C code to check covariant parameters
1514 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1515 do
1516 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1517
1518 for i in [0..msignature.arity[ do
1519 # skip test for vararg since the array is instantiated with the correct polymorphic type
1520 if msignature.vararg_rank == i then continue
1521
1522 # skip if the cast is not required
1523 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1524 if not origmtype.need_anchor then continue
1525
1526 # get the parameter type
1527 var mtype = self.msignature.mparameters[i].mtype
1528
1529 # generate the cast
1530 # note that v decides if and how to implements the cast
1531 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1532 v.add_cast(arguments[i+1], mtype, "covariance")
1533 end
1534 end
1535 end
1536
1537 # Node visit
1538
1539 redef class APropdef
1540 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1541 do
1542 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1543 debug("Not yet implemented")
1544 end
1545
1546 fun can_inline: Bool do return true
1547 end
1548
1549 redef class AConcreteMethPropdef
1550 redef fun compile_to_c(v, mpropdef, arguments)
1551 do
1552 for i in [0..mpropdef.msignature.arity[ do
1553 var variable = self.n_signature.n_params[i].variable.as(not null)
1554 v.assign(v.variable(variable), arguments[i+1])
1555 end
1556 # Call the implicit super-init
1557 var auto_super_inits = self.auto_super_inits
1558 if auto_super_inits != null then
1559 var args = [arguments.first]
1560 for auto_super_init in auto_super_inits do
1561 args.clear
1562 for i in [0..auto_super_init.msignature.arity+1[ do
1563 args.add(arguments[i])
1564 end
1565 v.compile_callsite(auto_super_init, args)
1566 end
1567 end
1568 v.stmt(self.n_block)
1569 end
1570
1571 redef fun can_inline
1572 do
1573 if self.auto_super_inits != null then return false
1574 var nblock = self.n_block
1575 if nblock == null then return true
1576 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1577 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1578 return false
1579 end
1580 end
1581
1582 redef class AInternMethPropdef
1583 redef fun compile_to_c(v, mpropdef, arguments)
1584 do
1585 var pname = mpropdef.mproperty.name
1586 var cname = mpropdef.mclassdef.mclass.name
1587 var ret = mpropdef.msignature.return_mtype
1588 if ret != null then
1589 ret = v.resolve_for(ret, arguments.first)
1590 end
1591 if pname != "==" and pname != "!=" then
1592 v.adapt_signature(mpropdef, arguments)
1593 end
1594 if cname == "Int" then
1595 if pname == "output" then
1596 v.add("printf(\"%ld\\n\", {arguments.first});")
1597 return
1598 else if pname == "object_id" then
1599 v.ret(arguments.first)
1600 return
1601 else if pname == "+" then
1602 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1603 return
1604 else if pname == "-" then
1605 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1606 return
1607 else if pname == "unary -" then
1608 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1609 return
1610 else if pname == "succ" then
1611 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1612 return
1613 else if pname == "prec" then
1614 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1615 return
1616 else if pname == "*" then
1617 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1618 return
1619 else if pname == "/" then
1620 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1621 return
1622 else if pname == "%" then
1623 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1624 return
1625 else if pname == "lshift" then
1626 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1627 return
1628 else if pname == "rshift" then
1629 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1630 return
1631 else if pname == "==" then
1632 v.ret(v.equal_test(arguments[0], arguments[1]))
1633 return
1634 else if pname == "!=" then
1635 var res = v.equal_test(arguments[0], arguments[1])
1636 v.ret(v.new_expr("!{res}", ret.as(not null)))
1637 return
1638 else if pname == "<" then
1639 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1640 return
1641 else if pname == ">" then
1642 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1643 return
1644 else if pname == "<=" then
1645 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1646 return
1647 else if pname == ">=" then
1648 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1649 return
1650 else if pname == "to_f" then
1651 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1652 return
1653 else if pname == "ascii" then
1654 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1655 return
1656 end
1657 else if cname == "Char" then
1658 if pname == "output" then
1659 v.add("printf(\"%c\", {arguments.first});")
1660 return
1661 else if pname == "object_id" then
1662 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1663 return
1664 else if pname == "+" then
1665 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1666 return
1667 else if pname == "-" then
1668 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1669 return
1670 else if pname == "==" then
1671 v.ret(v.equal_test(arguments[0], arguments[1]))
1672 return
1673 else if pname == "!=" then
1674 var res = v.equal_test(arguments[0], arguments[1])
1675 v.ret(v.new_expr("!{res}", ret.as(not null)))
1676 return
1677 else if pname == "succ" then
1678 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1679 return
1680 else if pname == "prec" then
1681 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1682 return
1683 else if pname == "<" then
1684 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1685 return
1686 else if pname == ">" then
1687 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1688 return
1689 else if pname == "<=" then
1690 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1691 return
1692 else if pname == ">=" then
1693 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1694 return
1695 else if pname == "to_i" then
1696 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1697 return
1698 else if pname == "ascii" then
1699 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1700 return
1701 end
1702 else if cname == "Bool" then
1703 if pname == "output" then
1704 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1705 return
1706 else if pname == "object_id" then
1707 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1708 return
1709 else if pname == "==" then
1710 v.ret(v.equal_test(arguments[0], arguments[1]))
1711 return
1712 else if pname == "!=" then
1713 var res = v.equal_test(arguments[0], arguments[1])
1714 v.ret(v.new_expr("!{res}", ret.as(not null)))
1715 return
1716 end
1717 else if cname == "Float" then
1718 if pname == "output" then
1719 v.add("printf(\"%f\\n\", {arguments.first});")
1720 return
1721 else if pname == "object_id" then
1722 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1723 return
1724 else if pname == "+" then
1725 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1726 return
1727 else if pname == "-" then
1728 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1729 return
1730 else if pname == "unary -" then
1731 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1732 return
1733 else if pname == "succ" then
1734 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1735 return
1736 else if pname == "prec" then
1737 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1738 return
1739 else if pname == "*" then
1740 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1741 return
1742 else if pname == "/" then
1743 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1744 return
1745 else if pname == "==" then
1746 v.ret(v.equal_test(arguments[0], arguments[1]))
1747 return
1748 else if pname == "!=" then
1749 var res = v.equal_test(arguments[0], arguments[1])
1750 v.ret(v.new_expr("!{res}", ret.as(not null)))
1751 return
1752 else if pname == "<" then
1753 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1754 return
1755 else if pname == ">" then
1756 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1757 return
1758 else if pname == "<=" then
1759 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1760 return
1761 else if pname == ">=" then
1762 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1763 return
1764 else if pname == "to_i" then
1765 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1766 return
1767 end
1768 else if cname == "NativeString" then
1769 if pname == "[]" then
1770 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1771 return
1772 else if pname == "[]=" then
1773 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1774 return
1775 else if pname == "copy_to" then
1776 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1777 return
1778 else if pname == "atoi" then
1779 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1780 return
1781 end
1782 else if cname == "NativeArray" then
1783 v.native_array_def(pname, ret, arguments)
1784 return
1785 end
1786 if pname == "exit" then
1787 v.add("exit({arguments[1]});")
1788 return
1789 else if pname == "sys" then
1790 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1791 return
1792 else if pname == "calloc_string" then
1793 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1794 return
1795 else if pname == "calloc_array" then
1796 v.calloc_array(ret.as(not null), arguments)
1797 return
1798 else if pname == "object_id" then
1799 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1800 return
1801 else if pname == "is_same_type" then
1802 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1803 return
1804 else if pname == "is_same_instance" then
1805 v.ret(v.equal_test(arguments[0], arguments[1]))
1806 return
1807 else if pname == "output_class_name" then
1808 var nat = v.class_name_string(arguments.first)
1809 v.add("printf(\"%s\\n\", {nat});")
1810 return
1811 else if pname == "native_class_name" then
1812 var nat = v.class_name_string(arguments.first)
1813 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1814 return
1815 else if pname == "force_garbage_collection" then
1816 v.add("nit_gcollect();")
1817 return
1818 else if pname == "native_argc" then
1819 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1820 return
1821 else if pname == "native_argv" then
1822 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1823 return
1824 end
1825 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1826 debug("Not implemented {mpropdef}")
1827 end
1828 end
1829
1830 redef class AExternMethPropdef
1831 redef fun compile_to_c(v, mpropdef, arguments)
1832 do
1833 var externname
1834 var nextern = self.n_extern
1835 if nextern == null then
1836 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1837 v.add("show_backtrace(1);")
1838 return
1839 end
1840 externname = nextern.text.substring(1, nextern.text.length-2)
1841 if location.file != null then
1842 var file = location.file.filename
1843 v.add_extern(file)
1844 end
1845 var res: nullable RuntimeVariable = null
1846 var ret = mpropdef.msignature.return_mtype
1847 if ret != null then
1848 ret = v.resolve_for(ret, arguments.first)
1849 res = v.new_var(ret)
1850 end
1851 v.adapt_signature(mpropdef, arguments)
1852
1853 if res == null then
1854 v.add("{externname}({arguments.join(", ")});")
1855 else
1856 v.add("{res} = {externname}({arguments.join(", ")});")
1857 v.ret(res)
1858 end
1859 end
1860 end
1861
1862 redef class AExternInitPropdef
1863 redef fun compile_to_c(v, mpropdef, arguments)
1864 do
1865 var externname
1866 var nextern = self.n_extern
1867 if nextern == null then
1868 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1869 v.add("show_backtrace(1);")
1870 return
1871 end
1872 externname = nextern.text.substring(1, nextern.text.length-2)
1873 if location.file != null then
1874 var file = location.file.filename
1875 v.add_extern(file)
1876 end
1877 v.adapt_signature(mpropdef, arguments)
1878 var ret = arguments.first.mtype
1879 var res = v.new_var(ret)
1880
1881 arguments.shift
1882
1883 v.add("{res} = {externname}({arguments.join(", ")});")
1884 v.ret(res)
1885 end
1886 end
1887
1888 redef class AAttrPropdef
1889 redef fun compile_to_c(v, mpropdef, arguments)
1890 do
1891 if arguments.length == 1 then
1892 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1893 v.assign(v.frame.returnvar.as(not null), res)
1894 else
1895 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1896 end
1897 end
1898
1899 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1900 do
1901 var nexpr = self.n_expr
1902 if nexpr != null then
1903 var oldnode = v.current_node
1904 v.current_node = self
1905 var old_frame = v.frame
1906 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1907 v.frame = frame
1908 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1909 v.write_attribute(self.mpropdef.mproperty, recv, value)
1910 v.frame = old_frame
1911 v.current_node = oldnode
1912 end
1913 end
1914
1915 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1916 do
1917 var nexpr = self.n_expr
1918 if nexpr != null then return
1919
1920 var oldnode = v.current_node
1921 v.current_node = self
1922 var old_frame = v.frame
1923 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1924 v.frame = frame
1925 # Force read to check the initialization
1926 v.read_attribute(self.mpropdef.mproperty, recv)
1927 v.frame = old_frame
1928 v.current_node = oldnode
1929 end
1930 end
1931
1932 redef class AClassdef
1933 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1934 do
1935 if mpropdef == self.mfree_init then
1936 var super_inits = self.super_inits
1937 if super_inits != null then
1938 assert arguments.length == 1
1939 for su in super_inits do
1940 v.send(su, arguments)
1941 end
1942 return
1943 end
1944 var recv = arguments.first
1945 var i = 1
1946 # Collect undefined attributes
1947 for npropdef in self.n_propdefs do
1948 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
1949 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
1950 i += 1
1951 end
1952 end
1953 else
1954 abort
1955 end
1956 end
1957 end
1958
1959 redef class ADeferredMethPropdef
1960 redef fun compile_to_c(v, mpropdef, arguments) do
1961 var cn = v.class_name_string(arguments.first)
1962 v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1963 v.add_raw_abort
1964 end
1965 redef fun can_inline do return true
1966 end
1967
1968 redef class AExpr
1969 # Try to compile self as an expression
1970 # Do not call this method directly, use `v.expr` instead
1971 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
1972 do
1973 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
1974 var mtype = self.mtype
1975 if mtype == null then
1976 return null
1977 else
1978 var res = v.new_var(mtype)
1979 v.add("/* {res} = NOT YET {class_name} */")
1980 return res
1981 end
1982 end
1983
1984 # Try to compile self as a statement
1985 # Do not call this method directly, use `v.stmt` instead
1986 private fun stmt(v: AbstractCompilerVisitor)
1987 do
1988 var res = expr(v)
1989 if res != null then v.add("{res};")
1990 end
1991 end
1992
1993 redef class ABlockExpr
1994 redef fun stmt(v)
1995 do
1996 for e in self.n_expr do v.stmt(e)
1997 end
1998 redef fun expr(v)
1999 do
2000 var last = self.n_expr.last
2001 for e in self.n_expr do
2002 if e == last then break
2003 v.stmt(e)
2004 end
2005 return v.expr(last, null)
2006 end
2007 end
2008
2009 redef class AVardeclExpr
2010 redef fun stmt(v)
2011 do
2012 var variable = self.variable.as(not null)
2013 var ne = self.n_expr
2014 if ne != null then
2015 var i = v.expr(ne, variable.declared_type)
2016 v.assign(v.variable(variable), i)
2017 end
2018 end
2019 end
2020
2021 redef class AVarExpr
2022 redef fun expr(v)
2023 do
2024 var res = v.variable(self.variable.as(not null))
2025 var mtype = self.mtype.as(not null)
2026 return v.autoadapt(res, mtype)
2027 end
2028 end
2029
2030 redef class AVarAssignExpr
2031 redef fun stmt(v)
2032 do
2033 var variable = self.variable.as(not null)
2034 var i = v.expr(self.n_value, variable.declared_type)
2035 v.assign(v.variable(variable), i)
2036 end
2037 redef fun expr(v)
2038 do
2039 var variable = self.variable.as(not null)
2040 var i = v.expr(self.n_value, variable.declared_type)
2041 v.assign(v.variable(variable), i)
2042 return i
2043 end
2044 end
2045
2046 redef class AVarReassignExpr
2047 redef fun stmt(v)
2048 do
2049 var variable = self.variable.as(not null)
2050 var vari = v.variable(variable)
2051 var value = v.expr(self.n_value, variable.declared_type)
2052 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2053 assert res != null
2054 v.assign(v.variable(variable), res)
2055 end
2056 end
2057
2058 redef class ASelfExpr
2059 redef fun expr(v) do return v.frame.arguments.first
2060 end
2061
2062 redef class AContinueExpr
2063 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2064 end
2065
2066 redef class ABreakExpr
2067 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2068 end
2069
2070 redef class AReturnExpr
2071 redef fun stmt(v)
2072 do
2073 var nexpr = self.n_expr
2074 if nexpr != null then
2075 var returnvar = v.frame.returnvar.as(not null)
2076 var i = v.expr(nexpr, returnvar.mtype)
2077 v.assign(returnvar, i)
2078 end
2079 v.add("goto {v.frame.returnlabel.as(not null)};")
2080 end
2081 end
2082
2083 redef class AAbortExpr
2084 redef fun stmt(v) do v.add_abort("Aborted")
2085 end
2086
2087 redef class AIfExpr
2088 redef fun stmt(v)
2089 do
2090 var cond = v.expr_bool(self.n_expr)
2091 v.add("if ({cond})\{")
2092 v.stmt(self.n_then)
2093 v.add("\} else \{")
2094 v.stmt(self.n_else)
2095 v.add("\}")
2096 end
2097
2098 redef fun expr(v)
2099 do
2100 var res = v.new_var(self.mtype.as(not null))
2101 var cond = v.expr_bool(self.n_expr)
2102 v.add("if ({cond})\{")
2103 v.assign(res, v.expr(self.n_then.as(not null), null))
2104 v.add("\} else \{")
2105 v.assign(res, v.expr(self.n_else.as(not null), null))
2106 v.add("\}")
2107 return res
2108 end
2109 end
2110
2111 redef class AIfexprExpr
2112 redef fun expr(v)
2113 do
2114 var res = v.new_var(self.mtype.as(not null))
2115 var cond = v.expr_bool(self.n_expr)
2116 v.add("if ({cond})\{")
2117 v.assign(res, v.expr(self.n_then, null))
2118 v.add("\} else \{")
2119 v.assign(res, v.expr(self.n_else, null))
2120 v.add("\}")
2121 return res
2122 end
2123 end
2124
2125 redef class ADoExpr
2126 redef fun stmt(v)
2127 do
2128 v.stmt(self.n_block)
2129 var escapemark = self.escapemark
2130 if escapemark != null then
2131 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2132 end
2133 end
2134 end
2135
2136 redef class AWhileExpr
2137 redef fun stmt(v)
2138 do
2139 v.add("for(;;) \{")
2140 var cond = v.expr_bool(self.n_expr)
2141 v.add("if (!{cond}) break;")
2142 v.stmt(self.n_block)
2143 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2144 v.add("\}")
2145 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2146 end
2147 end
2148
2149 redef class ALoopExpr
2150 redef fun stmt(v)
2151 do
2152 v.add("for(;;) \{")
2153 v.stmt(self.n_block)
2154 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2155 v.add("\}")
2156 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2157 end
2158 end
2159
2160 redef class AForExpr
2161 redef fun stmt(v)
2162 do
2163 # Shortcut on explicit range
2164 # Avoid the instantiation of the range and the iterator
2165 var nexpr = self.n_expr
2166 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2167 var from = v.expr(nexpr.n_expr, null)
2168 var to = v.expr(nexpr.n_expr2, null)
2169 var variable = v.variable(variables.first)
2170
2171 v.assign(variable, from)
2172 v.add("for(;;) \{ /* shortcut range */")
2173
2174 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2175 assert ok != null
2176 v.add("if(!{ok}) break;")
2177
2178 v.stmt(self.n_block)
2179
2180 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2181 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2182 assert succ != null
2183 v.assign(variable, succ)
2184 v.add("\}")
2185 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2186 return
2187 end
2188
2189 var cl = v.expr(self.n_expr, null)
2190 var it_meth = self.method_iterator
2191 assert it_meth != null
2192 var it = v.send(it_meth, [cl])
2193 assert it != null
2194 v.add("for(;;) \{")
2195 var isok_meth = self.method_is_ok
2196 assert isok_meth != null
2197 var ok = v.send(isok_meth, [it])
2198 assert ok != null
2199 v.add("if(!{ok}) break;")
2200 if self.variables.length == 1 then
2201 var item_meth = self.method_item
2202 assert item_meth != null
2203 var i = v.send(item_meth, [it])
2204 assert i != null
2205 v.assign(v.variable(variables.first), i)
2206 else if self.variables.length == 2 then
2207 var key_meth = self.method_key
2208 assert key_meth != null
2209 var i = v.send(key_meth, [it])
2210 assert i != null
2211 v.assign(v.variable(variables[0]), i)
2212 var item_meth = self.method_item
2213 assert item_meth != null
2214 i = v.send(item_meth, [it])
2215 assert i != null
2216 v.assign(v.variable(variables[1]), i)
2217 else
2218 abort
2219 end
2220 v.stmt(self.n_block)
2221 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2222 var next_meth = self.method_next
2223 assert next_meth != null
2224 v.send(next_meth, [it])
2225 v.add("\}")
2226 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2227 end
2228 end
2229
2230 redef class AAssertExpr
2231 redef fun stmt(v)
2232 do
2233 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2234
2235 var cond = v.expr_bool(self.n_expr)
2236 v.add("if (!{cond}) \{")
2237 v.stmt(self.n_else)
2238 var nid = self.n_id
2239 if nid != null then
2240 v.add_abort("Assert '{nid.text}' failed")
2241 else
2242 v.add_abort("Assert failed")
2243 end
2244 v.add("\}")
2245 end
2246 end
2247
2248 redef class AOrExpr
2249 redef fun expr(v)
2250 do
2251 var res = v.new_var(self.mtype.as(not null))
2252 var i1 = v.expr_bool(self.n_expr)
2253 v.add("if ({i1}) \{")
2254 v.add("{res} = 1;")
2255 v.add("\} else \{")
2256 var i2 = v.expr_bool(self.n_expr2)
2257 v.add("{res} = {i2};")
2258 v.add("\}")
2259 return res
2260 end
2261 end
2262
2263 redef class AImpliesExpr
2264 redef fun expr(v)
2265 do
2266 var res = v.new_var(self.mtype.as(not null))
2267 var i1 = v.expr_bool(self.n_expr)
2268 v.add("if (!{i1}) \{")
2269 v.add("{res} = 1;")
2270 v.add("\} else \{")
2271 var i2 = v.expr_bool(self.n_expr2)
2272 v.add("{res} = {i2};")
2273 v.add("\}")
2274 return res
2275 end
2276 end
2277
2278 redef class AAndExpr
2279 redef fun expr(v)
2280 do
2281 var res = v.new_var(self.mtype.as(not null))
2282 var i1 = v.expr_bool(self.n_expr)
2283 v.add("if (!{i1}) \{")
2284 v.add("{res} = 0;")
2285 v.add("\} else \{")
2286 var i2 = v.expr_bool(self.n_expr2)
2287 v.add("{res} = {i2};")
2288 v.add("\}")
2289 return res
2290 end
2291 end
2292
2293 redef class ANotExpr
2294 redef fun expr(v)
2295 do
2296 var cond = v.expr_bool(self.n_expr)
2297 return v.new_expr("!{cond}", self.mtype.as(not null))
2298 end
2299 end
2300
2301 redef class AOrElseExpr
2302 redef fun expr(v)
2303 do
2304 var res = v.new_var(self.mtype.as(not null))
2305 var i1 = v.expr(self.n_expr, null)
2306 v.add("if ({i1}!=NULL) \{")
2307 v.assign(res, i1)
2308 v.add("\} else \{")
2309 var i2 = v.expr(self.n_expr2, null)
2310 v.assign(res, i2)
2311 v.add("\}")
2312 return res
2313 end
2314 end
2315
2316 redef class AIntExpr
2317 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2318 end
2319
2320 redef class AFloatExpr
2321 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2322 end
2323
2324 redef class ACharExpr
2325 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2326 end
2327
2328 redef class AArrayExpr
2329 redef fun expr(v)
2330 do
2331 var mtype = self.mtype.as(MClassType).arguments.first
2332 var array = new Array[RuntimeVariable]
2333 for nexpr in self.n_exprs.n_exprs do
2334 var i = v.expr(nexpr, mtype)
2335 array.add(i)
2336 end
2337 return v.array_instance(array, mtype)
2338 end
2339 end
2340
2341 redef class AStringFormExpr
2342 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2343 end
2344
2345 redef class ASuperstringExpr
2346 redef fun expr(v)
2347 do
2348 var array = new Array[RuntimeVariable]
2349 for ne in self.n_exprs do
2350 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2351 var i = v.expr(ne, null)
2352 array.add(i)
2353 end
2354 var a = v.array_instance(array, v.object_type)
2355 var res = v.send(v.get_property("to_s", a.mtype), [a])
2356 return res
2357 end
2358 end
2359
2360 redef class ACrangeExpr
2361 redef fun expr(v)
2362 do
2363 var i1 = v.expr(self.n_expr, null)
2364 var i2 = v.expr(self.n_expr2, null)
2365 var mtype = self.mtype.as(MClassType)
2366 var res = v.init_instance(mtype)
2367 var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
2368 return res
2369 end
2370 end
2371
2372 redef class AOrangeExpr
2373 redef fun expr(v)
2374 do
2375 var i1 = v.expr(self.n_expr, null)
2376 var i2 = v.expr(self.n_expr2, null)
2377 var mtype = self.mtype.as(MClassType)
2378 var res = v.init_instance(mtype)
2379 var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
2380 return res
2381 end
2382 end
2383
2384 redef class ATrueExpr
2385 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2386 end
2387
2388 redef class AFalseExpr
2389 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2390 end
2391
2392 redef class ANullExpr
2393 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2394 end
2395
2396 redef class AIsaExpr
2397 redef fun expr(v)
2398 do
2399 var i = v.expr(self.n_expr, null)
2400 return v.type_test(i, self.cast_type.as(not null), "isa")
2401 end
2402 end
2403
2404 redef class AAsCastExpr
2405 redef fun expr(v)
2406 do
2407 var i = v.expr(self.n_expr, null)
2408 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2409
2410 v.add_cast(i, self.mtype.as(not null), "as")
2411 return i
2412 end
2413 end
2414
2415 redef class AAsNotnullExpr
2416 redef fun expr(v)
2417 do
2418 var i = v.expr(self.n_expr, null)
2419 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2420
2421 v.add("if ({i} == NULL) \{")
2422 v.add_abort("Cast failed")
2423 v.add("\}")
2424 return i
2425 end
2426 end
2427
2428 redef class AParExpr
2429 redef fun expr(v) do return v.expr(self.n_expr, null)
2430 end
2431
2432 redef class AOnceExpr
2433 redef fun expr(v)
2434 do
2435 var mtype = self.mtype.as(not null)
2436 var name = v.get_name("varonce")
2437 var guard = v.get_name(name + "_guard")
2438 v.add_decl("static {mtype.ctype} {name};")
2439 v.add_decl("static int {guard};")
2440 var res = v.new_var(mtype)
2441 v.add("if ({guard}) \{")
2442 v.add("{res} = {name};")
2443 v.add("\} else \{")
2444 var i = v.expr(self.n_expr, mtype)
2445 v.add("{res} = {i};")
2446 v.add("{name} = {res};")
2447 v.add("{guard} = 1;")
2448 v.add("\}")
2449 return res
2450 end
2451 end
2452
2453 redef class ASendExpr
2454 redef fun expr(v)
2455 do
2456 var recv = v.expr(self.n_expr, null)
2457 var args = [recv]
2458 for a in self.raw_arguments.as(not null) do
2459 args.add(v.expr(a, null))
2460 end
2461 return v.compile_callsite(self.callsite.as(not null), args)
2462 end
2463 end
2464
2465 redef class ASendReassignFormExpr
2466 redef fun stmt(v)
2467 do
2468 var recv = v.expr(self.n_expr, null)
2469 var args = [recv]
2470 for a in self.raw_arguments.as(not null) do
2471 args.add(v.expr(a, null))
2472 end
2473 var value = v.expr(self.n_value, null)
2474
2475 var left = v.compile_callsite(self.callsite.as(not null), args)
2476 assert left != null
2477
2478 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2479 assert res != null
2480
2481 args.add(res)
2482 v.compile_callsite(self.write_callsite.as(not null), args)
2483 end
2484 end
2485
2486 redef class ASuperExpr
2487 redef fun expr(v)
2488 do
2489 var recv = v.frame.arguments.first
2490 var args = [recv]
2491 for a in self.n_args.n_exprs do
2492 args.add(v.expr(a, null))
2493 end
2494
2495 var callsite = self.callsite
2496 if callsite != null then
2497 # Add additionnals arguments for the super init call
2498 if args.length == 1 then
2499 for i in [0..callsite.mproperty.intro.msignature.arity[ do
2500 args.add(v.frame.arguments[i+1])
2501 end
2502 end
2503 # Super init call
2504 var res = v.compile_callsite(callsite, args)
2505 return res
2506 end
2507
2508 if args.length == 1 then
2509 args = v.frame.arguments
2510 end
2511
2512 # stantard call-next-method
2513 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2514 end
2515 end
2516
2517 redef class ANewExpr
2518 redef fun expr(v)
2519 do
2520 var mtype = self.mtype.as(MClassType)
2521 var recv
2522 var ctype = mtype.ctype
2523 if ctype == "val*" then
2524 recv = v.init_instance(mtype)
2525 else if ctype == "void*" then
2526 recv = v.new_expr("NULL/*special!*/", mtype)
2527 else
2528 debug("cannot new {mtype}")
2529 abort
2530 end
2531 var args = [recv]
2532 for a in self.n_args.n_exprs do
2533 args.add(v.expr(a, null))
2534 end
2535 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2536 if res2 != null then
2537 #self.debug("got {res2} from {mproperty}. drop {recv}")
2538 return res2
2539 end
2540 return recv
2541 end
2542 end
2543
2544 redef class AAttrExpr
2545 redef fun expr(v)
2546 do
2547 var recv = v.expr(self.n_expr, null)
2548 var mproperty = self.mproperty.as(not null)
2549 return v.read_attribute(mproperty, recv)
2550 end
2551 end
2552
2553 redef class AAttrAssignExpr
2554 redef fun stmt(v)
2555 do
2556 var recv = v.expr(self.n_expr, null)
2557 var i = v.expr(self.n_value, null)
2558 var mproperty = self.mproperty.as(not null)
2559 v.write_attribute(mproperty, recv, i)
2560 end
2561 end
2562
2563 redef class AAttrReassignExpr
2564 redef fun stmt(v)
2565 do
2566 var recv = v.expr(self.n_expr, null)
2567 var value = v.expr(self.n_value, null)
2568 var mproperty = self.mproperty.as(not null)
2569 var attr = v.read_attribute(mproperty, recv)
2570 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2571 assert res != null
2572 v.write_attribute(mproperty, recv, res)
2573 end
2574 end
2575
2576 redef class AIssetAttrExpr
2577 redef fun expr(v)
2578 do
2579 var recv = v.expr(self.n_expr, null)
2580 var mproperty = self.mproperty.as(not null)
2581 return v.isset_attribute(mproperty, recv)
2582 end
2583 end
2584
2585 redef class ADebugTypeExpr
2586 redef fun stmt(v)
2587 do
2588 # do nothing
2589 end
2590 end
2591
2592 # Utils
2593
2594 redef class Array[E]
2595 # Return a new `Array` with the elements only contened in self and not in `o`
2596 fun -(o: Array[E]): Array[E] do
2597 var res = new Array[E]
2598 for e in self do if not o.has(e) then res.add(e)
2599 return res
2600 end
2601 end
2602
2603 redef class MModule
2604 # All `MProperty` associated to all `MClassDef` of `mclass`
2605 fun properties(mclass: MClass): Set[MProperty] do
2606 if not self.properties_cache.has_key(mclass) then
2607 var properties = new HashSet[MProperty]
2608 var parents = new Array[MClass]
2609 if self.flatten_mclass_hierarchy.has(mclass) then
2610 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2611 end
2612 for parent in parents do
2613 properties.add_all(self.properties(parent))
2614 end
2615 for mclassdef in mclass.mclassdefs do
2616 for mprop in mclassdef.intro_mproperties do
2617 properties.add(mprop)
2618 end
2619 end
2620 self.properties_cache[mclass] = properties
2621 end
2622 return properties_cache[mclass]
2623 end
2624 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2625 end
2626
2627 redef class AModule
2628 # Does this module use the legacy native interface?
2629 fun uses_legacy_ni: Bool is abstract
2630
2631 # Write FFI results to file
2632 fun finalize_ffi(v: AbstractCompilerVisitor, modelbuilder: ModelBuilder) is abstract
2633
2634 # Write nitni results to file
2635 fun finalize_nitni(v: AbstractCompilerVisitor) is abstract
2636 end