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