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