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