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