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