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