4e61ecfa14de913de2efee8df2188cd5566e19bf
[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 if not toolctx.opt_no_stacktrace.value then self.header.add_decl("#define UNW_LOCAL_ONLY")
370 self.header.add_decl("#include <stdlib.h>")
371 self.header.add_decl("#include <stdio.h>")
372 self.header.add_decl("#include <string.h>")
373 if toolctx.opt_stacktrace.value then
374 self.header.add_decl("#include \"c_functions_hash.h\"")
375 end
376 self.header.add_decl("#include <signal.h>")
377 if not toolctx.opt_no_stacktrace.value then
378 self.header.add_decl("#include <libunwind.h>")
379 end
380 self.header.add_decl("#include <gc_chooser.h>")
381
382 compile_header_structs
383
384 # Signal handler function prototype
385 if not toolctx.opt_no_stacktrace.value then self.header.add_decl("void show_backtrace(int);")
386
387 # Global variable used by the legacy native interface
388 self.header.add_decl("extern int glob_argc;")
389 self.header.add_decl("extern char **glob_argv;")
390 self.header.add_decl("extern val *glob_sys;")
391 end
392
393 # Declaration of structures the live Nit types
394 protected fun compile_header_structs is abstract
395
396 # Generate the main C function.
397 # This function:
398 # * allocate the Sys object if it exists
399 # * call init if is exists
400 # * call main if it exists
401 fun compile_main_function
402 do
403 var v = self.new_visitor
404 v.add_decl("int glob_argc;")
405 v.add_decl("char **glob_argv;")
406 v.add_decl("val *glob_sys;")
407
408 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
409 for tag in count_type_test_tags do
410 v.add_decl("long count_type_test_resolved_{tag};")
411 v.add_decl("long count_type_test_unresolved_{tag};")
412 v.add_decl("long count_type_test_skipped_{tag};")
413 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
414 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
415 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
416 end
417 end
418
419 v.add_decl("void show_backtrace (int signo) \{")
420 if not modelbuilder.toolcontext.opt_no_stacktrace.value then
421 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
422 v.add_decl("unw_cursor_t cursor;")
423 v.add_decl("if(opt==NULL)\{")
424 v.add_decl("unw_context_t uc;")
425 v.add_decl("unw_word_t ip;")
426 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
427 v.add_decl("unw_getcontext(&uc);")
428 v.add_decl("unw_init_local(&cursor, &uc);")
429 v.add_decl("printf(\"-------------------------------------------------\\n\");")
430 v.add_decl("printf(\"-- Stack Trace ------------------------------\\n\");")
431 v.add_decl("printf(\"-------------------------------------------------\\n\");")
432 v.add_decl("while (unw_step(&cursor) > 0) \{")
433 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
434 if modelbuilder.toolcontext.opt_stacktrace.value then
435 v.add_decl(" const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));")
436 v.add_decl(" if (recv != 0)\{")
437 v.add_decl(" printf(\"` %s\\n\", recv->nit_name);")
438 v.add_decl(" \}else\{")
439 v.add_decl(" printf(\"` %s\\n\", procname);")
440 v.add_decl(" \}")
441 else
442 v.add_decl(" printf(\"` %s \\n\",procname);")
443 end
444 v.add_decl("\}")
445 v.add_decl("printf(\"-------------------------------------------------\\n\");")
446 v.add_decl("free(procname);")
447 v.add_decl("\}")
448 end
449 v.add_decl("exit(signo);")
450 v.add_decl("\}")
451
452 v.add_decl("int main(int argc, char** argv) \{")
453
454 v.add("signal(SIGABRT, show_backtrace);")
455 v.add("signal(SIGFPE, show_backtrace);")
456 v.add("signal(SIGILL, show_backtrace);")
457 v.add("signal(SIGINT, show_backtrace);")
458 v.add("signal(SIGTERM, show_backtrace);")
459 v.add("signal(SIGSEGV, show_backtrace);")
460
461 v.add("glob_argc = argc; glob_argv = argv;")
462 v.add("initialize_gc_option();")
463 var main_type = mainmodule.sys_type
464 if main_type != null then
465 var mainmodule = v.compiler.mainmodule
466 var glob_sys = v.init_instance(main_type)
467 v.add("glob_sys = {glob_sys};")
468 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
469 if main_init != null then
470 v.send(main_init, [glob_sys])
471 end
472 var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass)
473 if main_method != null then
474 v.send(main_method, [glob_sys])
475 end
476 end
477
478 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
479 v.add_decl("long count_type_test_resolved_total = 0;")
480 v.add_decl("long count_type_test_unresolved_total = 0;")
481 v.add_decl("long count_type_test_skipped_total = 0;")
482 v.add_decl("long count_type_test_total_total = 0;")
483 for tag in count_type_test_tags do
484 v.add_decl("long count_type_test_total_{tag};")
485 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
486 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
487 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
488 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
489 v.add("count_type_test_total_total += count_type_test_total_{tag};")
490 end
491 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
492 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
493 var tags = count_type_test_tags.to_a
494 tags.add("total")
495 for tag in tags do
496 v.add("printf(\"{tag}\");")
497 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
498 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
499 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
500 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
501 end
502 end
503
504 v.add("return 0;")
505 v.add("\}")
506 end
507
508 # List of additional .c files required to compile (native interface)
509 var extern_bodies = new Array[ExternCFile]
510
511 # This is used to avoid adding an extern file more than once
512 private var seen_extern = new ArraySet[String]
513
514 # Generate code that check if an instance is correctly initialized
515 fun generate_check_init_instance(mtype: MClassType) is abstract
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 # Attributes handling
791
792 # Generate a polymorphic attribute is_set test
793 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
794
795 # Generate a polymorphic attribute read
796 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
797
798 # Generate a polymorphic attribute write
799 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
800
801 # Checks
802
803 # Add a check and an abort for a null reciever if needed
804 fun check_recv_notnull(recv: RuntimeVariable)
805 do
806 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
807
808 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
809 if maybenull then
810 self.add("if ({recv} == NULL) \{")
811 self.add_abort("Reciever is null")
812 self.add("\}")
813 end
814 end
815
816 # Generate a check-init-instance
817 fun check_init_instance(recv: RuntimeVariable, mtype: MClassType) is abstract
818
819 # Names handling
820
821 private var names: HashSet[String] = new HashSet[String]
822 private var last: Int = 0
823
824 # Return a new name based on `s` and unique in the visitor
825 fun get_name(s: String): String
826 do
827 if not self.names.has(s) then
828 self.names.add(s)
829 return s
830 end
831 var i = self.last + 1
832 loop
833 var s2 = s + i.to_s
834 if not self.names.has(s2) then
835 self.last = i
836 self.names.add(s2)
837 return s2
838 end
839 i = i + 1
840 end
841 end
842
843 # Return an unique and stable identifier associated with an escapemark
844 fun escapemark_name(e: nullable EscapeMark): String
845 do
846 assert e != null
847 if escapemark_names.has_key(e) then return escapemark_names[e]
848 var name = e.name
849 if name == null then name = "label"
850 name = get_name(name)
851 escapemark_names[e] = name
852 return name
853 end
854
855 private var escapemark_names = new HashMap[EscapeMark, String]
856
857 # Return a "const char*" variable associated to the classname of the dynamic type of an object
858 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
859 fun class_name_string(value: RuntimeVariable): String is abstract
860
861 # Variables handling
862
863 protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
864
865 # Return the local runtime_variable associated to a Nit local variable
866 fun variable(variable: Variable): RuntimeVariable
867 do
868 if self.variables.has_key(variable) then
869 return self.variables[variable]
870 else
871 var name = self.get_name("var_{variable.name}")
872 var mtype = variable.declared_type.as(not null)
873 mtype = self.anchor(mtype)
874 var res = new RuntimeVariable(name, mtype, mtype)
875 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
876 self.variables[variable] = res
877 return res
878 end
879 end
880
881 # Return a new uninitialized local runtime_variable
882 fun new_var(mtype: MType): RuntimeVariable
883 do
884 mtype = self.anchor(mtype)
885 var name = self.get_name("var")
886 var res = new RuntimeVariable(name, mtype, mtype)
887 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
888 return res
889 end
890
891 # Return a new uninitialized named runtime_variable
892 fun new_named_var(mtype: MType, name: String): RuntimeVariable
893 do
894 mtype = self.anchor(mtype)
895 var res = new RuntimeVariable(name, mtype, mtype)
896 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
897 return res
898 end
899
900 # Correctly assign a left and a right value
901 # Boxing and unboxing is performed if required
902 fun assign(left, right: RuntimeVariable)
903 do
904 right = self.autobox(right, left.mtype)
905 self.add("{left} = {right};")
906 end
907
908 # Generate instances
909
910 # Generate a alloc-instance + init-attributes
911 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
912
913 # Generate an integer value
914 fun int_instance(value: Int): RuntimeVariable
915 do
916 var res = self.new_var(self.get_class("Int").mclass_type)
917 self.add("{res} = {value};")
918 return res
919 end
920
921 # Generate a string value
922 fun string_instance(string: String): RuntimeVariable
923 do
924 var mtype = self.get_class("String").mclass_type
925 var name = self.get_name("varonce")
926 self.add_decl("static {mtype.ctype} {name};")
927 var res = self.new_var(mtype)
928 self.add("if ({name}) \{")
929 self.add("{res} = {name};")
930 self.add("\} else \{")
931 var native_mtype = self.get_class("NativeString").mclass_type
932 var nat = self.new_var(native_mtype)
933 self.add("{nat} = \"{string.escape_to_c}\";")
934 var length = self.int_instance(string.length)
935 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
936 self.add("{name} = {res};")
937 self.add("\}")
938 return res
939 end
940
941 # Generate an array value
942 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
943
944 # Get an instance of a array for a vararg
945 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
946
947 # Code generation
948
949 # Add a line in the main part of the generated C
950 fun add(s: String) do self.writer.lines.add(s)
951
952 # Add a line in the
953 # (used for local or global declaration)
954 fun add_decl(s: String) do self.writer.decl_lines.add(s)
955
956 # Request the presence of a global declaration
957 fun require_declaration(key: String)
958 do
959 self.writer.file.required_declarations.add(key)
960 end
961
962 # Add a declaration in the local-header
963 # The declaration is ensured to be present once
964 fun declare_once(s: String)
965 do
966 self.compiler.provide_declaration(s, s)
967 self.require_declaration(s)
968 end
969
970 # look for a needed .h and .c file for a given .nit source-file
971 # FIXME: bad API, parameter should be a `MModule`, not its source-file
972 fun add_extern(file: String)
973 do
974 file = file.strip_extension(".nit")
975 var tryfile = file + ".nit.h"
976 if tryfile.file_exists then
977 self.declare_once("#include \"{"..".join_path(tryfile)}\"")
978 end
979 tryfile = file + "_nit.h"
980 if tryfile.file_exists then
981 self.declare_once("#include \"{"..".join_path(tryfile)}\"")
982 end
983
984 if self.compiler.seen_extern.has(file) then return
985 self.compiler.seen_extern.add(file)
986 tryfile = file + ".nit.c"
987 if not tryfile.file_exists then
988 tryfile = file + "_nit.c"
989 if not tryfile.file_exists then return
990 end
991 var f = new ExternCFile(tryfile, "")
992 self.compiler.extern_bodies.add(f)
993 end
994
995 # Return a new local runtime_variable initialized with the C expression `cexpr`.
996 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
997 do
998 var res = new_var(mtype)
999 self.add("{res} = {cexpr};")
1000 return res
1001 end
1002
1003 # Generate generic abort
1004 # used by aborts, asserts, casts, etc.
1005 fun add_abort(message: String)
1006 do
1007 self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");")
1008 add_raw_abort
1009 end
1010
1011 fun add_raw_abort
1012 do
1013 if self.current_node != null and self.current_node.location.file != null then
1014 self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1015 else
1016 self.add("fprintf(stderr, \"\\n\");")
1017 end
1018 self.add("show_backtrace(1);")
1019 end
1020
1021 # Add a dynamic cast
1022 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1023 do
1024 var res = self.type_test(value, mtype, tag)
1025 self.add("if (!{res}) \{")
1026 var cn = self.class_name_string(value)
1027 self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1028 self.add_raw_abort
1029 self.add("\}")
1030 end
1031
1032 # Generate a return with the value `s`
1033 fun ret(s: RuntimeVariable)
1034 do
1035 self.assign(self.frame.returnvar.as(not null), s)
1036 self.add("goto {self.frame.returnlabel.as(not null)};")
1037 end
1038
1039 # Compile a statement (if any)
1040 fun stmt(nexpr: nullable AExpr)
1041 do
1042 if nexpr == null then return
1043 var old = self.current_node
1044 self.current_node = nexpr
1045 nexpr.stmt(self)
1046 self.current_node = old
1047 end
1048
1049 # Compile an expression an return its result
1050 # `mtype` is the expected return type, pass null if no specific type is expected.
1051 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1052 do
1053 var old = self.current_node
1054 self.current_node = nexpr
1055 var res = nexpr.expr(self).as(not null)
1056 if mtype != null then
1057 mtype = self.anchor(mtype)
1058 res = self.autobox(res, mtype)
1059 end
1060 res = autoadapt(res, nexpr.mtype.as(not null))
1061 var implicit_cast_to = nexpr.implicit_cast_to
1062 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1063 add_cast(res, implicit_cast_to, "auto")
1064 res = autoadapt(res, implicit_cast_to)
1065 end
1066 self.current_node = old
1067 return res
1068 end
1069
1070 # Alias for `self.expr(nexpr, self.bool_type)`
1071 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1072
1073 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1074 fun debug(message: String)
1075 do
1076 var node = self.current_node
1077 if node == null then
1078 print "?: {message}"
1079 else
1080 node.debug(message)
1081 end
1082 self.add("/* DEBUG: {message} */")
1083 end
1084 end
1085
1086 # A C function associated to a Nit method
1087 # Because of customization, a given Nit method can be compiler more that once
1088 abstract class AbstractRuntimeFunction
1089
1090 type COMPILER: AbstractCompiler
1091 type VISITOR: AbstractCompilerVisitor
1092
1093 # The associated Nit method
1094 var mmethoddef: MMethodDef
1095
1096 # The mangled c name of the runtime_function
1097 # Subclasses should redefine `build_c_name` instead
1098 fun c_name: String
1099 do
1100 var res = self.c_name_cache
1101 if res != null then return res
1102 res = self.build_c_name
1103 self.c_name_cache = res
1104 return res
1105 end
1106
1107 # Non cached version of `c_name`
1108 protected fun build_c_name: String is abstract
1109
1110 protected var c_name_cache: nullable String writable = null
1111
1112 # Implements a call of the runtime_function
1113 # May inline the body or generate a C function call
1114 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1115
1116 # Generate the code for the `AbstractRuntimeFunction`
1117 # Warning: compile more than once compilation makes CC unhappy
1118 fun compile_to_c(compiler: COMPILER) is abstract
1119 end
1120
1121 # A runtime variable hold a runtime value in C.
1122 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1123 #
1124 # 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.
1125 class RuntimeVariable
1126 # The name of the variable in the C code
1127 var name: String
1128
1129 # The static type of the variable (as declard in C)
1130 var mtype: MType
1131
1132 # The current casted type of the variable (as known in Nit)
1133 var mcasttype: MType writable
1134
1135 # If the variable exaclty a mcasttype?
1136 # false (usual value) means that the variable is a mcasttype or a subtype.
1137 var is_exact: Bool writable = false
1138
1139 init(name: String, mtype: MType, mcasttype: MType)
1140 do
1141 self.name = name
1142 self.mtype = mtype
1143 self.mcasttype = mcasttype
1144 assert not mtype.need_anchor
1145 assert not mcasttype.need_anchor
1146 end
1147
1148 redef fun to_s do return name
1149
1150 redef fun inspect
1151 do
1152 var exact_str
1153 if self.is_exact then
1154 exact_str = " exact"
1155 else
1156 exact_str = ""
1157 end
1158 var type_str
1159 if self.mtype == self.mcasttype then
1160 type_str = "{mtype}{exact_str}"
1161 else
1162 type_str = "{mtype}({mcasttype}{exact_str})"
1163 end
1164 return "<{name}:{type_str}>"
1165 end
1166 end
1167
1168 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1169 class Frame
1170
1171 type VISITOR: AbstractCompilerVisitor
1172
1173 # The associated visitor
1174 var visitor: VISITOR
1175
1176 # The executed property.
1177 # A Method in case of a call, an attribute in case of a default initialization.
1178 var mpropdef: MPropDef
1179
1180 # The static type of the receiver
1181 var receiver: MClassType
1182
1183 # Arguments of the method (the first is the receiver)
1184 var arguments: Array[RuntimeVariable]
1185
1186 # The runtime_variable associated to the return (in a function)
1187 var returnvar: nullable RuntimeVariable writable = null
1188
1189 # The label at the end of the property
1190 var returnlabel: nullable String writable = null
1191 end
1192
1193 # An extern C file to compile
1194 class ExternCFile
1195 # The filename of the file
1196 var filename: String
1197 # Additionnal specific CC compiler -c flags
1198 var cflags: String
1199 end
1200
1201 redef class MType
1202 # Return the C type associated to a given Nit static type
1203 fun ctype: String do return "val*"
1204
1205 fun ctypename: String do return "val"
1206
1207 # Return the name of the C structure associated to a Nit live type
1208 fun c_name: String is abstract
1209 protected var c_name_cache: nullable String protected writable
1210 end
1211
1212 redef class MClassType
1213 redef fun c_name
1214 do
1215 var res = self.c_name_cache
1216 if res != null then return res
1217 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1218 self.c_name_cache = res
1219 return res
1220 end
1221
1222 redef fun ctype: String
1223 do
1224 if mclass.name == "Int" then
1225 return "long"
1226 else if mclass.name == "Bool" then
1227 return "short int"
1228 else if mclass.name == "Char" then
1229 return "char"
1230 else if mclass.name == "Float" then
1231 return "double"
1232 else if mclass.name == "NativeString" then
1233 return "char*"
1234 else if mclass.name == "NativeArray" then
1235 return "val*"
1236 else if mclass.kind == extern_kind then
1237 return "void*"
1238 else
1239 return "val*"
1240 end
1241 end
1242
1243 redef fun ctypename: String
1244 do
1245 if mclass.name == "Int" then
1246 return "l"
1247 else if mclass.name == "Bool" then
1248 return "s"
1249 else if mclass.name == "Char" then
1250 return "c"
1251 else if mclass.name == "Float" then
1252 return "d"
1253 else if mclass.name == "NativeString" then
1254 return "str"
1255 else if mclass.name == "NativeArray" then
1256 #return "{self.arguments.first.ctype}*"
1257 return "val"
1258 else if mclass.kind == extern_kind then
1259 return "ptr"
1260 else
1261 return "val"
1262 end
1263 end
1264 end
1265
1266 redef class MGenericType
1267 redef fun c_name
1268 do
1269 var res = self.c_name_cache
1270 if res != null then return res
1271 res = super
1272 for t in self.arguments do
1273 res = res + t.c_name
1274 end
1275 self.c_name_cache = res
1276 return res
1277 end
1278 end
1279
1280 redef class MParameterType
1281 redef fun c_name
1282 do
1283 var res = self.c_name_cache
1284 if res != null then return res
1285 res = "{self.mclass.c_name}_FT{self.rank}"
1286 self.c_name_cache = res
1287 return res
1288 end
1289 end
1290
1291 redef class MVirtualType
1292 redef fun c_name
1293 do
1294 var res = self.c_name_cache
1295 if res != null then return res
1296 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1297 self.c_name_cache = res
1298 return res
1299 end
1300 end
1301
1302 redef class MNullableType
1303 redef fun c_name
1304 do
1305 var res = self.c_name_cache
1306 if res != null then return res
1307 res = "nullable_{self.mtype.c_name}"
1308 self.c_name_cache = res
1309 return res
1310 end
1311 end
1312
1313 redef class MClass
1314 # Return the name of the C structure associated to a Nit class
1315 fun c_name: String do
1316 var res = self.c_name_cache
1317 if res != null then return res
1318 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1319 self.c_name_cache = res
1320 return res
1321 end
1322 private var c_name_cache: nullable String
1323 end
1324
1325 redef class MProperty
1326 fun c_name: String do
1327 var res = self.c_name_cache
1328 if res != null then return res
1329 res = "{self.intro.c_name}"
1330 self.c_name_cache = res
1331 return res
1332 end
1333 private var c_name_cache: nullable String
1334 end
1335
1336 redef class MPropDef
1337 type VISITOR: AbstractCompilerVisitor
1338
1339 private var c_name_cache: nullable String
1340
1341 # The mangled name associated to the property
1342 fun c_name: String
1343 do
1344 var res = self.c_name_cache
1345 if res != null then return res
1346 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1347 self.c_name_cache = res
1348 return res
1349 end
1350 end
1351
1352 redef class MMethodDef
1353 # Can the body be inlined?
1354 fun can_inline(v: VISITOR): Bool
1355 do
1356 var modelbuilder = v.compiler.modelbuilder
1357 if modelbuilder.mpropdef2npropdef.has_key(self) then
1358 var npropdef = modelbuilder.mpropdef2npropdef[self]
1359 return npropdef.can_inline
1360 else if self.mproperty.name == "init" then
1361 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1362 return true
1363 else
1364 abort
1365 end
1366 end
1367
1368 # Inline the body in another visitor
1369 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1370 do
1371 var modelbuilder = v.compiler.modelbuilder
1372 if modelbuilder.mpropdef2npropdef.has_key(self) then
1373 var npropdef = modelbuilder.mpropdef2npropdef[self]
1374 var oldnode = v.current_node
1375 v.current_node = npropdef
1376 self.compile_parameter_check(v, arguments)
1377 npropdef.compile_to_c(v, self, arguments)
1378 v.current_node = oldnode
1379 else if self.mproperty.name == "init" then
1380 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1381 var oldnode = v.current_node
1382 v.current_node = nclassdef
1383 self.compile_parameter_check(v, arguments)
1384 nclassdef.compile_to_c(v, self, arguments)
1385 v.current_node = oldnode
1386 else
1387 abort
1388 end
1389 return null
1390 end
1391
1392 # Generate type checks in the C code to check covariant parameters
1393 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1394 do
1395 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1396
1397 for i in [0..msignature.arity[ do
1398 # skip test for vararg since the array is instantiated with the correct polymorphic type
1399 if msignature.vararg_rank == i then continue
1400
1401 # skip if the cast is not required
1402 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1403 if not origmtype.need_anchor then continue
1404
1405 # get the parameter type
1406 var mtype = self.msignature.mparameters[i].mtype
1407
1408 # generate the cast
1409 # note that v decides if and how to implements the cast
1410 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1411 v.add_cast(arguments[i+1], mtype, "covariance")
1412 end
1413 end
1414 end
1415
1416 # Node visit
1417
1418 redef class APropdef
1419 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1420 do
1421 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1422 debug("Not yet implemented")
1423 end
1424
1425 fun can_inline: Bool do return true
1426 end
1427
1428 redef class AConcreteMethPropdef
1429 redef fun compile_to_c(v, mpropdef, arguments)
1430 do
1431 for i in [0..mpropdef.msignature.arity[ do
1432 var variable = self.n_signature.n_params[i].variable.as(not null)
1433 v.assign(v.variable(variable), arguments[i+1])
1434 end
1435 # Call the implicit super-init
1436 var auto_super_inits = self.auto_super_inits
1437 if auto_super_inits != null then
1438 var selfarg = [arguments.first]
1439 for auto_super_init in auto_super_inits do
1440 if auto_super_init.intro.msignature.arity == 0 then
1441 v.send(auto_super_init, selfarg)
1442 else
1443 v.send(auto_super_init, arguments)
1444 end
1445 end
1446 end
1447 v.stmt(self.n_block)
1448 end
1449
1450 redef fun can_inline
1451 do
1452 if self.auto_super_inits != null then return false
1453 var nblock = self.n_block
1454 if nblock == null then return true
1455 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1456 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1457 return false
1458 end
1459 end
1460
1461 redef class AInternMethPropdef
1462 redef fun compile_to_c(v, mpropdef, arguments)
1463 do
1464 var pname = mpropdef.mproperty.name
1465 var cname = mpropdef.mclassdef.mclass.name
1466 var ret = mpropdef.msignature.return_mtype
1467 if ret != null then
1468 ret = v.resolve_for(ret, arguments.first)
1469 end
1470 if pname != "==" and pname != "!=" then
1471 v.adapt_signature(mpropdef, arguments)
1472 end
1473 if cname == "Int" then
1474 if pname == "output" then
1475 v.add("printf(\"%ld\\n\", {arguments.first});")
1476 return
1477 else if pname == "object_id" then
1478 v.ret(arguments.first)
1479 return
1480 else if pname == "+" then
1481 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1482 return
1483 else if pname == "-" then
1484 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1485 return
1486 else if pname == "unary -" then
1487 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1488 return
1489 else if pname == "succ" then
1490 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1491 return
1492 else if pname == "prec" then
1493 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1494 return
1495 else if pname == "*" then
1496 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1497 return
1498 else if pname == "/" then
1499 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1500 return
1501 else if pname == "%" then
1502 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1503 return
1504 else if pname == "lshift" then
1505 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1506 return
1507 else if pname == "rshift" then
1508 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1509 return
1510 else if pname == "==" then
1511 v.ret(v.equal_test(arguments[0], arguments[1]))
1512 return
1513 else if pname == "!=" then
1514 var res = v.equal_test(arguments[0], arguments[1])
1515 v.ret(v.new_expr("!{res}", ret.as(not null)))
1516 return
1517 else if pname == "<" then
1518 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1519 return
1520 else if pname == ">" then
1521 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1522 return
1523 else if pname == "<=" then
1524 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1525 return
1526 else if pname == ">=" then
1527 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1528 return
1529 else if pname == "to_f" then
1530 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1531 return
1532 else if pname == "ascii" then
1533 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1534 return
1535 end
1536 else if cname == "Char" then
1537 if pname == "output" then
1538 v.add("printf(\"%c\", {arguments.first});")
1539 return
1540 else if pname == "object_id" then
1541 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
1547 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1548 return
1549 else if pname == "==" then
1550 v.ret(v.equal_test(arguments[0], arguments[1]))
1551 return
1552 else if pname == "!=" then
1553 var res = v.equal_test(arguments[0], arguments[1])
1554 v.ret(v.new_expr("!{res}", ret.as(not null)))
1555 return
1556 else if pname == "succ" then
1557 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1558 return
1559 else if pname == "prec" then
1560 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1561 return
1562 else if pname == "<" then
1563 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1564 return
1565 else if pname == ">" then
1566 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1567 return
1568 else if pname == "<=" then
1569 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1570 return
1571 else if pname == ">=" then
1572 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1573 return
1574 else if pname == "to_i" then
1575 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1576 return
1577 else if pname == "ascii" then
1578 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1579 return
1580 end
1581 else if cname == "Bool" then
1582 if pname == "output" then
1583 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1584 return
1585 else if pname == "object_id" then
1586 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1587 return
1588 else if pname == "==" then
1589 v.ret(v.equal_test(arguments[0], arguments[1]))
1590 return
1591 else if pname == "!=" then
1592 var res = v.equal_test(arguments[0], arguments[1])
1593 v.ret(v.new_expr("!{res}", ret.as(not null)))
1594 return
1595 end
1596 else if cname == "Float" then
1597 if pname == "output" then
1598 v.add("printf(\"%f\\n\", {arguments.first});")
1599 return
1600 else if pname == "object_id" then
1601 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1602 return
1603 else if pname == "+" then
1604 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1605 return
1606 else if pname == "-" then
1607 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1608 return
1609 else if pname == "unary -" then
1610 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1611 return
1612 else if pname == "succ" then
1613 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1614 return
1615 else if pname == "prec" then
1616 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1617 return
1618 else if pname == "*" then
1619 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1620 return
1621 else if pname == "/" then
1622 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1623 return
1624 else if pname == "==" then
1625 v.ret(v.equal_test(arguments[0], arguments[1]))
1626 return
1627 else if pname == "!=" then
1628 var res = v.equal_test(arguments[0], arguments[1])
1629 v.ret(v.new_expr("!{res}", ret.as(not null)))
1630 return
1631 else if pname == "<" then
1632 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1633 return
1634 else if pname == ">" then
1635 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1636 return
1637 else if pname == "<=" then
1638 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1639 return
1640 else if pname == ">=" then
1641 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1642 return
1643 else if pname == "to_i" then
1644 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1645 return
1646 end
1647 else if cname == "NativeString" then
1648 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.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1653 return
1654 else if pname == "copy_to" then
1655 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1656 return
1657 else if pname == "atoi" then
1658 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1659 return
1660 end
1661 else if cname == "NativeArray" then
1662 v.native_array_def(pname, ret, arguments)
1663 return
1664 end
1665 if pname == "exit" then
1666 v.add("exit({arguments[1]});")
1667 return
1668 else if pname == "sys" then
1669 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1670 return
1671 else if pname == "calloc_string" then
1672 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1673 return
1674 else if pname == "calloc_array" then
1675 v.calloc_array(ret.as(not null), arguments)
1676 return
1677 else if pname == "object_id" then
1678 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1679 return
1680 else if pname == "is_same_type" then
1681 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1682 return
1683 else if pname == "is_same_instance" then
1684 v.ret(v.equal_test(arguments[0], arguments[1]))
1685 return
1686 else if pname == "output_class_name" then
1687 var nat = v.class_name_string(arguments.first)
1688 v.add("printf(\"%s\\n\", {nat});")
1689 return
1690 else if pname == "native_class_name" then
1691 var nat = v.class_name_string(arguments.first)
1692 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1693 return
1694 else if pname == "force_garbage_collection" then
1695 v.add("nit_gcollect();")
1696 return
1697 else if pname == "native_argc" then
1698 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1699 return
1700 else if pname == "native_argv" then
1701 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1702 return
1703 end
1704 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1705 debug("Not implemented {mpropdef}")
1706 end
1707 end
1708
1709 redef class AExternMethPropdef
1710 redef fun compile_to_c(v, mpropdef, arguments)
1711 do
1712 var externname
1713 var nextern = self.n_extern
1714 if nextern == null then
1715 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1716 v.add("show_backtrace(1);")
1717 return
1718 end
1719 externname = nextern.text.substring(1, nextern.text.length-2)
1720 if location.file != null then
1721 var file = location.file.filename
1722 v.add_extern(file)
1723 end
1724 var res: nullable RuntimeVariable = null
1725 var ret = mpropdef.msignature.return_mtype
1726 if ret != null then
1727 ret = v.resolve_for(ret, arguments.first)
1728 res = v.new_var(ret)
1729 end
1730 v.adapt_signature(mpropdef, arguments)
1731
1732 if res == null then
1733 v.add("{externname}({arguments.join(", ")});")
1734 else
1735 v.add("{res} = {externname}({arguments.join(", ")});")
1736 v.ret(res)
1737 end
1738 end
1739 end
1740
1741 redef class AExternInitPropdef
1742 redef fun compile_to_c(v, mpropdef, arguments)
1743 do
1744 var externname
1745 var nextern = self.n_extern
1746 if nextern == null then
1747 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1748 v.add("show_backtrace(1);")
1749 return
1750 end
1751 externname = nextern.text.substring(1, nextern.text.length-2)
1752 if location.file != null then
1753 var file = location.file.filename
1754 v.add_extern(file)
1755 end
1756 v.adapt_signature(mpropdef, arguments)
1757 var ret = arguments.first.mtype
1758 var res = v.new_var(ret)
1759
1760 arguments.shift
1761
1762 v.add("{res} = {externname}({arguments.join(", ")});")
1763 v.ret(res)
1764 end
1765 end
1766
1767 redef class AAttrPropdef
1768 redef fun compile_to_c(v, mpropdef, arguments)
1769 do
1770 if arguments.length == 1 then
1771 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1772 v.assign(v.frame.returnvar.as(not null), res)
1773 else
1774 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1775 end
1776 end
1777
1778 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1779 do
1780 var nexpr = self.n_expr
1781 if nexpr != null then
1782 var oldnode = v.current_node
1783 v.current_node = self
1784 var old_frame = v.frame
1785 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1786 v.frame = frame
1787 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1788 v.write_attribute(self.mpropdef.mproperty, recv, value)
1789 v.frame = old_frame
1790 v.current_node = oldnode
1791 end
1792 end
1793
1794 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1795 do
1796 var nexpr = self.n_expr
1797 if nexpr != null then return
1798
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 # Force read to check the initialization
1805 v.read_attribute(self.mpropdef.mproperty, recv)
1806 v.frame = old_frame
1807 v.current_node = oldnode
1808 end
1809 end
1810
1811 redef class AClassdef
1812 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1813 do
1814 if mpropdef == self.mfree_init then
1815 var super_inits = self.super_inits
1816 if super_inits != null then
1817 assert arguments.length == 1
1818 for su in super_inits do
1819 v.send(su, arguments)
1820 end
1821 return
1822 end
1823 var recv = arguments.first
1824 var i = 1
1825 # Collect undefined attributes
1826 for npropdef in self.n_propdefs do
1827 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
1828 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
1829 i += 1
1830 end
1831 end
1832 else
1833 abort
1834 end
1835 end
1836 end
1837
1838 redef class ADeferredMethPropdef
1839 redef fun compile_to_c(v, mpropdef, arguments) do
1840 var cn = v.class_name_string(arguments.first)
1841 v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1842 v.add_raw_abort
1843 end
1844 redef fun can_inline do return true
1845 end
1846
1847 redef class AExpr
1848 # Try to compile self as an expression
1849 # Do not call this method directly, use `v.expr` instead
1850 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
1851 do
1852 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
1853 var mtype = self.mtype
1854 if mtype == null then
1855 return null
1856 else
1857 var res = v.new_var(mtype)
1858 v.add("/* {res} = NOT YET {class_name} */")
1859 return res
1860 end
1861 end
1862
1863 # Try to compile self as a statement
1864 # Do not call this method directly, use `v.stmt` instead
1865 private fun stmt(v: AbstractCompilerVisitor)
1866 do
1867 var res = expr(v)
1868 if res != null then v.add("{res};")
1869 end
1870 end
1871
1872 redef class ABlockExpr
1873 redef fun stmt(v)
1874 do
1875 for e in self.n_expr do v.stmt(e)
1876 end
1877 redef fun expr(v)
1878 do
1879 var last = self.n_expr.last
1880 for e in self.n_expr do
1881 if e == last then break
1882 v.stmt(e)
1883 end
1884 return v.expr(last, null)
1885 end
1886 end
1887
1888 redef class AVardeclExpr
1889 redef fun stmt(v)
1890 do
1891 var variable = self.variable.as(not null)
1892 var ne = self.n_expr
1893 if ne != null then
1894 var i = v.expr(ne, variable.declared_type)
1895 v.assign(v.variable(variable), i)
1896 end
1897 end
1898 end
1899
1900 redef class AVarExpr
1901 redef fun expr(v)
1902 do
1903 var res = v.variable(self.variable.as(not null))
1904 var mtype = self.mtype.as(not null)
1905 return v.autoadapt(res, mtype)
1906 end
1907 end
1908
1909 redef class AVarAssignExpr
1910 redef fun stmt(v)
1911 do
1912 var variable = self.variable.as(not null)
1913 var i = v.expr(self.n_value, variable.declared_type)
1914 v.assign(v.variable(variable), i)
1915 end
1916 redef fun expr(v)
1917 do
1918 var variable = self.variable.as(not null)
1919 var i = v.expr(self.n_value, variable.declared_type)
1920 v.assign(v.variable(variable), i)
1921 return i
1922 end
1923 end
1924
1925 redef class AVarReassignExpr
1926 redef fun stmt(v)
1927 do
1928 var variable = self.variable.as(not null)
1929 var vari = v.variable(variable)
1930 var value = v.expr(self.n_value, variable.declared_type)
1931 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
1932 assert res != null
1933 v.assign(v.variable(variable), res)
1934 end
1935 end
1936
1937 redef class ASelfExpr
1938 redef fun expr(v) do return v.frame.arguments.first
1939 end
1940
1941 redef class AContinueExpr
1942 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
1943 end
1944
1945 redef class ABreakExpr
1946 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
1947 end
1948
1949 redef class AReturnExpr
1950 redef fun stmt(v)
1951 do
1952 var nexpr = self.n_expr
1953 if nexpr != null then
1954 var returnvar = v.frame.returnvar.as(not null)
1955 var i = v.expr(nexpr, returnvar.mtype)
1956 v.assign(returnvar, i)
1957 end
1958 v.add("goto {v.frame.returnlabel.as(not null)};")
1959 end
1960 end
1961
1962 redef class AAbortExpr
1963 redef fun stmt(v) do v.add_abort("Aborted")
1964 end
1965
1966 redef class AIfExpr
1967 redef fun stmt(v)
1968 do
1969 var cond = v.expr_bool(self.n_expr)
1970 v.add("if ({cond})\{")
1971 v.stmt(self.n_then)
1972 v.add("\} else \{")
1973 v.stmt(self.n_else)
1974 v.add("\}")
1975 end
1976
1977 redef fun expr(v)
1978 do
1979 var res = v.new_var(self.mtype.as(not null))
1980 var cond = v.expr_bool(self.n_expr)
1981 v.add("if ({cond})\{")
1982 v.assign(res, v.expr(self.n_then.as(not null), null))
1983 v.add("\} else \{")
1984 v.assign(res, v.expr(self.n_else.as(not null), null))
1985 v.add("\}")
1986 return res
1987 end
1988 end
1989
1990 redef class AIfexprExpr
1991 redef fun expr(v)
1992 do
1993 var res = v.new_var(self.mtype.as(not null))
1994 var cond = v.expr_bool(self.n_expr)
1995 v.add("if ({cond})\{")
1996 v.assign(res, v.expr(self.n_then, null))
1997 v.add("\} else \{")
1998 v.assign(res, v.expr(self.n_else, null))
1999 v.add("\}")
2000 return res
2001 end
2002 end
2003
2004 redef class ADoExpr
2005 redef fun stmt(v)
2006 do
2007 v.stmt(self.n_block)
2008 var escapemark = self.escapemark
2009 if escapemark != null then
2010 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2011 end
2012 end
2013 end
2014
2015 redef class AWhileExpr
2016 redef fun stmt(v)
2017 do
2018 v.add("for(;;) \{")
2019 var cond = v.expr_bool(self.n_expr)
2020 v.add("if (!{cond}) break;")
2021 v.stmt(self.n_block)
2022 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2023 v.add("\}")
2024 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2025 end
2026 end
2027
2028 redef class ALoopExpr
2029 redef fun stmt(v)
2030 do
2031 v.add("for(;;) \{")
2032 v.stmt(self.n_block)
2033 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2034 v.add("\}")
2035 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2036 end
2037 end
2038
2039 redef class AForExpr
2040 redef fun stmt(v)
2041 do
2042 # Shortcut on explicit range
2043 # Avoid the instantiation of the range and the iterator
2044 var nexpr = self.n_expr
2045 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2046 var from = v.expr(nexpr.n_expr, null)
2047 var to = v.expr(nexpr.n_expr2, null)
2048 var variable = v.variable(variables.first)
2049
2050 v.assign(variable, from)
2051 v.add("for(;;) \{ /* shortcut range */")
2052
2053 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2054 assert ok != null
2055 v.add("if(!{ok}) break;")
2056
2057 v.stmt(self.n_block)
2058
2059 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2060 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2061 assert succ != null
2062 v.assign(variable, succ)
2063 v.add("\}")
2064 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2065 return
2066 end
2067
2068 var cl = v.expr(self.n_expr, null)
2069 var it_meth = self.method_iterator
2070 assert it_meth != null
2071 var it = v.send(it_meth, [cl])
2072 assert it != null
2073 v.add("for(;;) \{")
2074 var isok_meth = self.method_is_ok
2075 assert isok_meth != null
2076 var ok = v.send(isok_meth, [it])
2077 assert ok != null
2078 v.add("if(!{ok}) break;")
2079 if self.variables.length == 1 then
2080 var item_meth = self.method_item
2081 assert item_meth != null
2082 var i = v.send(item_meth, [it])
2083 assert i != null
2084 v.assign(v.variable(variables.first), i)
2085 else if self.variables.length == 2 then
2086 var key_meth = self.method_key
2087 assert key_meth != null
2088 var i = v.send(key_meth, [it])
2089 assert i != null
2090 v.assign(v.variable(variables[0]), i)
2091 var item_meth = self.method_item
2092 assert item_meth != null
2093 i = v.send(item_meth, [it])
2094 assert i != null
2095 v.assign(v.variable(variables[1]), i)
2096 else
2097 abort
2098 end
2099 v.stmt(self.n_block)
2100 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2101 var next_meth = self.method_next
2102 assert next_meth != null
2103 v.send(next_meth, [it])
2104 v.add("\}")
2105 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2106 end
2107 end
2108
2109 redef class AAssertExpr
2110 redef fun stmt(v)
2111 do
2112 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2113
2114 var cond = v.expr_bool(self.n_expr)
2115 v.add("if (!{cond}) \{")
2116 v.stmt(self.n_else)
2117 var nid = self.n_id
2118 if nid != null then
2119 v.add_abort("Assert '{nid.text}' failed")
2120 else
2121 v.add_abort("Assert failed")
2122 end
2123 v.add("\}")
2124 end
2125 end
2126
2127 redef class AOrExpr
2128 redef fun expr(v)
2129 do
2130 var res = v.new_var(self.mtype.as(not null))
2131 var i1 = v.expr_bool(self.n_expr)
2132 v.add("if ({i1}) \{")
2133 v.add("{res} = 1;")
2134 v.add("\} else \{")
2135 var i2 = v.expr_bool(self.n_expr2)
2136 v.add("{res} = {i2};")
2137 v.add("\}")
2138 return res
2139 end
2140 end
2141
2142 redef class AImpliesExpr
2143 redef fun expr(v)
2144 do
2145 var res = v.new_var(self.mtype.as(not null))
2146 var i1 = v.expr_bool(self.n_expr)
2147 v.add("if (!{i1}) \{")
2148 v.add("{res} = 1;")
2149 v.add("\} else \{")
2150 var i2 = v.expr_bool(self.n_expr2)
2151 v.add("{res} = {i2};")
2152 v.add("\}")
2153 return res
2154 end
2155 end
2156
2157 redef class AAndExpr
2158 redef fun expr(v)
2159 do
2160 var res = v.new_var(self.mtype.as(not null))
2161 var i1 = v.expr_bool(self.n_expr)
2162 v.add("if (!{i1}) \{")
2163 v.add("{res} = 0;")
2164 v.add("\} else \{")
2165 var i2 = v.expr_bool(self.n_expr2)
2166 v.add("{res} = {i2};")
2167 v.add("\}")
2168 return res
2169 end
2170 end
2171
2172 redef class ANotExpr
2173 redef fun expr(v)
2174 do
2175 var cond = v.expr_bool(self.n_expr)
2176 return v.new_expr("!{cond}", self.mtype.as(not null))
2177 end
2178 end
2179
2180 redef class AOrElseExpr
2181 redef fun expr(v)
2182 do
2183 var res = v.new_var(self.mtype.as(not null))
2184 var i1 = v.expr(self.n_expr, null)
2185 v.add("if ({i1}!=NULL) \{")
2186 v.assign(res, i1)
2187 v.add("\} else \{")
2188 var i2 = v.expr(self.n_expr2, null)
2189 v.assign(res, i2)
2190 v.add("\}")
2191 return res
2192 end
2193 end
2194
2195 redef class AIntExpr
2196 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2197 end
2198
2199 redef class AFloatExpr
2200 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2201 end
2202
2203 redef class ACharExpr
2204 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2205 end
2206
2207 redef class AArrayExpr
2208 redef fun expr(v)
2209 do
2210 var mtype = self.mtype.as(MClassType).arguments.first
2211 var array = new Array[RuntimeVariable]
2212 for nexpr in self.n_exprs.n_exprs do
2213 var i = v.expr(nexpr, mtype)
2214 array.add(i)
2215 end
2216 return v.array_instance(array, mtype)
2217 end
2218 end
2219
2220 redef class AStringFormExpr
2221 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2222 end
2223
2224 redef class ASuperstringExpr
2225 redef fun expr(v)
2226 do
2227 var array = new Array[RuntimeVariable]
2228 for ne in self.n_exprs do
2229 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2230 var i = v.expr(ne, null)
2231 array.add(i)
2232 end
2233 var a = v.array_instance(array, v.object_type)
2234 var res = v.send(v.get_property("to_s", a.mtype), [a])
2235 return res
2236 end
2237 end
2238
2239 redef class ACrangeExpr
2240 redef fun expr(v)
2241 do
2242 var i1 = v.expr(self.n_expr, null)
2243 var i2 = v.expr(self.n_expr2, null)
2244 var mtype = self.mtype.as(MClassType)
2245 var res = v.init_instance(mtype)
2246 var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
2247 v.check_init_instance(res, mtype)
2248 return res
2249 end
2250 end
2251
2252 redef class AOrangeExpr
2253 redef fun expr(v)
2254 do
2255 var i1 = v.expr(self.n_expr, null)
2256 var i2 = v.expr(self.n_expr2, null)
2257 var mtype = self.mtype.as(MClassType)
2258 var res = v.init_instance(mtype)
2259 var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
2260 v.check_init_instance(res, mtype)
2261 return res
2262 end
2263 end
2264
2265 redef class ATrueExpr
2266 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2267 end
2268
2269 redef class AFalseExpr
2270 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2271 end
2272
2273 redef class ANullExpr
2274 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2275 end
2276
2277 redef class AIsaExpr
2278 redef fun expr(v)
2279 do
2280 var i = v.expr(self.n_expr, null)
2281 return v.type_test(i, self.cast_type.as(not null), "isa")
2282 end
2283 end
2284
2285 redef class AAsCastExpr
2286 redef fun expr(v)
2287 do
2288 var i = v.expr(self.n_expr, null)
2289 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2290
2291 v.add_cast(i, self.mtype.as(not null), "as")
2292 return i
2293 end
2294 end
2295
2296 redef class AAsNotnullExpr
2297 redef fun expr(v)
2298 do
2299 var i = v.expr(self.n_expr, null)
2300 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2301
2302 v.add("if ({i} == NULL) \{")
2303 v.add_abort("Cast failed")
2304 v.add("\}")
2305 return i
2306 end
2307 end
2308
2309 redef class AParExpr
2310 redef fun expr(v) do return v.expr(self.n_expr, null)
2311 end
2312
2313 redef class AOnceExpr
2314 redef fun expr(v)
2315 do
2316 var mtype = self.mtype.as(not null)
2317 var name = v.get_name("varonce")
2318 var guard = v.get_name(name + "_guard")
2319 v.add_decl("static {mtype.ctype} {name};")
2320 v.add_decl("static int {guard};")
2321 var res = v.new_var(mtype)
2322 v.add("if ({guard}) \{")
2323 v.add("{res} = {name};")
2324 v.add("\} else \{")
2325 var i = v.expr(self.n_expr, mtype)
2326 v.add("{res} = {i};")
2327 v.add("{name} = {res};")
2328 v.add("{guard} = 1;")
2329 v.add("\}")
2330 return res
2331 end
2332 end
2333
2334 redef class ASendExpr
2335 redef fun expr(v)
2336 do
2337 var recv = v.expr(self.n_expr, null)
2338 var args = [recv]
2339 for a in self.raw_arguments.as(not null) do
2340 args.add(v.expr(a, null))
2341 end
2342 return v.compile_callsite(self.callsite.as(not null), args)
2343 end
2344 end
2345
2346 redef class ASendReassignFormExpr
2347 redef fun stmt(v)
2348 do
2349 var recv = v.expr(self.n_expr, null)
2350 var args = [recv]
2351 for a in self.raw_arguments.as(not null) do
2352 args.add(v.expr(a, null))
2353 end
2354 var value = v.expr(self.n_value, null)
2355
2356 var left = v.compile_callsite(self.callsite.as(not null), args)
2357 assert left != null
2358
2359 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2360 assert res != null
2361
2362 args.add(res)
2363 v.compile_callsite(self.write_callsite.as(not null), args)
2364 end
2365 end
2366
2367 redef class ASuperExpr
2368 redef fun expr(v)
2369 do
2370 var recv = v.frame.arguments.first
2371 var args = [recv]
2372 for a in self.n_args.n_exprs do
2373 args.add(v.expr(a, null))
2374 end
2375 if args.length == 1 then
2376 args = v.frame.arguments
2377 end
2378
2379 var mproperty = self.mproperty
2380 if mproperty != null then
2381 if mproperty.intro.msignature.arity == 0 then
2382 args = [recv]
2383 end
2384 # Super init call
2385 var res = v.send(mproperty, args)
2386 return res
2387 end
2388
2389 # stantard call-next-method
2390 return v.supercall(v.frame.mpropdef.as(MMethodDef), recv.mtype.as(MClassType), args)
2391 end
2392 end
2393
2394 redef class ANewExpr
2395 redef fun expr(v)
2396 do
2397 var mtype = self.mtype.as(MClassType)
2398 var recv
2399 var ctype = mtype.ctype
2400 if ctype == "val*" then
2401 recv = v.init_instance(mtype)
2402 else if ctype == "void*" then
2403 recv = v.new_expr("NULL/*special!*/", mtype)
2404 else
2405 debug("cannot new {mtype}")
2406 abort
2407 end
2408 var args = [recv]
2409 for a in self.n_args.n_exprs do
2410 args.add(v.expr(a, null))
2411 end
2412 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2413 if res2 != null then
2414 #self.debug("got {res2} from {mproperty}. drop {recv}")
2415 return res2
2416 end
2417 v.check_init_instance(recv, mtype)
2418 return recv
2419 end
2420 end
2421
2422 redef class AAttrExpr
2423 redef fun expr(v)
2424 do
2425 var recv = v.expr(self.n_expr, null)
2426 var mproperty = self.mproperty.as(not null)
2427 return v.read_attribute(mproperty, recv)
2428 end
2429 end
2430
2431 redef class AAttrAssignExpr
2432 redef fun stmt(v)
2433 do
2434 var recv = v.expr(self.n_expr, null)
2435 var i = v.expr(self.n_value, null)
2436 var mproperty = self.mproperty.as(not null)
2437 v.write_attribute(mproperty, recv, i)
2438 end
2439 end
2440
2441 redef class AAttrReassignExpr
2442 redef fun stmt(v)
2443 do
2444 var recv = v.expr(self.n_expr, null)
2445 var value = v.expr(self.n_value, null)
2446 var mproperty = self.mproperty.as(not null)
2447 var attr = v.read_attribute(mproperty, recv)
2448 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2449 assert res != null
2450 v.write_attribute(mproperty, recv, res)
2451 end
2452 end
2453
2454 redef class AIssetAttrExpr
2455 redef fun expr(v)
2456 do
2457 var recv = v.expr(self.n_expr, null)
2458 var mproperty = self.mproperty.as(not null)
2459 return v.isset_attribute(mproperty, recv)
2460 end
2461 end
2462
2463 redef class ADebugTypeExpr
2464 redef fun stmt(v)
2465 do
2466 # do nothing
2467 end
2468 end
2469
2470 # Utils
2471
2472 redef class Array[E]
2473 # Return a new `Array` with the elements only contened in self and not in `o`
2474 fun -(o: Array[E]): Array[E] do
2475 var res = new Array[E]
2476 for e in self do if not o.has(e) then res.add(e)
2477 return res
2478 end
2479 end
2480
2481 redef class MModule
2482 # All `MProperty` associated to all `MClassDef` of `mclass`
2483 fun properties(mclass: MClass): Set[MProperty] do
2484 if not self.properties_cache.has_key(mclass) then
2485 var properties = new HashSet[MProperty]
2486 var parents = new Array[MClass]
2487 if self.flatten_mclass_hierarchy.has(mclass) then
2488 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2489 end
2490 for parent in parents do
2491 properties.add_all(self.properties(parent))
2492 end
2493 for mclassdef in mclass.mclassdefs do
2494 for mprop in mclassdef.intro_mproperties do
2495 properties.add(mprop)
2496 end
2497 end
2498 self.properties_cache[mclass] = properties
2499 end
2500 return properties_cache[mclass]
2501 end
2502 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2503 end