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