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