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