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