nitg: add option --typing-test-metrics
[nit.git] / src / global_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 # Global compilation of a Nit program
18 #
19 # Techniques used are:
20 # * heterogeneous generics
21 # * customization
22 # * switch dispatch
23 # * inlining
24 module global_compiler
25
26 import literal
27 import typing
28 import auto_super_init
29 import rapid_type_analysis
30
31 redef class ToolContext
32 # --output
33 var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
34
35 # --no-cc
36 var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
37
38 # --make-flags
39 var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
40
41 # --hardening
42 var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
43
44 # --no-shortcut-range
45 var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
46
47 # --no-check-covariance
48 var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
49
50 # --no-check-initialization
51 var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization")
52
53 # --no-check-assert
54 var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
55
56 # --no-check-autocast
57 var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
58
59 # --no-check-other
60 var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
61
62 # --typing-test-metrics
63 var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
64
65 redef init
66 do
67 super
68 self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_hardening, self.opt_no_shortcut_range)
69 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)
70 self.option_context.add_option(self.opt_typing_test_metrics)
71 end
72 end
73
74 redef class ModelBuilder
75 # Entry point to performs a global compilation on the AST of a complete program.
76 # `mainmodule` is the main module of the program
77 # `runtime_type_analysis` is a already computer type analysis.
78 fun run_global_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis)
79 do
80 var time0 = get_time
81 self.toolcontext.info("*** COMPILING TO C ***", 1)
82
83 var compiler = new GlobalCompiler(mainmodule, runtime_type_analysis, self)
84 compiler.compile_header
85 var v = compiler.header
86
87 for t in runtime_type_analysis.live_types do
88 compiler.declare_runtimeclass(v, t)
89 end
90
91 compiler.compile_class_names
92
93 # Init instance code (allocate and init-arguments)
94 for t in runtime_type_analysis.live_types do
95 if t.ctype == "val*" then
96 compiler.generate_init_instance(t)
97 compiler.generate_check_init_instance(t)
98 else
99 compiler.generate_box_instance(t)
100 end
101 end
102
103 # The main function of the C
104 compiler.compile_main_function
105
106 # Compile until all runtime_functions are visited
107 while not compiler.todos.is_empty do
108 var m = compiler.todos.shift
109 self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
110 m.compile_to_c(compiler)
111 end
112 self.toolcontext.info("Total methods to compile to C: {compiler.visitors.length}", 2)
113
114 compiler.display_stats
115
116 var time1 = get_time
117 self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2)
118 write_and_make(compiler)
119 end
120
121 protected fun write_and_make(compiler: GlobalCompiler)
122 do
123 var mainmodule = compiler.mainmodule
124
125 # Generate the .h and .c files
126 # A single C file regroups many compiled rumtime functions
127 # 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
128 var time0 = get_time
129
130 var outname = self.toolcontext.opt_output.value
131 if outname == null then
132 outname = "{mainmodule.name}.bin"
133 end
134
135 var hfilename = ".nit_compile/{mainmodule.name}.1.h"
136 var h = new OFStream.open(hfilename)
137 for l in compiler.header.decl_lines do
138 h.write l
139 h.write "\n"
140 end
141 h.close
142
143 var cfiles = new Array[String]
144
145 var file: nullable OFStream = null
146 var count = 0
147
148 ".nit_compile".mkdir
149 var i = 0
150 for vis in compiler.visitors do
151 count += vis.lines.length
152 if file == null or count > 10000 then
153 i += 1
154 if file != null then file.close
155 var cfilename = ".nit_compile/{mainmodule.name}.{i}.c"
156 cfiles.add(cfilename)
157 file = new OFStream.open(cfilename)
158 file.write "#include \"{mainmodule.name}.1.h\"\n"
159 count = vis.lines.length
160 end
161 if vis != compiler.header then
162 for l in vis.decl_lines do
163 file.write l
164 file.write "\n"
165 end
166 end
167 for l in vis.lines do
168 file.write l
169 file.write "\n"
170 end
171 end
172 if file != null then file.close
173
174 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
175
176 # Generate the Makefile
177
178 var makename = ".nit_compile/{mainmodule.name}.mk"
179 var makefile = new OFStream.open(makename)
180
181 makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc\n\n")
182 makefile.write("all: {outname}\n\n")
183
184 var ofiles = new Array[String]
185 # Compile each generated file
186 for f in cfiles do
187 var o = f.strip_extension(".c") + ".o"
188 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
189 ofiles.add(o)
190 end
191 # Compile each required extern body into a specific .o
192 for f in compiler.extern_bodies do
193 i += 1
194 var o = ".nit_compile/{mainmodule.name}.{i}.o"
195 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
196 ofiles.add(o)
197 end
198 # Link edition
199 makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n")
200 # Clean
201 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
202 makefile.close
203 self.toolcontext.info("Generated makefile: {makename}", 2)
204
205 var time1 = get_time
206 self.toolcontext.info("*** END COMPILING TO C: {time1-time0} ***", 2)
207
208 # Execute the Makefile
209
210 if self.toolcontext.opt_no_cc.value then return
211
212 time0 = time1
213 self.toolcontext.info("*** COMPILING C ***", 1)
214 var makeflags = self.toolcontext.opt_make_flags.value
215 if makeflags == null then makeflags = ""
216 self.toolcontext.info("make -B -f {makename} -j 4 {makeflags}", 2)
217
218 var res
219 if self.toolcontext.verbose_level >= 3 then
220 res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1")
221 else
222 res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
223 end
224 if res != 0 then
225 toolcontext.error(null, "make failed! Error code: {res}.")
226 end
227
228 time1 = get_time
229 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
230 end
231 end
232
233 # Singleton that store the knowledge about the compilation process
234 class GlobalCompiler
235 # The main module of the program
236 var mainmodule: MModule writable
237
238 # The result of the RTA (used to know live types and methods)
239 var runtime_type_analysis: RapidTypeAnalysis
240
241 # The modeulbuilder used to know the model and the AST
242 var modelbuilder: ModelBuilder
243
244 # Is hardening asked? (see --hardening)
245 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
246
247 init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, modelbuilder: ModelBuilder)
248 do
249 self.header = new_visitor
250 self.mainmodule = mainmodule
251 self.runtime_type_analysis = runtime_type_analysis
252 self.modelbuilder = modelbuilder
253 self.live_primitive_types = new Array[MClassType]
254 for t in runtime_type_analysis.live_types do
255 if t.ctype != "val*" then
256 self.live_primitive_types.add(t)
257 end
258 end
259 end
260
261 fun compile_header do
262 var v = self.header
263 self.header.add_decl("#include <stdlib.h>")
264 self.header.add_decl("#include <stdio.h>")
265 self.header.add_decl("#include <string.h>")
266 self.header.add_decl("#ifndef NOBOEHM")
267 self.header.add_decl("#include <gc/gc.h>")
268 self.header.add_decl("#ifdef NOBOEHM_ATOMIC")
269 self.header.add_decl("#undef GC_MALLOC_ATOMIC")
270 self.header.add_decl("#define GC_MALLOC_ATOMIC(x) GC_MALLOC(x)")
271 self.header.add_decl("#endif /*NOBOEHM_ATOMIC*/")
272 self.header.add_decl("#else /*NOBOEHM*/")
273 self.header.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
274 self.header.add_decl("#define GC_MALLOC_ATOMIC(x) calloc(1, (x))")
275 self.header.add_decl("#endif /*NOBOEHM*/")
276
277 compile_header_structs
278
279 # Global variable used by the legacy native interface
280 self.header.add_decl("extern int glob_argc;")
281 self.header.add_decl("extern char **glob_argv;")
282 self.header.add_decl("extern val *glob_sys;")
283 end
284
285 # Class names (for the class_name and output_class_name methods)
286 protected fun compile_class_names do
287 self.header.add_decl("extern const char const * class_names[];")
288 self.header.add("const char const * class_names[] = \{")
289 for t in self.runtime_type_analysis.live_types do
290 self.header.add("\"{t}\", /* {self.classid(t)} */")
291 end
292 self.header.add("\};")
293 end
294
295 # Declaration of structures the live Nit types
296 # Each live type is generated as an independent C `struct' type.
297 # They only share a common first field `classid` used to implement the polymorphism.
298 # Usualy, all C variables that refers to a Nit object are typed on the abstract struct `val' that contains only the `classid` field.
299 protected fun compile_header_structs do
300 self.header.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
301 end
302
303 # Subset of runtime_type_analysis.live_types that contains only primitive types
304 # Used to implement the equal test
305 var live_primitive_types: Array[MClassType]
306
307 # runtime_functions that need to be compiled
308 private var todos: List[AbstractRuntimeFunction] = new List[AbstractRuntimeFunction]
309
310 # runtime_functions already seen (todo or done)
311 private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
312 fun todo(m: AbstractRuntimeFunction)
313 do
314 if seen.has(m) then return
315 todos.add(m)
316 seen.add(m)
317 end
318
319 # Where global declaration are stored (the main .h)
320 #
321 # FIXME: should not be a vistor but just somewhere to store lines
322 # FIXME: should not have a global .h since it does not help recompilations
323 var header: GlobalCompilerVisitor writable
324
325 # The list of all associated visitors
326 # Used to generate .c files
327 # FIXME: should not be vistors but just somewhere to store lines
328 private var visitors: List[GlobalCompilerVisitor] = new List[GlobalCompilerVisitor]
329
330 # List of additional .c files required to compile (native interface)
331 var extern_bodies = new ArraySet[String]
332
333 # Return the C symbol associated to a live type runtime
334 # REQUIRE: self.runtime_type_analysis.live_types.has(mtype)
335 fun classid(mtype: MClassType): String
336 do
337 if self.classids.has_key(mtype) then
338 return self.classids[mtype]
339 end
340 print "No classid for {mtype}"
341 abort
342 end
343
344 # Cache for classid (computed by declare_runtimeclass)
345 protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
346
347 # Declare C structures and identifiers for a runtime class
348 fun declare_runtimeclass(v: GlobalCompilerVisitor, mtype: MClassType)
349 do
350 assert self.runtime_type_analysis.live_types.has(mtype)
351 v.add_decl("/* runtime class {mtype} */")
352 var idnum = classids.length
353 var idname = "ID_" + mtype.c_name
354 self.classids[mtype] = idname
355 v.add_decl("#define {idname} {idnum} /* {mtype} */")
356
357 v.add_decl("struct {mtype.c_name} \{")
358 v.add_decl("int classid; /* must be {idname} */")
359
360 if mtype.mclass.name == "NativeArray" then
361 # NativeArrays are just a instance header followed by an array of values
362 v.add_decl("{mtype.arguments.first.ctype} values[1];")
363 end
364
365 if mtype.ctype != "val*" then
366 # Is the Nit type is native then the struct is a box with two fields:
367 # * the `classid` to be polymorph
368 # * the `value` that contains the native value.
369 v.add_decl("{mtype.ctype} value;")
370 end
371
372 # Collect all attributes and associate them a field in the structure.
373 # Note: we do not try to optimize the order and helps CC to optimize the client code.
374 for cd in mtype.collect_mclassdefs(self.mainmodule) do
375 for p in cd.intro_mproperties do
376 if not p isa MAttribute then continue
377 var t = p.intro.static_mtype.as(not null)
378 t = t.anchor_to(self.mainmodule, mtype)
379 v.add_decl("{t.ctype} {p.intro.c_name}; /* {p}: {t} */")
380 end
381 end
382 v.add_decl("\};")
383 end
384
385 # Generate the init-instance of a live type (allocate + init-instance)
386 fun generate_init_instance(mtype: MClassType)
387 do
388 assert self.runtime_type_analysis.live_types.has(mtype)
389 assert mtype.ctype == "val*"
390 var v = self.new_visitor
391
392 var is_native_array = mtype.mclass.name == "NativeArray"
393
394 var sig
395 if is_native_array then
396 sig = "int length"
397 else
398 sig = "void"
399 end
400
401 self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});")
402 v.add_decl("/* allocate {mtype} */")
403 v.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig}) \{")
404 var res = v.new_var(mtype)
405 res.is_exact = true
406 if is_native_array then
407 var mtype_elt = mtype.arguments.first
408 v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
409 else
410 v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
411 end
412 v.add("{res}->classid = {self.classid(mtype)};")
413
414 self.generate_init_attr(v, res, mtype)
415 v.add("return {res};")
416 v.add("\}")
417 end
418
419 fun generate_check_init_instance(mtype: MClassType)
420 do
421 if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return
422
423 var v = self.new_visitor
424 var res = new RuntimeVariable("self", mtype, mtype)
425 self.header.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype});")
426 v.add_decl("/* allocate {mtype} */")
427 v.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype} {res}) \{")
428 self.generate_check_attr(v, res, mtype)
429 v.add("\}")
430 end
431
432 # Generate code that collect initialize the attributes on a new instance
433 fun generate_init_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
434 do
435 for cd in mtype.collect_mclassdefs(self.mainmodule)
436 do
437 var n = self.modelbuilder.mclassdef2nclassdef[cd]
438 for npropdef in n.n_propdefs do
439 if npropdef isa AAttrPropdef then
440 npropdef.init_expr(v, recv)
441 end
442 end
443 end
444 end
445
446 # Generate a check-init-instance
447 fun generate_check_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
448 do
449 for cd in mtype.collect_mclassdefs(self.mainmodule)
450 do
451 var n = self.modelbuilder.mclassdef2nclassdef[cd]
452 for npropdef in n.n_propdefs do
453 if npropdef isa AAttrPropdef then
454 npropdef.check_expr(v, recv)
455 end
456 end
457 end
458 end
459
460 fun generate_box_instance(mtype: MClassType)
461 do
462 assert self.runtime_type_analysis.live_types.has(mtype)
463 assert mtype.ctype != "val*"
464 var v = self.new_visitor
465
466 self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
467 v.add_decl("/* allocate {mtype} */")
468 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
469 v.add("struct {mtype.c_name}*res = GC_MALLOC(sizeof(struct {mtype.c_name}));")
470 v.add("res->classid = {self.classid(mtype)};")
471 v.add("res->value = value;")
472 v.add("return (val*)res;")
473 v.add("\}")
474
475 end
476
477 # look for a needed .h and .c file for a given .nit source-file
478 # FIXME: bad API, parameter should be a MModule, not its source-file
479 fun add_extern(file: String)
480 do
481 file = file.strip_extension(".nit")
482 var tryfile = file + ".nit.h"
483 if tryfile.file_exists then
484 self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
485 end
486 tryfile = file + "_nit.h"
487 if tryfile.file_exists then
488 self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
489 end
490 tryfile = file + ".nit.c"
491 if tryfile.file_exists then
492 self.extern_bodies.add(tryfile)
493 end
494 tryfile = file + "_nit.c"
495 if tryfile.file_exists then
496 self.extern_bodies.add(tryfile)
497 end
498 end
499
500 # Initialize a visitor specific for a compiler engine
501 fun new_visitor: GlobalCompilerVisitor do return new GlobalCompilerVisitor(self)
502
503 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
504 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
505 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
506 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
507
508 private fun init_count_type_test_tags: HashMap[String, Int]
509 do
510 var res = new HashMap[String, Int]
511 for tag in count_type_test_tags do
512 res[tag] = 0
513 end
514 return res
515 end
516
517 # Generate the main C function.
518 # This function:
519 # allocate the Sys object if it exists
520 # call init if is exists
521 # call main if it exists
522 fun compile_main_function
523 do
524 var v = self.new_visitor
525 v.add_decl("int glob_argc;")
526 v.add_decl("char **glob_argv;")
527 v.add_decl("val *glob_sys;")
528
529 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
530 for tag in count_type_test_tags do
531 v.add_decl("long count_type_test_resolved_{tag};")
532 v.add_decl("long count_type_test_unresolved_{tag};")
533 v.add_decl("long count_type_test_skipped_{tag};")
534 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
535 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
536 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
537 end
538 end
539 v.add_decl("int main(int argc, char** argv) \{")
540 v.add("glob_argc = argc; glob_argv = argv;")
541 var main_type = mainmodule.sys_type
542 if main_type != null then
543 var mainmodule = v.compiler.mainmodule
544 var glob_sys = v.init_instance(main_type)
545 v.add("glob_sys = {glob_sys};")
546 var main_init = mainmodule.try_get_primitive_method("init", main_type)
547 if main_init != null then
548 v.send(main_init, [glob_sys])
549 end
550 var main_method = mainmodule.try_get_primitive_method("main", main_type)
551 if main_method != null then
552 v.send(main_method, [glob_sys])
553 end
554 end
555
556 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
557 v.add_decl("long count_type_test_resolved_total = 0;")
558 v.add_decl("long count_type_test_unresolved_total = 0;")
559 v.add_decl("long count_type_test_skipped_total = 0;")
560 v.add_decl("long count_type_test_total_total = 0;")
561 for tag in count_type_test_tags do
562 v.add_decl("long count_type_test_total_{tag};")
563 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
564 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
565 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
566 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
567 v.add("count_type_test_total_total += count_type_test_total_{tag};")
568 end
569 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
570 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
571 var tags = count_type_test_tags.to_a
572 tags.add("total")
573 for tag in tags do
574 v.add("printf(\"{tag}\");")
575 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
576 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
577 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
578 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
579 end
580 end
581 v.add("return 0;")
582 v.add("\}")
583
584 end
585
586 fun div(a,b:Int):String
587 do
588 if b == 0 then return "n/a"
589 return ((a*10000/b).to_f / 100.0).to_precision(2)
590 end
591
592 fun display_stats
593 do
594 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
595 print "# static count_type_test"
596 print "\tresolved:\tunresolved\tskipped\ttotal"
597 var count_type_test_total = init_count_type_test_tags
598 count_type_test_resolved["total"] = 0
599 count_type_test_unresolved["total"] = 0
600 count_type_test_skipped["total"] = 0
601 count_type_test_total["total"] = 0
602 for tag in count_type_test_tags do
603 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
604 count_type_test_resolved["total"] += count_type_test_resolved[tag]
605 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
606 count_type_test_skipped["total"] += count_type_test_skipped[tag]
607 count_type_test_total["total"] += count_type_test_total[tag]
608 end
609 var count_type_test = count_type_test_total["total"]
610 var tags = count_type_test_tags.to_a
611 tags.add("total")
612 for tag in tags do
613 printn tag
614 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
615 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
616 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
617 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
618 print ""
619 end
620 end
621 end
622
623 private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
624 end
625
626 redef class String
627 # Mangle a string to be a unique valid C identifier
628 fun to_cmangle: String
629 do
630 var res = new Buffer
631 var underscore = false
632 for c in self do
633 if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
634 res.add(c)
635 underscore = false
636 continue
637 end
638 if underscore then
639 res.append('_'.ascii.to_s)
640 res.add('d')
641 end
642 if c >= '0' and c <= '9' then
643 res.add(c)
644 underscore = false
645 else if c == '_' then
646 res.add(c)
647 underscore = true
648 else
649 res.add('_')
650 res.append(c.ascii.to_s)
651 res.add('d')
652 underscore = false
653 end
654 end
655 return res.to_s
656 end
657
658 # Escape " \ ' and non printable characters for literal C strings or characters
659 fun escape_to_c: String
660 do
661 var b = new Buffer
662 for c in self do
663 if c == '\n' then
664 b.append("\\n")
665 else if c == '\0' then
666 b.append("\\0")
667 else if c == '"' then
668 b.append("\\\"")
669 else if c == '\'' then
670 b.append("\\\'")
671 else if c == '\\' then
672 b.append("\\\\")
673 else if c.ascii < 32 then
674 b.append("\\{c.ascii.to_base(8, false)}")
675 else
676 b.add(c)
677 end
678 end
679 return b.to_s
680 end
681 end
682
683 redef class MType
684 # Return the C type associated to a given Nit static type
685 fun ctype: String
686 do
687 return "val*"
688 end
689
690 fun ctypename: String
691 do
692 return "val"
693 end
694
695 # Return the name of the C structure associated to a Nit live type
696 # FIXME: move to GlobalCompiler so we can check that self is a live type
697 fun c_name: String is abstract
698 protected var c_name_cache: nullable String protected writable
699 end
700
701 redef class MClassType
702 redef fun c_name
703 do
704 var res = self.c_name_cache
705 if res != null then return res
706 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
707 self.c_name_cache = res
708 return res
709 end
710
711 redef fun ctype: String
712 do
713 if mclass.name == "Int" then
714 return "long"
715 else if mclass.name == "Bool" then
716 return "short int"
717 else if mclass.name == "Char" then
718 return "char"
719 else if mclass.name == "Float" then
720 return "double"
721 else if mclass.name == "NativeString" then
722 return "char*"
723 else if mclass.name == "NativeArray" then
724 #return "{self.arguments.first.ctype}*"
725 return "val*"
726 else if mclass.kind == extern_kind then
727 return "void*"
728 else
729 return "val*"
730 end
731 end
732
733 redef fun ctypename: String
734 do
735 if mclass.name == "Int" then
736 return "l"
737 else if mclass.name == "Bool" then
738 return "s"
739 else if mclass.name == "Char" then
740 return "c"
741 else if mclass.name == "Float" then
742 return "d"
743 else if mclass.name == "NativeString" then
744 return "str"
745 else if mclass.name == "NativeArray" then
746 #return "{self.arguments.first.ctype}*"
747 return "val"
748 else if mclass.kind == extern_kind then
749 return "ptr"
750 else
751 return "val"
752 end
753 end
754 end
755
756 redef class MGenericType
757 redef fun c_name
758 do
759 var res = self.c_name_cache
760 if res != null then return res
761 res = super
762 for t in self.arguments do
763 res = res + t.c_name
764 end
765 self.c_name_cache = res
766 return res
767 end
768 end
769
770 # A C function associated to a Nit method
771 # Because of customization, a given Nit method can be compiler more that once
772 abstract class AbstractRuntimeFunction
773 # The associated Nit method
774 var mmethoddef: MMethodDef
775
776 # The mangled c name of the runtime_function
777 # Subclasses should redefine `build_c_name` instead
778 fun c_name: String
779 do
780 var res = self.c_name_cache
781 if res != null then return res
782 res = self.build_c_name
783 self.c_name_cache = res
784 return res
785 end
786
787 # Non cached version of `c_name`
788 protected fun build_c_name: String is abstract
789
790 private var c_name_cache: nullable String = null
791
792 # Implements a call of the runtime_function
793 # May inline the body or generate a C function call
794 fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
795
796 # Generate the code for the RuntimeFunction
797 # Warning: compile more than once compilation makes CC unhappy
798 fun compile_to_c(compiler: GlobalCompiler) is abstract
799 end
800
801 # A runtime function customized on a specific monomrph receiver type
802 private class CustomizedRuntimeFunction
803 super AbstractRuntimeFunction
804
805 # The considered reciever
806 # (usually is a live type but no strong guarantee)
807 var recv: MClassType
808
809 init(mmethoddef: MMethodDef, recv: MClassType)
810 do
811 super(mmethoddef)
812 self.recv = recv
813 end
814
815 redef fun build_c_name: String
816 do
817 var res = self.c_name_cache
818 if res != null then return res
819 if self.mmethoddef.mclassdef.bound_mtype == self.recv then
820 res = self.mmethoddef.c_name
821 else
822 res = "{mmethoddef.c_name}__{recv.c_name}"
823 end
824 self.c_name_cache = res
825 return res
826 end
827
828 redef fun ==(o)
829 # used in the compiler worklist
830 do
831 if not o isa CustomizedRuntimeFunction then return false
832 if self.mmethoddef != o.mmethoddef then return false
833 if self.recv != o.recv then return false
834 return true
835 end
836
837 redef fun hash
838 # used in the compiler work-list
839 do
840 var res = self.mmethoddef.hash + self.recv.hash
841 return res
842 end
843
844 redef fun to_s
845 do
846 if self.mmethoddef.mclassdef.bound_mtype == self.recv then
847 return self.mmethoddef.to_s
848 else
849 return "{self.mmethoddef}@{self.recv}"
850 end
851 end
852
853 # compile the code customized for the reciever
854 redef fun compile_to_c(compiler)
855 do
856 var recv = self.recv
857 var mmethoddef = self.mmethoddef
858 if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then
859 print("problem: why do we compile {self} for {recv}?")
860 abort
861 end
862
863 var v = compiler.new_visitor
864 var selfvar = new RuntimeVariable("self", recv, recv)
865 if compiler.runtime_type_analysis.live_types.has(recv) then
866 selfvar.is_exact = true
867 end
868 var arguments = new Array[RuntimeVariable]
869 var frame = new Frame(v, mmethoddef, recv, arguments)
870 v.frame = frame
871
872 var sig = new Buffer
873 var comment = new Buffer
874 var ret = mmethoddef.msignature.return_mtype
875 if ret != null then
876 ret = v.resolve_for(ret, selfvar)
877 sig.append("{ret.ctype} ")
878 else if mmethoddef.mproperty.is_new then
879 ret = recv
880 sig.append("{ret.ctype} ")
881 else
882 sig.append("void ")
883 end
884 sig.append(self.c_name)
885 sig.append("({recv.ctype} {selfvar}")
886 comment.append("(self: {recv}")
887 arguments.add(selfvar)
888 for i in [0..mmethoddef.msignature.arity[ do
889 var mtype = mmethoddef.msignature.mparameters[i].mtype
890 if i == mmethoddef.msignature.vararg_rank then
891 mtype = v.get_class("Array").get_mtype([mtype])
892 end
893 mtype = v.resolve_for(mtype, selfvar)
894 comment.append(", {mtype}")
895 sig.append(", {mtype.ctype} p{i}")
896 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
897 arguments.add(argvar)
898 end
899 sig.append(")")
900 comment.append(")")
901 if ret != null then
902 comment.append(": {ret}")
903 end
904 compiler.header.add_decl("{sig};")
905
906 v.add_decl("/* method {self} for {comment} */")
907 v.add_decl("{sig} \{")
908 #v.add("printf(\"method {self} for {comment}\\n\");")
909 if ret != null then
910 frame.returnvar = v.new_var(ret)
911 end
912 frame.returnlabel = v.get_name("RET_LABEL")
913
914 mmethoddef.compile_inside_to_c(v, arguments)
915
916 v.add("{frame.returnlabel.as(not null)}:;")
917 if ret != null then
918 v.add("return {frame.returnvar.as(not null)};")
919 end
920 v.add("\}")
921 end
922
923 redef fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
924 do
925 var ret = self.mmethoddef.msignature.return_mtype
926 if self.mmethoddef.mproperty.is_new then
927 ret = recv
928 end
929 if ret != null then
930 ret = v.resolve_for(ret, arguments.first)
931 end
932 if self.mmethoddef.can_inline(v) then
933 var frame = new Frame(v, self.mmethoddef, self.recv, arguments)
934 frame.returnlabel = v.get_name("RET_LABEL")
935 if ret != null then
936 frame.returnvar = v.new_var(ret)
937 end
938 var old_frame = v.frame
939 v.frame = frame
940 v.add("\{ /* Inline {self} ({arguments.join(",")}) */")
941 self.mmethoddef.compile_inside_to_c(v, arguments)
942 v.add("{frame.returnlabel.as(not null)}:(void)0;")
943 v.add("\}")
944 v.frame = old_frame
945 return frame.returnvar
946 end
947 v.adapt_signature(self.mmethoddef, arguments)
948 v.compiler.todo(self)
949 if ret == null then
950 v.add("{self.c_name}({arguments.join(",")});")
951 return null
952 else
953 var res = v.new_var(ret)
954 v.add("{res} = {self.c_name}({arguments.join(",")});")
955 return res
956 end
957 end
958 end
959
960 # A runtime variable hold a runtime value in C.
961 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
962 #
963 # 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.
964 class RuntimeVariable
965 # The name of the variable in the C code
966 var name: String
967
968 # The static type of the variable (as declard in C)
969 var mtype: MType
970
971 # The current casted type of the variable (as known in Nit)
972 var mcasttype: MType writable
973
974 # If the variable exaclty a mcasttype?
975 # false (usual value) means that the variable is a mcasttype or a subtype.
976 var is_exact: Bool writable = false
977
978 init(name: String, mtype: MType, mcasttype: MType)
979 do
980 self.name = name
981 self.mtype = mtype
982 self.mcasttype = mcasttype
983 assert not mtype.need_anchor
984 assert not mcasttype.need_anchor
985 end
986
987 redef fun to_s do return name
988
989 redef fun inspect
990 do
991 var exact_str
992 if self.is_exact then
993 exact_str = " exact"
994 else
995 exact_str = ""
996 end
997 var type_str
998 if self.mtype == self.mcasttype then
999 type_str = "{mtype}{exact_str}"
1000 else
1001 type_str = "{mtype}({mcasttype}{exact_str})"
1002 end
1003 return "<{name}:{type_str}>"
1004 end
1005 end
1006
1007 # A visitor on the AST of property definition that generate the C code.
1008 # Because of inlining, a visitor can visit more than one property.
1009 class GlobalCompilerVisitor
1010 # The associated compiler
1011 var compiler: GlobalCompiler
1012
1013 init(compiler: GlobalCompiler)
1014 do
1015 self.compiler = compiler
1016 compiler.visitors.add(self)
1017 end
1018
1019 # Alias for self.compiler.mainmodule.object_type
1020 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1021
1022 # Alias for self.compiler.mainmodule.bool_type
1023 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1024
1025 # Force to get the primitive class named `name' or abort
1026 fun get_class(name: String): MClass
1027 do
1028 return self.compiler.mainmodule.get_primitive_class(name)
1029 end
1030
1031 # Force to get the primitive property named `name' in the instance `recv' or abort
1032 fun get_property(name: String, recv: MType): MMethod
1033 do
1034 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
1035 end
1036
1037 # The current Frame
1038 var frame: nullable Frame writable
1039
1040 # Anchor a type to the main module and the current receiver
1041 fun anchor(mtype: MType): MType
1042 do
1043 if not mtype.need_anchor then return mtype
1044 #debug("anchor {mtype} to {self.reciever.as(not null)}:{self.reciever.mtype}")
1045 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1046 end
1047
1048 # Add a line in the main part of the generated C
1049 fun add(s: String)
1050 do
1051 self.lines.add(s)
1052 end
1053
1054 # Add a line in the
1055 # (used for local or global declaration)
1056 fun add_decl(s: String)
1057 do
1058 self.decl_lines.add(s)
1059 end
1060
1061 private var lines: List[String] = new List[String]
1062 private var decl_lines: List[String] = new List[String]
1063
1064 # The current visited AST node
1065 var current_node: nullable ANode = null
1066
1067 # Compile an expression an return its result
1068 # `mtype` is the expected return type, pass null if no specific type is expected.
1069 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1070 do
1071 var old = self.current_node
1072 self.current_node = nexpr
1073 var res = nexpr.expr(self).as(not null)
1074 if mtype != null then
1075 mtype = self.anchor(mtype)
1076 res = self.autobox(res, mtype)
1077 end
1078 res = autoadapt(res, nexpr.mtype.as(not null))
1079 var implicit_cast_to = nexpr.implicit_cast_to
1080 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1081 var castres = self.type_test(res, implicit_cast_to, "auto")
1082 self.add("if (!{castres}) \{")
1083 self.add_abort("Cast failed")
1084 self.add("\}")
1085 res = autoadapt(res, implicit_cast_to)
1086 end
1087 self.current_node = old
1088 return res
1089 end
1090
1091 # Unsafely cast a value to a new type
1092 # ie the result share the same C variable but my have a different mcasttype
1093 # NOTE: if the adaptation is useless then `value' is returned as it.
1094 # ENSURE: return.name == value.name
1095 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1096 do
1097 mtype = self.anchor(mtype)
1098 var valmtype = value.mcasttype
1099 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1100 return value
1101 end
1102
1103 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1104 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1105 return res
1106 else
1107 var res = new RuntimeVariable(value.name, valmtype, mtype)
1108 return res
1109 end
1110 end
1111
1112 # Box or unbox a value to another type iff a C type conversion is needed
1113 # ENSURE: result.mtype.ctype == mtype.ctype
1114 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable
1115 do
1116 if value.mtype == mtype then
1117 return value
1118 else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
1119 return value
1120 else if value.mtype.ctype == "val*" then
1121 return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
1122 else if mtype.ctype == "val*" then
1123 var valtype = value.mtype.as(MClassType)
1124 var res = self.new_var(mtype)
1125 if not compiler.runtime_type_analysis.live_types.has(valtype) then
1126 self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
1127 self.add("printf(\"Dead code executed!\\n\"); exit(1);")
1128 return res
1129 end
1130 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1131 return res
1132 else
1133 # Bad things will appen!
1134 var res = self.new_var(mtype)
1135 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1136 self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); exit(1);")
1137 return res
1138 end
1139 end
1140
1141 # Correctly assign a left and a right value
1142 # Boxing and unboxing is performed if required
1143 fun assign(left, right: RuntimeVariable)
1144 do
1145 right = self.autobox(right, left.mtype)
1146 self.add("{left} = {right};")
1147 end
1148
1149
1150 # Alias for `self.expr(nexpr, self.bool_type)'
1151 fun expr_bool(nexpr: AExpr): RuntimeVariable
1152 do
1153 return expr(nexpr, bool_type)
1154 end
1155
1156 # Compile a statement (if any)
1157 fun stmt(nexpr: nullable AExpr)
1158 do
1159 if nexpr == null then return
1160 var old = self.current_node
1161 self.current_node = nexpr
1162 nexpr.stmt(self)
1163 self.current_node = old
1164 end
1165
1166 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1167 fun debug(message: String)
1168 do
1169 var node = self.current_node
1170 if node == null then
1171 print "?: {message}"
1172 else
1173 node.debug(message)
1174 end
1175 self.add("/* DEBUG: {message} */")
1176 end
1177
1178 # Return a new uninitialized local runtime_variable
1179 fun new_var(mtype: MType): RuntimeVariable
1180 do
1181 mtype = self.anchor(mtype)
1182 var name = self.get_name("var")
1183 var res = new RuntimeVariable(name, mtype, mtype)
1184 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1185 return res
1186 end
1187
1188 # Return a new uninitialized named runtime_variable
1189 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1190 do
1191 mtype = self.anchor(mtype)
1192 var res = new RuntimeVariable(name, mtype, mtype)
1193 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1194 return res
1195 end
1196
1197 # Return a new local runtime_variable initialized with the C expression `cexpr'.
1198 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1199 do
1200 var res = new_var(mtype)
1201 self.add("{res} = {cexpr};")
1202 return res
1203 end
1204
1205 # Return the local runtime_variable associated to a Nit local variable
1206 fun variable(variable: Variable): RuntimeVariable
1207 do
1208 if self.variables.has_key(variable) then
1209 return self.variables[variable]
1210 else
1211 var name = self.get_name("var_{variable.name}")
1212 var mtype = variable.declared_type.as(not null)
1213 mtype = self.anchor(mtype)
1214 var res = new RuntimeVariable(name, mtype, mtype)
1215 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1216 self.variables[variable] = res
1217 return res
1218 end
1219 end
1220
1221 private var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
1222
1223 # Return an unique and stable identifier associated with an escapemark
1224 fun escapemark_name(e: nullable EscapeMark): String
1225 do
1226 assert e != null
1227 if escapemark_names.has_key(e) then return escapemark_names[e]
1228 var name = e.name
1229 if name == null then name = "label"
1230 name = get_name(name)
1231 escapemark_names[e] = name
1232 return name
1233 end
1234
1235 private var escapemark_names = new HashMap[EscapeMark, String]
1236
1237 # Return a new name based on `s' and unique in the visitor
1238 fun get_name(s: String): String
1239 do
1240 if not self.names.has(s) then
1241 self.names.add(s)
1242 return s
1243 end
1244 var i = self.last + 1
1245 loop
1246 var s2 = s + i.to_s
1247 if not self.names.has(s2) then
1248 self.last = i
1249 self.names.add(s2)
1250 return s2
1251 end
1252 i = i + 1
1253 end
1254 end
1255
1256 private var last: Int = 0
1257
1258 private var names: HashSet[String] = new HashSet[String]
1259
1260 # Generate a return with the value `s'
1261 fun ret(s: RuntimeVariable)
1262 do
1263 self.assign(self.frame.returnvar.as(not null), s)
1264 self.add("goto {self.frame.returnlabel.as(not null)};")
1265 end
1266
1267 # The runtime types that are acceptable for a given receiver.
1268 fun collect_types(recv: RuntimeVariable): Array[MClassType]
1269 do
1270 var mtype = recv.mcasttype
1271 if recv.is_exact then
1272 assert mtype isa MClassType
1273 assert self.compiler.runtime_type_analysis.live_types.has(mtype)
1274 var types = [mtype]
1275 return types
1276 end
1277 var cache = self.compiler.collect_types_cache
1278 if cache.has_key(mtype) then
1279 return cache[mtype]
1280 end
1281 var types = new Array[MClassType]
1282 var mainmodule = self.compiler.mainmodule
1283 for t in self.compiler.runtime_type_analysis.live_types do
1284 if not t.is_subtype(mainmodule, null, mtype) then continue
1285 types.add(t)
1286 end
1287 cache[mtype] = types
1288 return types
1289 end
1290
1291 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1292 do
1293 if not mtype.need_anchor then return mtype
1294 #debug("resolve for {mtype} to {recv}:{recv.mcasttype}(declared as {recv.mtype}) (in {self.reciever.to_s}:{self.reciever.mtype})")
1295 var res = mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1296 return res
1297 end
1298
1299 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable])
1300 do
1301 var elttype = arguments.first.mtype
1302 var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values"
1303 if pname == "[]" then
1304 self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
1305 return
1306 else if pname == "[]=" then
1307 self.add("{recv}[{arguments[1]}]={arguments[2]};")
1308 return
1309 else if pname == "copy_to" then
1310 var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values"
1311 self.add("memcpy({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));")
1312 return
1313 end
1314 end
1315
1316 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable])
1317 do
1318 self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
1319 end
1320
1321 # Add a check and an abort for a null reciever is needed
1322 fun check_recv_notnull(recv: RuntimeVariable)
1323 do
1324 if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
1325
1326 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1327 if maybenull then
1328 self.add("if ({recv} == NULL) \{")
1329 self.add_abort("Reciever is null")
1330 self.add("\}")
1331 end
1332 end
1333
1334 fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
1335 do
1336 var ret = self.send(callsite.mproperty, args)
1337 return ret
1338 end
1339
1340 # Generate a polymorphic send for the method `m' and the arguments `args'
1341 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable
1342 do
1343 var types = self.collect_types(args.first)
1344
1345 var res: nullable RuntimeVariable
1346 var ret = m.intro.msignature.return_mtype
1347 if m.is_new then
1348 ret = args.first.mtype
1349 res = self.new_var(ret)
1350 else if ret == null then
1351 res = null
1352 else
1353 ret = self.resolve_for(ret, args.first)
1354 res = self.new_var(ret)
1355 end
1356
1357 self.add("/* send {m} on {args.first.inspect} */")
1358 if args.first.mtype.ctype != "val*" then
1359 var mclasstype = args.first.mtype.as(MClassType)
1360 if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
1361 self.add("/* skip, no method {m} */")
1362 return res
1363 end
1364 var propdefs = m.lookup_definitions(self.compiler.mainmodule, mclasstype)
1365 if propdefs.length == 0 then
1366 self.add("/* skip, no method {m} */")
1367 return res
1368 end
1369 assert propdefs.length == 1
1370 var propdef = propdefs.first
1371 var res2 = self.call(propdef, mclasstype, args)
1372 if res != null then self.assign(res, res2.as(not null))
1373 return res
1374 end
1375 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or m.name == "==" or m.name == "!="
1376 if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then
1377 # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
1378 self.add("if ({args.first} == NULL) \{ /* Special null case */")
1379 if m.name == "==" then
1380 assert res != null
1381 if args[1].mcasttype isa MNullableType then
1382 self.add("{res} = ({args[1]} == NULL);")
1383 else if args[1].mcasttype isa MNullType then
1384 self.add("{res} = 1; /* is null */")
1385 else
1386 self.add("{res} = 0; /* {args[1].inspect} cannot be null */")
1387 end
1388 else if m.name == "!=" then
1389 assert res != null
1390 if args[1].mcasttype isa MNullableType then
1391 self.add("{res} = ({args[1]} != NULL);")
1392 else if args[1].mcasttype isa MNullType then
1393 self.add("{res} = 0; /* is null */")
1394 else
1395 self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
1396 end
1397 else
1398 self.add_abort("Reciever is null")
1399 end
1400 self.add "\} else"
1401 end
1402 if types.is_empty then
1403 self.add("\{")
1404 self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
1405 self.bugtype(args.first)
1406 self.add("\}")
1407 return res
1408 end
1409
1410 self.add("switch({args.first}->classid) \{")
1411 var last = types.last
1412 var defaultpropdef: nullable MMethodDef = null
1413 for t in types do
1414 var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
1415 if propdefs.length == 0 then
1416 self.add("/* skip {t}, no method {m} */")
1417 continue
1418 end
1419 if propdefs.length > 1 then
1420 self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
1421 end
1422 var propdef = propdefs.first
1423 if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
1424 defaultpropdef = propdef
1425 continue
1426 end
1427 if not self.compiler.hardening and t == last and defaultpropdef == null then
1428 self.add("default: /* test {t} */")
1429 else
1430 self.add("case {self.compiler.classid(t)}: /* test {t} */")
1431 end
1432 var res2 = self.call(propdef, t, args)
1433 if res != null then self.assign(res, res2.as(not null))
1434 self.add "break;"
1435 end
1436 if defaultpropdef != null then
1437 self.add("default: /* default is Object */")
1438 var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args)
1439 if res != null then self.assign(res, res2.as(not null))
1440 else if self.compiler.hardening then
1441 self.add("default: /* bug */")
1442 self.bugtype(args.first)
1443 end
1444 self.add("\}")
1445 return res
1446 end
1447
1448 # Generate a monomorphic send for the method `m', the type `t' and the arguments `args'
1449 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1450 do
1451 assert t isa MClassType
1452 var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
1453 if propdefs.length == 0 then
1454 abort
1455 end
1456 if propdefs.length > 1 then
1457 self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
1458 end
1459 var propdef = propdefs.first
1460 return self.call(propdef, t, args)
1461 end
1462
1463 fun check_valid_reciever(recvtype: MClassType)
1464 do
1465 if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return
1466 print "{recvtype} is not a live type"
1467 abort
1468 end
1469
1470 # Generate a static call on a method definition
1471 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1472 do
1473 check_valid_reciever(recvtype)
1474 #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}")
1475 if m.mclassdef.mclass.name == "Object" and recvtype.ctype == "val*" then
1476 recvtype = m.mclassdef.bound_mtype
1477 end
1478 var recv = self.autobox(args.first, recvtype)
1479 recv = self.autoadapt(recv, recvtype)
1480
1481 args = args.to_a
1482 self.varargize(m, m.msignature.as(not null), args)
1483 if args.length != m.msignature.arity + 1 then # because of self
1484 add("printf(\"NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.\\n\"); exit(1);")
1485 debug("NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.")
1486 return null
1487 end
1488
1489 args.first = recv
1490 var rm = new CustomizedRuntimeFunction(m, recvtype)
1491 return rm.call(self, args)
1492 end
1493
1494 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable])
1495 do
1496 var recv = args.first
1497 for i in [0..m.msignature.arity[ do
1498 var t = m.msignature.mparameters[i].mtype
1499 if i == m.msignature.vararg_rank then
1500 t = args[i+1].mtype
1501 end
1502 t = self.resolve_for(t, recv)
1503 args[i+1] = self.autobox(args[i+1], t)
1504 end
1505 end
1506
1507 # Transform varargs, in raw arguments, into a single argument of type Array
1508 # Note: this method modify the given `args`
1509 # If there is no vararg, then `args` is not modified.
1510 fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
1511 do
1512 var recv = args.first
1513 var vararg_rank = msignature.vararg_rank
1514 if vararg_rank >= 0 then
1515 assert args.length >= msignature.arity + 1 # because of self
1516 var rawargs = args
1517 args = new Array[RuntimeVariable]
1518
1519 args.add(rawargs.first) # recv
1520
1521 for i in [0..vararg_rank[ do
1522 args.add(rawargs[i+1])
1523 end
1524
1525 var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
1526 var vararg = new Array[RuntimeVariable]
1527 for i in [vararg_rank..vararg_lastrank] do
1528 vararg.add(rawargs[i+1])
1529 end
1530
1531 var elttype = msignature.mparameters[vararg_rank].mtype
1532 args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
1533
1534 for i in [vararg_lastrank+1..rawargs.length-1[ do
1535 args.add(rawargs[i+1])
1536 end
1537 rawargs.clear
1538 rawargs.add_all(args)
1539 end
1540 end
1541
1542 # Get an instance of a anny for a vararg
1543 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable
1544 do
1545 # FIXME: this is currently buggy since recv is not exact
1546
1547 elttype = self.resolve_for(elttype, recv)
1548 return self.array_instance(varargs, elttype)
1549 end
1550
1551
1552 fun bugtype(recv: RuntimeVariable)
1553 do
1554 if recv.mtype.ctype != "val*" then return
1555 self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
1556 self.add("exit(1);")
1557 end
1558
1559 # Generate a polymorphic attribute is_set test
1560 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
1561 do
1562 check_recv_notnull(recv)
1563
1564 var types = self.collect_types(recv)
1565
1566 var res = self.new_var(bool_type)
1567
1568 if types.is_empty then
1569 self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
1570 self.bugtype(recv)
1571 return res
1572 end
1573 self.add("/* isset {a} on {recv.inspect} */")
1574 self.add("switch({recv}->classid) \{")
1575 var last = types.last
1576 for t in types do
1577 if not self.compiler.hardening and t == last then
1578 self.add("default: /*{self.compiler.classid(t)}*/")
1579 else
1580 self.add("case {self.compiler.classid(t)}:")
1581 end
1582 var recv2 = self.autoadapt(recv, t)
1583 var ta = a.intro.static_mtype.as(not null)
1584 ta = self.resolve_for(ta, recv2)
1585 var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
1586 if not ta isa MNullableType then
1587 if ta.ctype == "val*" then
1588 self.add("{res} = ({attr} != NULL);")
1589 else
1590 self.add("{res} = 1; /*NOTYET isset on primitive attributes*/")
1591 end
1592 end
1593 self.add("break;")
1594 end
1595 if self.compiler.hardening then
1596 self.add("default: /* Bug */")
1597 self.bugtype(recv)
1598 end
1599 self.add("\}")
1600
1601 return res
1602 end
1603
1604 # Generate a polymorphic attribute read
1605 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
1606 do
1607 check_recv_notnull(recv)
1608
1609 var types = self.collect_types(recv)
1610
1611 var ret = a.intro.static_mtype.as(not null)
1612 ret = self.resolve_for(ret, recv)
1613 var res = self.new_var(ret)
1614
1615 if types.is_empty then
1616 self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
1617 self.bugtype(recv)
1618 return res
1619 end
1620 self.add("/* read {a} on {recv.inspect} */")
1621 self.add("switch({recv}->classid) \{")
1622 var last = types.last
1623 for t in types do
1624 if not self.compiler.hardening and t == last then
1625 self.add("default: /*{self.compiler.classid(t)}*/")
1626 else
1627 self.add("case {self.compiler.classid(t)}:")
1628 end
1629 var recv2 = self.autoadapt(recv, t)
1630 var ta = a.intro.static_mtype.as(not null)
1631 ta = self.resolve_for(ta, recv2)
1632 var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
1633 if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then
1634 if ta.ctype == "val*" then
1635 self.add("if ({res2} == NULL) \{")
1636 self.add_abort("Uninitialized attribute {a.name}")
1637 self.add("\}")
1638 else
1639 self.add("/*NOTYET isset on primitive attributes*/")
1640 end
1641 end
1642 self.assign(res, res2)
1643 self.add("break;")
1644 end
1645 if self.compiler.hardening then
1646 self.add("default: /* Bug */")
1647 self.bugtype(recv)
1648 end
1649 self.add("\}")
1650
1651 return res
1652 end
1653
1654 # Generate a polymorphic attribute write
1655 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable)
1656 do
1657 check_recv_notnull(recv)
1658
1659 var types = self.collect_types(recv)
1660
1661 if types.is_empty then
1662 self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
1663 self.bugtype(recv)
1664 return
1665 end
1666 self.add("/* write {a} on {recv.inspect} */")
1667 self.add("switch({recv}->classid) \{")
1668 var last = types.last
1669 for t in types do
1670 if not self.compiler.hardening and t == last then
1671 self.add("default: /*{self.compiler.classid(t)}*/")
1672 else
1673 self.add("case {self.compiler.classid(t)}:")
1674 end
1675 var recv2 = self.autoadapt(recv, t)
1676 var ta = a.intro.static_mtype.as(not null)
1677 ta = self.resolve_for(ta, recv2)
1678 self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};")
1679 self.add("break;")
1680 end
1681 if self.compiler.hardening then
1682 self.add("default: /* Bug*/")
1683 self.bugtype(recv)
1684 end
1685 self.add("\}")
1686 end
1687
1688 # Generate a alloc-instance + init-attributes
1689 fun init_instance(mtype: MClassType): RuntimeVariable
1690 do
1691 mtype = self.anchor(mtype).as(MClassType)
1692 if not self.compiler.runtime_type_analysis.live_types.has(mtype) then
1693 debug "problem: {mtype} was detected dead"
1694 end
1695 var res = self.new_expr("NEW_{mtype.c_name}()", mtype)
1696 res.is_exact = true
1697 return res
1698 end
1699
1700 # Generate a polymorphic subtype test
1701 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable
1702 do
1703 mtype = self.anchor(mtype)
1704 var mclasstype = mtype
1705 if mtype isa MNullableType then mclasstype = mtype.mtype
1706 assert mclasstype isa MClassType
1707 if not self.compiler.runtime_type_analysis.live_cast_types.has(mclasstype) then
1708 debug "problem: {mtype} was detected cast-dead"
1709 abort
1710 end
1711
1712 var types = self.collect_types(value)
1713
1714 var res = self.new_var(bool_type)
1715
1716 self.add("/* isa {mtype} on {value.inspect} */")
1717 if value.mtype.ctype != "val*" then
1718 if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1719 self.add("{res} = 1;")
1720 else
1721 self.add("{res} = 0;")
1722 end
1723 return res
1724 end
1725 if value.mcasttype isa MNullableType or value.mcasttype isa MNullType then
1726 self.add("if ({value} == NULL) \{")
1727 if mtype isa MNullableType then
1728 self.add("{res} = 1; /* isa {mtype} */")
1729 else
1730 self.add("{res} = 0; /* not isa {mtype} */")
1731 end
1732 self.add("\} else ")
1733 end
1734 self.add("switch({value}->classid) \{")
1735 for t in types do
1736 if t.is_subtype(self.compiler.mainmodule, null, mtype) then
1737 self.add("case {self.compiler.classid(t)}: /* {t} */")
1738 end
1739 end
1740 self.add("{res} = 1;")
1741 self.add("break;")
1742 self.add("default:")
1743 self.add("{res} = 0;")
1744 self.add("\}")
1745
1746 return res
1747 end
1748
1749 # Generate the code required to dynamically check if 2 objects share the same runtime type
1750 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable
1751 do
1752 var res = self.new_var(bool_type)
1753 if value2.mtype.ctype == "val*" then
1754 if value1.mtype.ctype == "val*" then
1755 self.add "{res} = {value1}->classid == {value2}->classid;"
1756 else
1757 self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;"
1758 end
1759 else
1760 if value1.mtype.ctype == "val*" then
1761 self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};"
1762 else if value1.mcasttype == value2.mcasttype then
1763 self.add "{res} = 1;"
1764 else
1765 self.add "{res} = 0;"
1766 end
1767 end
1768 return res
1769 end
1770
1771 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1772 # NOTE: we do not return a RuntimeVariable "NativeString" as the class may not exist in the module/program
1773 fun class_name_string(value: RuntimeVariable): String
1774 do
1775 var res = self.get_name("var_class_name")
1776 self.add_decl("const char* {res};")
1777 if value.mtype.ctype == "val*" then
1778 self.add "{res} = class_names[{value}->classid];"
1779 else
1780 self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];"
1781 end
1782 return res
1783 end
1784
1785 # Generate a Nit "is" for two runtime_variables
1786 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable
1787 do
1788 var res = self.new_var(bool_type)
1789 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1790 var tmp = value1
1791 value1 = value2
1792 value2 = tmp
1793 end
1794 if value1.mtype.ctype != "val*" then
1795 if value2.mtype == value1.mtype then
1796 self.add("{res} = {value1} == {value2};")
1797 else if value2.mtype.ctype != "val*" then
1798 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1799 else
1800 var mtype1 = value1.mtype.as(MClassType)
1801 self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});")
1802 self.add("if ({res}) \{")
1803 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1804 self.add("\}")
1805 end
1806 else
1807 var s = new Array[String]
1808 for t in self.compiler.live_primitive_types do
1809 if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue
1810 if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
1811 s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
1812 end
1813 if s.is_empty then
1814 self.add("{res} = {value1} == {value2};")
1815 else
1816 self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));")
1817 end
1818 end
1819 return res
1820 end
1821
1822 # Generate a check-init-instance
1823 fun check_init_instance(recv: RuntimeVariable, mtype: MClassType)
1824 do
1825 if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return
1826
1827 mtype = self.anchor(mtype).as(MClassType)
1828 if not self.compiler.runtime_type_analysis.live_types.has(mtype) then
1829 debug "problem: {mtype} was detected dead"
1830 end
1831
1832 self.add("CHECK_NEW_{mtype.c_name}({recv});")
1833 end
1834
1835 # Generate an integer value
1836 fun int_instance(value: Int): RuntimeVariable
1837 do
1838 var res = self.new_var(self.get_class("Int").mclass_type)
1839 self.add("{res} = {value};")
1840 return res
1841 end
1842
1843 # Generate an array value
1844 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable
1845 do
1846 elttype = self.anchor(elttype)
1847 var arraytype = self.get_class("Array").get_mtype([elttype])
1848 var res = self.init_instance(arraytype)
1849 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1850 var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
1851 nat.is_exact = true
1852 self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});")
1853 for i in [0..array.length[ do
1854 var r = self.autobox(array[i], elttype)
1855 self.add("((struct {nat.mtype.c_name}*) {nat})->values[{i}] = {r};")
1856 end
1857 var length = self.int_instance(array.length)
1858 self.send(self.get_property("with_native", arraytype), [res, nat, length])
1859 self.check_init_instance(res, arraytype)
1860 self.add("\}")
1861 return res
1862 end
1863
1864 # Generate a string value
1865 fun string_instance(string: String): RuntimeVariable
1866 do
1867 var mtype = self.get_class("String").mclass_type
1868 var name = self.get_name("varonce")
1869 self.add_decl("static {mtype.ctype} {name};")
1870 var res = self.new_var(mtype)
1871 self.add("if ({name}) \{")
1872 self.add("{res} = {name};")
1873 self.add("\} else \{")
1874 var nat = self.new_var(self.get_class("NativeString").mclass_type)
1875 self.add("{nat} = \"{string.escape_to_c}\";")
1876 var res2 = self.init_instance(mtype)
1877 self.add("{res} = {res2};")
1878 var length = self.int_instance(string.length)
1879 self.send(self.get_property("with_native", mtype), [res, nat, length])
1880 self.check_init_instance(res, mtype)
1881 self.add("{name} = {res};")
1882 self.add("\}")
1883 return res
1884 end
1885
1886 # Generate generic abort
1887 # used by aborts, asserts, casts, etc.
1888 fun add_abort(message: String)
1889 do
1890 if self.current_node != null and self.current_node.location.file != null then
1891 self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1892 else
1893 self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
1894 end
1895 self.add("exit(1);")
1896 end
1897 end
1898
1899 # A frame correspond to a visited property in a GlobalCompilerVisitor
1900 class Frame
1901 # The associated visitor
1902
1903 var visitor: GlobalCompilerVisitor
1904
1905 # The executed property.
1906 # A Method in case of a call, an attribute in case of a default initialization.
1907 var mpropdef: MPropDef
1908
1909 # The static type of the receiver
1910 var receiver: MClassType
1911
1912 # Arguments of the method (the first is the receiver)
1913 var arguments: Array[RuntimeVariable]
1914
1915 # The runtime_variable associated to the return (in a function)
1916 var returnvar: nullable RuntimeVariable writable = null
1917
1918 # The label at the end of the property
1919 var returnlabel: nullable String writable = null
1920 end
1921
1922 redef class MPropDef
1923 private var c_name_cache: nullable String
1924
1925 # The mangled name associated to the property
1926 fun c_name: String
1927 do
1928 var res = self.c_name_cache
1929 if res != null then return res
1930 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1931 self.c_name_cache = res
1932 return res
1933 end
1934 end
1935
1936 redef class MMethodDef
1937 # Can the body be inlined?
1938 fun can_inline(v: GlobalCompilerVisitor): Bool
1939 do
1940 var modelbuilder = v.compiler.modelbuilder
1941 if modelbuilder.mpropdef2npropdef.has_key(self) then
1942 var npropdef = modelbuilder.mpropdef2npropdef[self]
1943 return npropdef.can_inline
1944 else if self.mproperty.name == "init" then
1945 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1946 return true
1947 else
1948 abort
1949 end
1950 end
1951
1952 # Inline the body in another visitor
1953 fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1954 do
1955 var modelbuilder = v.compiler.modelbuilder
1956 if modelbuilder.mpropdef2npropdef.has_key(self) then
1957 var npropdef = modelbuilder.mpropdef2npropdef[self]
1958 var oldnode = v.current_node
1959 v.current_node = npropdef
1960 self.compile_parameter_check(v, arguments)
1961 npropdef.compile_to_c(v, self, arguments)
1962 v.current_node = oldnode
1963 else if self.mproperty.name == "init" then
1964 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1965 var oldnode = v.current_node
1966 v.current_node = nclassdef
1967 self.compile_parameter_check(v, arguments)
1968 nclassdef.compile_to_c(v, self, arguments)
1969 v.current_node = oldnode
1970 else
1971 abort
1972 end
1973 return null
1974 end
1975
1976 # Generate type checks in the C code to check covariant parameters
1977 fun compile_parameter_check(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable])
1978 do
1979 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1980
1981 for i in [0..msignature.arity[ do
1982 # skip test for vararg since the array is instantiated with the correct polymorphic type
1983 if msignature.vararg_rank == i then continue
1984
1985 # skip if the cast is not required
1986 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1987 if not origmtype.need_anchor then continue
1988
1989 # get the parameter type
1990 var mtype = self.msignature.mparameters[i].mtype
1991
1992 # generate the cast
1993 # note that v decides if and how to implements the cast
1994 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1995 var cond = v.type_test(arguments[i+1], mtype, "covariance")
1996 v.add("if (!{cond}) \{")
1997 #var x = v.class_name_string(arguments[i+1])
1998 #var y = v.class_name_string(arguments.first)
1999 #v.add("fprintf(stderr, \"expected {mtype} (self is %s), got %s for {arguments[i+1].inspect}\\n\", {y}, {x});")
2000 v.add_abort("Cast failed")
2001 v.add("\}")
2002 end
2003 end
2004 end
2005
2006 redef class APropdef
2007 fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2008 do
2009 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2010 debug("Not yet implemented")
2011 end
2012
2013 fun can_inline: Bool do return true
2014 end
2015
2016 redef class AConcreteMethPropdef
2017 redef fun compile_to_c(v, mpropdef, arguments)
2018 do
2019 for i in [0..mpropdef.msignature.arity[ do
2020 var variable = self.n_signature.n_params[i].variable.as(not null)
2021 v.assign(v.variable(variable), arguments[i+1])
2022 end
2023 # Call the implicit super-init
2024 var auto_super_inits = self.auto_super_inits
2025 if auto_super_inits != null then
2026 var selfarg = [arguments.first]
2027 for auto_super_init in auto_super_inits do
2028 if auto_super_init.intro.msignature.arity == 0 then
2029 v.send(auto_super_init, selfarg)
2030 else
2031 v.send(auto_super_init, arguments)
2032 end
2033 end
2034 end
2035 v.stmt(self.n_block)
2036 end
2037
2038 redef fun can_inline
2039 do
2040 if self.auto_super_inits != null then return false
2041 var nblock = self.n_block
2042 if nblock == null then return true
2043 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2044 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2045 return false
2046 end
2047 end
2048
2049 redef class AInternMethPropdef
2050 redef fun compile_to_c(v, mpropdef, arguments)
2051 do
2052 var pname = mpropdef.mproperty.name
2053 var cname = mpropdef.mclassdef.mclass.name
2054 var ret = mpropdef.msignature.return_mtype
2055 if ret != null then
2056 ret = v.resolve_for(ret, arguments.first)
2057 end
2058 if pname != "==" and pname != "!=" then
2059 v.adapt_signature(mpropdef, arguments)
2060 end
2061 if cname == "Int" then
2062 if pname == "output" then
2063 v.add("printf(\"%ld\\n\", {arguments.first});")
2064 return
2065 else if pname == "object_id" then
2066 v.ret(arguments.first)
2067 return
2068 else if pname == "+" then
2069 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2070 return
2071 else if pname == "-" then
2072 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2073 return
2074 else if pname == "unary -" then
2075 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2076 return
2077 else if pname == "succ" then
2078 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2079 return
2080 else if pname == "prec" then
2081 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2082 return
2083 else if pname == "*" then
2084 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2085 return
2086 else if pname == "/" then
2087 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2088 return
2089 else if pname == "%" then
2090 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2091 return
2092 else if pname == "lshift" then
2093 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2094 return
2095 else if pname == "rshift" then
2096 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2097 return
2098 else if pname == "==" then
2099 v.ret(v.equal_test(arguments[0], arguments[1]))
2100 return
2101 else if pname == "!=" then
2102 var res = v.equal_test(arguments[0], arguments[1])
2103 v.ret(v.new_expr("!{res}", ret.as(not null)))
2104 return
2105 else if pname == "<" then
2106 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2107 return
2108 else if pname == ">" then
2109 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2110 return
2111 else if pname == "<=" then
2112 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2113 return
2114 else if pname == ">=" then
2115 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2116 return
2117 else if pname == "to_f" then
2118 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2119 return
2120 else if pname == "ascii" then
2121 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2122 return
2123 end
2124 else if cname == "Char" then
2125 if pname == "output" then
2126 v.add("printf(\"%c\", {arguments.first});")
2127 return
2128 else if pname == "object_id" then
2129 v.ret(arguments.first)
2130 return
2131 else if pname == "+" then
2132 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2133 return
2134 else if pname == "-" then
2135 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2136 return
2137 else if pname == "==" then
2138 v.ret(v.equal_test(arguments[0], arguments[1]))
2139 return
2140 else if pname == "!=" then
2141 var res = v.equal_test(arguments[0], arguments[1])
2142 v.ret(v.new_expr("!{res}", ret.as(not null)))
2143 return
2144 else if pname == "succ" then
2145 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2146 return
2147 else if pname == "prec" then
2148 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2149 return
2150 else if pname == "<" then
2151 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2152 return
2153 else if pname == ">" then
2154 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2155 return
2156 else if pname == "<=" then
2157 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2158 return
2159 else if pname == ">=" then
2160 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2161 return
2162 else if pname == "to_i" then
2163 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2164 return
2165 else if pname == "ascii" then
2166 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2167 return
2168 end
2169 else if cname == "Bool" then
2170 if pname == "output" then
2171 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2172 return
2173 else if pname == "object_id" then
2174 v.ret(arguments.first)
2175 return
2176 else if pname == "==" then
2177 v.ret(v.equal_test(arguments[0], arguments[1]))
2178 return
2179 else if pname == "!=" then
2180 var res = v.equal_test(arguments[0], arguments[1])
2181 v.ret(v.new_expr("!{res}", ret.as(not null)))
2182 return
2183 end
2184 else if cname == "Float" then
2185 if pname == "output" then
2186 v.add("printf(\"%f\\n\", {arguments.first});")
2187 return
2188 else if pname == "object_id" then
2189 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2190 return
2191 else if pname == "+" then
2192 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2193 return
2194 else if pname == "-" then
2195 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2196 return
2197 else if pname == "unary -" then
2198 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2199 return
2200 else if pname == "succ" then
2201 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2202 return
2203 else if pname == "prec" then
2204 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2205 return
2206 else if pname == "*" then
2207 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2208 return
2209 else if pname == "/" then
2210 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2211 return
2212 else if pname == "==" then
2213 v.ret(v.equal_test(arguments[0], arguments[1]))
2214 return
2215 else if pname == "!=" then
2216 var res = v.equal_test(arguments[0], arguments[1])
2217 v.ret(v.new_expr("!{res}", ret.as(not null)))
2218 return
2219 else if pname == "<" then
2220 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2221 return
2222 else if pname == ">" then
2223 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2224 return
2225 else if pname == "<=" then
2226 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2227 return
2228 else if pname == ">=" then
2229 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2230 return
2231 else if pname == "to_i" then
2232 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2233 return
2234 end
2235 else if cname == "Char" then
2236 if pname == "output" then
2237 v.add("printf(\"%c\", {arguments.first});")
2238 return
2239 else if pname == "object_id" then
2240 v.ret(arguments.first)
2241 return
2242 else if pname == "==" then
2243 v.ret(v.equal_test(arguments[0], arguments[1]))
2244 return
2245 else if pname == "!=" then
2246 var res = v.equal_test(arguments[0], arguments[1])
2247 v.ret(v.new_expr("!{res}", ret.as(not null)))
2248 return
2249 else if pname == "ascii" then
2250 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2251 return
2252 end
2253 else if cname == "NativeString" then
2254 if pname == "[]" then
2255 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2256 return
2257 else if pname == "[]=" then
2258 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2259 return
2260 else if pname == "copy_to" then
2261 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2262 return
2263 else if pname == "atoi" then
2264 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2265 return
2266 end
2267 else if cname == "NativeArray" then
2268 v.native_array_def(pname, ret, arguments)
2269 return
2270 end
2271 if pname == "exit" then
2272 v.add("exit({arguments[1]});")
2273 return
2274 else if pname == "sys" then
2275 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2276 return
2277 else if pname == "calloc_string" then
2278 v.ret(v.new_expr("(char*)GC_MALLOC_ATOMIC({arguments[1]})", ret.as(not null)))
2279 return
2280 else if pname == "calloc_array" then
2281 v.calloc_array(ret.as(not null), arguments)
2282 return
2283 else if pname == "object_id" then
2284 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2285 return
2286 else if pname == "is_same_type" then
2287 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2288 return
2289 else if pname == "output_class_name" then
2290 var nat = v.class_name_string(arguments.first)
2291 v.add("printf(\"%s\\n\", {nat});")
2292 return
2293 else if pname == "native_class_name" then
2294 var nat = v.class_name_string(arguments.first)
2295 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2296 return
2297 else if pname == "force_garbage_collection" then
2298 v.add("GC_gcollect();")
2299 return
2300 end
2301 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
2302 debug("Not implemented {mpropdef}")
2303 end
2304 end
2305
2306 redef class AExternMethPropdef
2307 redef fun compile_to_c(v, mpropdef, arguments)
2308 do
2309 var externname
2310 var nextern = self.n_extern
2311 if nextern == null then
2312 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
2313 v.add("exit(1);")
2314 return
2315 end
2316 externname = nextern.text.substring(1, nextern.text.length-2)
2317 if location.file != null then
2318 var file = location.file.filename
2319 v.compiler.add_extern(file)
2320 end
2321 var res: nullable RuntimeVariable = null
2322 var ret = mpropdef.msignature.return_mtype
2323 if ret != null then
2324 ret = v.resolve_for(ret, arguments.first)
2325 res = v.new_var(ret)
2326 end
2327 v.adapt_signature(mpropdef, arguments)
2328
2329 if res == null then
2330 v.add("{externname}({arguments.join(", ")});")
2331 else
2332 v.add("{res} = {externname}({arguments.join(", ")});")
2333 v.ret(res)
2334 end
2335 end
2336 end
2337
2338 redef class AExternInitPropdef
2339 redef fun compile_to_c(v, mpropdef, arguments)
2340 do
2341 var externname
2342 var nextern = self.n_extern
2343 if nextern == null then
2344 debug("{mpropdef} need extern name")
2345 return
2346 end
2347 externname = nextern.text.substring(1, nextern.text.length-2)
2348 if location.file != null then
2349 var file = location.file.filename
2350 v.compiler.add_extern(file)
2351 end
2352 v.adapt_signature(mpropdef, arguments)
2353 var ret = arguments.first.mtype
2354 var res = v.new_var(ret)
2355
2356 arguments.shift
2357
2358 v.add("{res} = {externname}({arguments.join(", ")});")
2359 v.ret(res)
2360 end
2361 end
2362
2363 redef class AAttrPropdef
2364 redef fun compile_to_c(v, mpropdef, arguments)
2365 do
2366 if arguments.length == 1 then
2367 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2368 v.assign(v.frame.returnvar.as(not null), res)
2369 else
2370 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2371 end
2372 end
2373
2374 fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
2375 do
2376 var nexpr = self.n_expr
2377 if nexpr != null then
2378 var oldnode = v.current_node
2379 v.current_node = self
2380 var old_frame = v.frame
2381 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2382 v.frame = frame
2383 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2384 v.write_attribute(self.mpropdef.mproperty, recv, value)
2385 v.frame = old_frame
2386 v.current_node = oldnode
2387 end
2388 end
2389
2390 fun check_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
2391 do
2392 var nexpr = self.n_expr
2393 if nexpr != null then return
2394
2395 var oldnode = v.current_node
2396 v.current_node = self
2397 var old_frame = v.frame
2398 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2399 v.frame = frame
2400 # Force read to check the initialization
2401 v.read_attribute(self.mpropdef.mproperty, recv)
2402 v.frame = old_frame
2403 v.current_node = oldnode
2404 end
2405 end
2406
2407 redef class AClassdef
2408 private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2409 do
2410 if mpropdef == self.mfree_init then
2411 var super_inits = self.super_inits
2412 if super_inits != null then
2413 assert arguments.length == 1
2414 for su in super_inits do
2415 v.send(su, arguments)
2416 end
2417 return
2418 end
2419 var recv = arguments.first
2420 var i = 1
2421 # Collect undefined attributes
2422 for npropdef in self.n_propdefs do
2423 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
2424 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
2425 i += 1
2426 end
2427 end
2428 else
2429 abort
2430 end
2431 end
2432 end
2433
2434 redef class ADeferredMethPropdef
2435 redef fun compile_to_c(v, mpropdef, arguments)
2436 do
2437 v.add_abort("Deferred method called")
2438 end
2439
2440 redef fun can_inline do return true
2441 end
2442
2443 redef class AExpr
2444 # Try to compile self as an expression
2445 # Do not call this method directly, use `v.expr' instead
2446 private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable
2447 do
2448 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2449 var mtype = self.mtype
2450 if mtype == null then
2451 return null
2452 else
2453 var res = v.new_var(mtype)
2454 v.add("/* {res} = NOT YET {class_name} */")
2455 return res
2456 end
2457 end
2458
2459 # Try to compile self as a statement
2460 # Do not call this method directly, use `v.stmt' instead
2461 private fun stmt(v: GlobalCompilerVisitor)
2462 do
2463 var res = expr(v)
2464 if res != null then v.add("{res};")
2465 end
2466
2467 end
2468
2469 redef class ABlockExpr
2470 redef fun stmt(v)
2471 do
2472 for e in self.n_expr do
2473 v.stmt(e)
2474 end
2475 end
2476 end
2477
2478 redef class AVardeclExpr
2479 redef fun stmt(v)
2480 do
2481 var variable = self.variable.as(not null)
2482 var ne = self.n_expr
2483 if ne != null then
2484 var i = v.expr(ne, variable.declared_type)
2485 v.assign(v.variable(variable), i)
2486 end
2487 end
2488 end
2489
2490 redef class AVarExpr
2491 redef fun expr(v)
2492 do
2493 var res = v.variable(self.variable.as(not null))
2494 var mtype = self.mtype.as(not null)
2495 return v.autoadapt(res, mtype)
2496 end
2497 end
2498
2499 redef class AVarAssignExpr
2500 redef fun stmt(v)
2501 do
2502 var variable = self.variable.as(not null)
2503 var i = v.expr(self.n_value, variable.declared_type)
2504 v.assign(v.variable(variable), i)
2505 end
2506 end
2507
2508 redef class AVarReassignExpr
2509 redef fun stmt(v)
2510 do
2511 var variable = self.variable.as(not null)
2512 var vari = v.variable(variable)
2513 var value = v.expr(self.n_value, variable.declared_type)
2514 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2515 assert res != null
2516 v.assign(v.variable(variable), res)
2517 end
2518 end
2519
2520 redef class ASelfExpr
2521 redef fun expr(v)
2522 do
2523 return v.frame.arguments.first
2524 end
2525 end
2526
2527 redef class AContinueExpr
2528 redef fun stmt(v)
2529 do
2530 v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2531 end
2532 end
2533
2534 redef class ABreakExpr
2535 redef fun stmt(v)
2536 do
2537 v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2538 end
2539 end
2540
2541 redef class AReturnExpr
2542 redef fun stmt(v)
2543 do
2544 var nexpr = self.n_expr
2545 if nexpr != null then
2546 var returnvar = v.frame.returnvar.as(not null)
2547 var i = v.expr(nexpr, returnvar.mtype)
2548 v.assign(returnvar, i)
2549 end
2550 v.add("goto {v.frame.returnlabel.as(not null)};")
2551 end
2552 end
2553
2554 redef class AAbortExpr
2555 redef fun stmt(v)
2556 do
2557 v.add_abort("Aborted")
2558 end
2559 end
2560
2561 redef class AIfExpr
2562 redef fun stmt(v)
2563 do
2564 var cond = v.expr_bool(self.n_expr)
2565 v.add("if ({cond})\{")
2566 v.stmt(self.n_then)
2567 v.add("\} else \{")
2568 v.stmt(self.n_else)
2569 v.add("\}")
2570 end
2571 end
2572
2573 redef class AIfexprExpr
2574 redef fun expr(v)
2575 do
2576 var res = v.new_var(self.mtype.as(not null))
2577 var cond = v.expr_bool(self.n_expr)
2578 v.add("if ({cond})\{")
2579 v.assign(res, v.expr(self.n_then, null))
2580 v.add("\} else \{")
2581 v.assign(res, v.expr(self.n_else, null))
2582 v.add("\}")
2583 return res
2584 end
2585 end
2586
2587 redef class ADoExpr
2588 redef fun stmt(v)
2589 do
2590 v.stmt(self.n_block)
2591 var escapemark = self.escapemark
2592 if escapemark != null then
2593 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2594 end
2595 end
2596 end
2597
2598 redef class AWhileExpr
2599 redef fun stmt(v)
2600 do
2601 v.add("for(;;) \{")
2602 var cond = v.expr_bool(self.n_expr)
2603 v.add("if (!{cond}) break;")
2604 v.stmt(self.n_block)
2605 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2606 v.add("\}")
2607 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2608 end
2609 end
2610
2611 redef class ALoopExpr
2612 redef fun stmt(v)
2613 do
2614 v.add("for(;;) \{")
2615 v.stmt(self.n_block)
2616 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2617 v.add("\}")
2618 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2619 end
2620 end
2621
2622 redef class AForExpr
2623 redef fun stmt(v)
2624 do
2625 # Shortcut on explicit range
2626 # Avoid the instantiation of the range and the iterator
2627 var nexpr = self.n_expr
2628 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2629 var from = v.expr(nexpr.n_expr, null)
2630 var to = v.expr(nexpr.n_expr2, null)
2631 var variable = v.variable(variables.first)
2632
2633 v.assign(variable, from)
2634 v.add("for(;;) \{ /* shortcut range */")
2635
2636 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2637 assert ok != null
2638 v.add("if(!{ok}) break;")
2639
2640 v.stmt(self.n_block)
2641
2642 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2643 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2644 assert succ != null
2645 v.assign(variable, succ)
2646 v.add("\}")
2647 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2648 return
2649 end
2650
2651 var cl = v.expr(self.n_expr, null)
2652 var it_meth = self.method_iterator
2653 assert it_meth != null
2654 var it = v.send(it_meth, [cl])
2655 assert it != null
2656 v.add("for(;;) \{")
2657 var isok_meth = self.method_is_ok
2658 assert isok_meth != null
2659 var ok = v.send(isok_meth, [it])
2660 assert ok != null
2661 v.add("if(!{ok}) break;")
2662 if self.variables.length == 1 then
2663 var item_meth = self.method_item
2664 assert item_meth != null
2665 var i = v.send(item_meth, [it])
2666 assert i != null
2667 v.assign(v.variable(variables.first), i)
2668 else if self.variables.length == 2 then
2669 var key_meth = self.method_key
2670 assert key_meth != null
2671 var i = v.send(key_meth, [it])
2672 assert i != null
2673 v.assign(v.variable(variables[0]), i)
2674 var item_meth = self.method_item
2675 assert item_meth != null
2676 i = v.send(item_meth, [it])
2677 assert i != null
2678 v.assign(v.variable(variables[1]), i)
2679 else
2680 abort
2681 end
2682 v.stmt(self.n_block)
2683 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2684 var next_meth = self.method_next
2685 assert next_meth != null
2686 v.send(next_meth, [it])
2687 v.add("\}")
2688 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2689 end
2690 end
2691
2692 redef class AAssertExpr
2693 redef fun stmt(v)
2694 do
2695 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2696
2697 var cond = v.expr_bool(self.n_expr)
2698 v.add("if (!{cond}) \{")
2699 v.stmt(self.n_else)
2700 var nid = self.n_id
2701 if nid != null then
2702 v.add_abort("Assert '{nid.text}' failed")
2703 else
2704 v.add_abort("Assert failed")
2705 end
2706 v.add("\}")
2707 end
2708 end
2709
2710 redef class AOrExpr
2711 redef fun expr(v)
2712 do
2713 var res = v.new_var(self.mtype.as(not null))
2714 var i1 = v.expr_bool(self.n_expr)
2715 v.add("if ({i1}) \{")
2716 v.add("{res} = 1;")
2717 v.add("\} else \{")
2718 var i2 = v.expr_bool(self.n_expr2)
2719 v.add("{res} = {i2};")
2720 v.add("\}")
2721 return res
2722 end
2723 end
2724
2725 redef class AAndExpr
2726 redef fun expr(v)
2727 do
2728 var res = v.new_var(self.mtype.as(not null))
2729 var i1 = v.expr_bool(self.n_expr)
2730 v.add("if (!{i1}) \{")
2731 v.add("{res} = 0;")
2732 v.add("\} else \{")
2733 var i2 = v.expr_bool(self.n_expr2)
2734 v.add("{res} = {i2};")
2735 v.add("\}")
2736 return res
2737 end
2738 end
2739
2740 redef class ANotExpr
2741 redef fun expr(v)
2742 do
2743 var cond = v.expr_bool(self.n_expr)
2744 return v.new_expr("!{cond}", self.mtype.as(not null))
2745 end
2746 end
2747
2748 redef class AOrElseExpr
2749 redef fun expr(v)
2750 do
2751 var res = v.new_var(self.mtype.as(not null))
2752 var i1 = v.expr(self.n_expr, null)
2753 v.add("if ({i1}!=NULL) \{")
2754 v.assign(res, i1)
2755 v.add("\} else \{")
2756 var i2 = v.expr(self.n_expr2, null)
2757 v.assign(res, i2)
2758 v.add("\}")
2759 return res
2760 end
2761 end
2762
2763 redef class AEeExpr
2764 redef fun expr(v)
2765 do
2766 var value1 = v.expr(self.n_expr, null)
2767 var value2 = v.expr(self.n_expr2, null)
2768 return v.equal_test(value1, value2)
2769 end
2770 end
2771
2772 redef class AIntExpr
2773 redef fun expr(v)
2774 do
2775 return v.new_expr("{self.n_number.text}", self.mtype.as(not null))
2776 end
2777 end
2778
2779 redef class AFloatExpr
2780 redef fun expr(v)
2781 do
2782 return v.new_expr("{self.n_float.text}", self.mtype.as(not null))
2783 end
2784 end
2785
2786 redef class ACharExpr
2787 redef fun expr(v)
2788 do
2789 return v.new_expr("{self.n_char.text}", self.mtype.as(not null))
2790 end
2791 end
2792
2793 redef class AArrayExpr
2794 redef fun expr(v)
2795 do
2796 var mtype = self.mtype.as(MClassType).arguments.first
2797 var array = new Array[RuntimeVariable]
2798 for nexpr in self.n_exprs.n_exprs do
2799 var i = v.expr(nexpr, mtype)
2800 array.add(i)
2801 end
2802 return v.array_instance(array, mtype)
2803 end
2804 end
2805
2806 redef class AStringFormExpr
2807 redef fun expr(v)
2808 do
2809 return v.string_instance(self.value.as(not null))
2810 end
2811 end
2812
2813 redef class ASuperstringExpr
2814 redef fun expr(v)
2815 do
2816 var array = new Array[RuntimeVariable]
2817 for ne in self.n_exprs do
2818 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2819 var i = v.expr(ne, null)
2820 array.add(i)
2821 end
2822 var a = v.array_instance(array, v.object_type)
2823 var res = v.send(v.get_property("to_s", a.mtype), [a])
2824 return res
2825 end
2826 end
2827
2828 redef class ACrangeExpr
2829 redef fun expr(v)
2830 do
2831 var i1 = v.expr(self.n_expr, null)
2832 var i2 = v.expr(self.n_expr2, null)
2833 var mtype = self.mtype.as(MClassType)
2834 var res = v.init_instance(mtype)
2835 var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
2836 v.check_init_instance(res, mtype)
2837 return res
2838 end
2839 end
2840
2841 redef class AOrangeExpr
2842 redef fun expr(v)
2843 do
2844 var i1 = v.expr(self.n_expr, null)
2845 var i2 = v.expr(self.n_expr2, null)
2846 var mtype = self.mtype.as(MClassType)
2847 var res = v.init_instance(mtype)
2848 var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
2849 v.check_init_instance(res, mtype)
2850 return res
2851 end
2852 end
2853
2854 redef class ATrueExpr
2855 redef fun expr(v)
2856 do
2857 return v.new_expr("1", self.mtype.as(not null))
2858 end
2859 end
2860
2861 redef class AFalseExpr
2862 redef fun expr(v)
2863 do
2864 return v.new_expr("0", self.mtype.as(not null))
2865 end
2866 end
2867
2868 redef class ANullExpr
2869 redef fun expr(v)
2870 do
2871 var res = v.new_expr("NULL", self.mtype.as(not null))
2872 return res
2873 end
2874 end
2875
2876 redef class AIsaExpr
2877 redef fun expr(v)
2878 do
2879 var i = v.expr(self.n_expr, null)
2880 return v.type_test(i, self.cast_type.as(not null), "isa")
2881 end
2882 end
2883
2884 redef class AAsCastExpr
2885 redef fun expr(v)
2886 do
2887 var i = v.expr(self.n_expr, null)
2888 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2889
2890 var cond = v.type_test(i, self.mtype.as(not null), "as")
2891 v.add("if (!{cond}) \{")
2892 v.add_abort("Cast failed")
2893 v.add("\}")
2894 return i
2895 end
2896 end
2897
2898 redef class AAsNotnullExpr
2899 redef fun expr(v)
2900 do
2901 var i = v.expr(self.n_expr, null)
2902 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2903
2904 v.add("if ({i} == NULL) \{")
2905 v.add_abort("Cast failed")
2906 v.add("\}")
2907 return i
2908 end
2909 end
2910
2911 redef class AParExpr
2912 redef fun expr(v)
2913 do
2914 return v.expr(self.n_expr, null)
2915 end
2916 end
2917
2918 redef class AOnceExpr
2919 redef fun expr(v)
2920 do
2921 var mtype = self.mtype.as(not null)
2922 var name = v.get_name("varonce")
2923 var guard = v.get_name(name + "_guard")
2924 v.add_decl("static {mtype.ctype} {name};")
2925 v.add_decl("static int {guard};")
2926 var res = v.new_var(mtype)
2927 v.add("if ({guard}) \{")
2928 v.add("{res} = {name};")
2929 v.add("\} else \{")
2930 var i = v.expr(self.n_expr, mtype)
2931 v.add("{res} = {i};")
2932 v.add("{name} = {res};")
2933 v.add("{guard} = 1;")
2934 v.add("\}")
2935 return res
2936 end
2937 end
2938
2939 redef class ASendExpr
2940 redef fun expr(v)
2941 do
2942 var recv = v.expr(self.n_expr, null)
2943 var args = [recv]
2944 for a in self.raw_arguments.as(not null) do
2945 args.add(v.expr(a, null))
2946 end
2947 return v.compile_callsite(self.callsite.as(not null), args)
2948 end
2949 end
2950
2951 redef class ASendReassignFormExpr
2952 redef fun stmt(v)
2953 do
2954 var recv = v.expr(self.n_expr, null)
2955 var args = [recv]
2956 for a in self.raw_arguments.as(not null) do
2957 args.add(v.expr(a, null))
2958 end
2959 var value = v.expr(self.n_value, null)
2960
2961 var left = v.compile_callsite(self.callsite.as(not null), args)
2962 assert left != null
2963
2964 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2965 assert res != null
2966
2967 args.add(res)
2968 v.compile_callsite(self.write_callsite.as(not null), args)
2969 end
2970 end
2971
2972 redef class ASuperExpr
2973 redef fun expr(v)
2974 do
2975 var recv = v.frame.arguments.first
2976 var args = [recv]
2977 for a in self.n_args.n_exprs do
2978 args.add(v.expr(a, null))
2979 end
2980 if args.length == 1 then
2981 args = v.frame.arguments
2982 end
2983
2984 var mproperty = self.mproperty
2985 if mproperty != null then
2986 if mproperty.intro.msignature.arity == 0 then
2987 args = [recv]
2988 end
2989 # Super init call
2990 var res = v.send(mproperty, args)
2991 return res
2992 end
2993
2994 # stantard call-next-method
2995 var mpropdef = v.frame.mpropdef
2996 # FIXME: we do not want an ugly static call!
2997 var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
2998 if mpropdefs.length != 1 then
2999 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
3000 debug("MPRODFEFS for super {mpropdef} for {recv}: {mpropdefs.join(", ")}")
3001 end
3002 mpropdef = mpropdefs.first
3003 assert mpropdef isa MMethodDef
3004 var res = v.call(mpropdef, recv.mtype.as(MClassType), args)
3005 return res
3006 end
3007 end
3008
3009 redef class ANewExpr
3010 redef fun expr(v)
3011 do
3012 var mtype = self.mtype.as(MClassType)
3013 var recv
3014 var ctype = mtype.ctype
3015 if ctype == "val*" then
3016 recv = v.init_instance(mtype)
3017 else if ctype == "void*" then
3018 recv = v.new_expr("NULL/*special!*/", mtype)
3019 else
3020 debug("cannot new {mtype}")
3021 abort
3022 end
3023 var args = [recv]
3024 for a in self.n_args.n_exprs do
3025 args.add(v.expr(a, null))
3026 end
3027 var res2 = v.compile_callsite(self.callsite.as(not null), args)
3028 if res2 != null then
3029 #self.debug("got {res2} from {mproperty}. drop {recv}")
3030 return res2
3031 end
3032 v.check_init_instance(recv, mtype)
3033 return recv
3034 end
3035 end
3036
3037 redef class AAttrExpr
3038 redef fun expr(v)
3039 do
3040 var recv = v.expr(self.n_expr, null)
3041 var mproperty = self.mproperty.as(not null)
3042 return v.read_attribute(mproperty, recv)
3043 end
3044 end
3045
3046 redef class AAttrAssignExpr
3047 redef fun stmt(v)
3048 do
3049 var recv = v.expr(self.n_expr, null)
3050 var i = v.expr(self.n_value, null)
3051 var mproperty = self.mproperty.as(not null)
3052 v.write_attribute(mproperty, recv, i)
3053 end
3054 end
3055
3056 redef class AAttrReassignExpr
3057 redef fun stmt(v)
3058 do
3059 var recv = v.expr(self.n_expr, null)
3060 var value = v.expr(self.n_value, null)
3061 var mproperty = self.mproperty.as(not null)
3062 var attr = v.read_attribute(mproperty, recv)
3063 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3064 assert res != null
3065 v.write_attribute(mproperty, recv, res)
3066 end
3067 end
3068
3069 redef class AIssetAttrExpr
3070 redef fun expr(v)
3071 do
3072 var recv = v.expr(self.n_expr, null)
3073 var mproperty = self.mproperty.as(not null)
3074 return v.isset_attribute(mproperty, recv)
3075 end
3076 end
3077
3078 redef class ADebugTypeExpr
3079 redef fun stmt(v)
3080 do
3081 # do nothing
3082 end
3083 end