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