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