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