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