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