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