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