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