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