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