53ccced746ebfbf23250b411a3a43ab22a188931
[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 nat = self.new_var(self.get_class("NativeString").mclass_type)
811 self.add("{nat} = \"{string.escape_to_c}\";")
812 var res2 = self.init_instance(mtype)
813 self.add("{res} = {res2};")
814 var length = self.int_instance(string.length)
815 self.send(self.get_property("with_native", mtype), [res, nat, length])
816 self.check_init_instance(res, mtype)
817 self.add("{name} = {res};")
818 self.add("\}")
819 return res
820 end
821
822 # Generate an array value
823 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
824
825 # Get an instance of a array for a vararg
826 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
827
828 # Code generation
829
830 # Add a line in the main part of the generated C
831 fun add(s: String) do self.writer.lines.add(s)
832
833 # Add a line in the
834 # (used for local or global declaration)
835 fun add_decl(s: String) do self.writer.decl_lines.add(s)
836
837 # Request the presence of a global declaration
838 fun require_declaration(key: String)
839 do
840 self.writer.file.required_declarations.add(key)
841 end
842
843 # Add a declaration in the local-header
844 # The declaration is ensured to be present once
845 fun declare_once(s: String)
846 do
847 self.compiler.provide_declaration(s, s)
848 self.require_declaration(s)
849 end
850
851 # look for a needed .h and .c file for a given .nit source-file
852 # FIXME: bad API, parameter should be a `MModule`, not its source-file
853 fun add_extern(file: String)
854 do
855 file = file.strip_extension(".nit")
856 var tryfile = file + ".nit.h"
857 if tryfile.file_exists then
858 self.declare_once("#include \"{"..".join_path(tryfile)}\"")
859 end
860 tryfile = file + "_nit.h"
861 if tryfile.file_exists then
862 self.declare_once("#include \"{"..".join_path(tryfile)}\"")
863 end
864
865 if self.compiler.seen_extern.has(file) then return
866 self.compiler.seen_extern.add(file)
867 tryfile = file + ".nit.c"
868 if not tryfile.file_exists then
869 tryfile = file + "_nit.c"
870 if not tryfile.file_exists then return
871 end
872 var f = new ExternCFile(tryfile, "")
873 self.compiler.extern_bodies.add(f)
874 end
875
876 # Return a new local runtime_variable initialized with the C expression `cexpr`.
877 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
878 do
879 var res = new_var(mtype)
880 self.add("{res} = {cexpr};")
881 return res
882 end
883
884 # Generate generic abort
885 # used by aborts, asserts, casts, etc.
886 fun add_abort(message: String)
887 do
888 if self.current_node != null and self.current_node.location.file != null then
889 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});")
890 else
891 self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
892 end
893 self.add("exit(1);")
894 end
895
896 # Generate a return with the value `s`
897 fun ret(s: RuntimeVariable)
898 do
899 self.assign(self.frame.returnvar.as(not null), s)
900 self.add("goto {self.frame.returnlabel.as(not null)};")
901 end
902
903 # Compile a statement (if any)
904 fun stmt(nexpr: nullable AExpr)
905 do
906 if nexpr == null then return
907 var old = self.current_node
908 self.current_node = nexpr
909 nexpr.stmt(self)
910 self.current_node = old
911 end
912
913 # Compile an expression an return its result
914 # `mtype` is the expected return type, pass null if no specific type is expected.
915 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
916 do
917 var old = self.current_node
918 self.current_node = nexpr
919 var res = nexpr.expr(self).as(not null)
920 if mtype != null then
921 mtype = self.anchor(mtype)
922 res = self.autobox(res, mtype)
923 end
924 res = autoadapt(res, nexpr.mtype.as(not null))
925 var implicit_cast_to = nexpr.implicit_cast_to
926 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
927 var castres = self.type_test(res, implicit_cast_to, "auto")
928 self.add("if (!{castres}) \{")
929 self.add_abort("Cast failed")
930 self.add("\}")
931 res = autoadapt(res, implicit_cast_to)
932 end
933 self.current_node = old
934 return res
935 end
936
937 # Alias for `self.expr(nexpr, self.bool_type)`
938 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
939
940 # Safely show a debug message on the current node and repeat the message in the C code as a comment
941 fun debug(message: String)
942 do
943 var node = self.current_node
944 if node == null then
945 print "?: {message}"
946 else
947 node.debug(message)
948 end
949 self.add("/* DEBUG: {message} */")
950 end
951 end
952
953 # A C function associated to a Nit method
954 # Because of customization, a given Nit method can be compiler more that once
955 abstract class AbstractRuntimeFunction
956
957 type COMPILER: AbstractCompiler
958 type VISITOR: AbstractCompilerVisitor
959
960 # The associated Nit method
961 var mmethoddef: MMethodDef
962
963 # The mangled c name of the runtime_function
964 # Subclasses should redefine `build_c_name` instead
965 fun c_name: String
966 do
967 var res = self.c_name_cache
968 if res != null then return res
969 res = self.build_c_name
970 self.c_name_cache = res
971 return res
972 end
973
974 # Non cached version of `c_name`
975 protected fun build_c_name: String is abstract
976
977 protected var c_name_cache: nullable String writable = null
978
979 # Implements a call of the runtime_function
980 # May inline the body or generate a C function call
981 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
982
983 # Generate the code for the `AbstractRuntimeFunction`
984 # Warning: compile more than once compilation makes CC unhappy
985 fun compile_to_c(compiler: COMPILER) is abstract
986 end
987
988 # A runtime variable hold a runtime value in C.
989 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
990 #
991 # 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.
992 class RuntimeVariable
993 # The name of the variable in the C code
994 var name: String
995
996 # The static type of the variable (as declard in C)
997 var mtype: MType
998
999 # The current casted type of the variable (as known in Nit)
1000 var mcasttype: MType writable
1001
1002 # If the variable exaclty a mcasttype?
1003 # false (usual value) means that the variable is a mcasttype or a subtype.
1004 var is_exact: Bool writable = false
1005
1006 init(name: String, mtype: MType, mcasttype: MType)
1007 do
1008 self.name = name
1009 self.mtype = mtype
1010 self.mcasttype = mcasttype
1011 assert not mtype.need_anchor
1012 assert not mcasttype.need_anchor
1013 end
1014
1015 redef fun to_s do return name
1016
1017 redef fun inspect
1018 do
1019 var exact_str
1020 if self.is_exact then
1021 exact_str = " exact"
1022 else
1023 exact_str = ""
1024 end
1025 var type_str
1026 if self.mtype == self.mcasttype then
1027 type_str = "{mtype}{exact_str}"
1028 else
1029 type_str = "{mtype}({mcasttype}{exact_str})"
1030 end
1031 return "<{name}:{type_str}>"
1032 end
1033 end
1034
1035 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1036 class Frame
1037
1038 type VISITOR: AbstractCompilerVisitor
1039
1040 # The associated visitor
1041 var visitor: VISITOR
1042
1043 # The executed property.
1044 # A Method in case of a call, an attribute in case of a default initialization.
1045 var mpropdef: MPropDef
1046
1047 # The static type of the receiver
1048 var receiver: MClassType
1049
1050 # Arguments of the method (the first is the receiver)
1051 var arguments: Array[RuntimeVariable]
1052
1053 # The runtime_variable associated to the return (in a function)
1054 var returnvar: nullable RuntimeVariable writable = null
1055
1056 # The label at the end of the property
1057 var returnlabel: nullable String writable = null
1058 end
1059
1060 # An extern C file to compile
1061 class ExternCFile
1062 # The filename of the file
1063 var filename: String
1064 # Additionnal specific CC compiler -c flags
1065 var cflags: String
1066 end
1067
1068 redef class String
1069 # Mangle a string to be a unique valid C identifier
1070 fun to_cmangle: String
1071 do
1072 var res = new Buffer
1073 var underscore = false
1074 for c in self do
1075 if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
1076 res.add(c)
1077 underscore = false
1078 continue
1079 end
1080 if underscore then
1081 res.append('_'.ascii.to_s)
1082 res.add('d')
1083 end
1084 if c >= '0' and c <= '9' then
1085 res.add(c)
1086 underscore = false
1087 else if c == '_' then
1088 res.add(c)
1089 underscore = true
1090 else
1091 res.add('_')
1092 res.append(c.ascii.to_s)
1093 res.add('d')
1094 underscore = false
1095 end
1096 end
1097 return res.to_s
1098 end
1099
1100 # Escape " \ ' and non printable characters for literal C strings or characters
1101 fun escape_to_c: String
1102 do
1103 var b = new Buffer
1104 for c in self do
1105 if c == '\n' then
1106 b.append("\\n")
1107 else if c == '\0' then
1108 b.append("\\0")
1109 else if c == '"' then
1110 b.append("\\\"")
1111 else if c == '\'' then
1112 b.append("\\\'")
1113 else if c == '\\' then
1114 b.append("\\\\")
1115 else if c.ascii < 32 then
1116 b.append("\\{c.ascii.to_base(8, false)}")
1117 else
1118 b.add(c)
1119 end
1120 end
1121 return b.to_s
1122 end
1123 end
1124
1125 redef class MType
1126 # Return the C type associated to a given Nit static type
1127 fun ctype: String do return "val*"
1128
1129 fun ctypename: String do return "val"
1130
1131 # Return the name of the C structure associated to a Nit live type
1132 fun c_name: String is abstract
1133 protected var c_name_cache: nullable String protected writable
1134 end
1135
1136 redef class MClassType
1137 redef fun c_name
1138 do
1139 var res = self.c_name_cache
1140 if res != null then return res
1141 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1142 self.c_name_cache = res
1143 return res
1144 end
1145
1146 redef fun ctype: String
1147 do
1148 if mclass.name == "Int" then
1149 return "long"
1150 else if mclass.name == "Bool" then
1151 return "short int"
1152 else if mclass.name == "Char" then
1153 return "char"
1154 else if mclass.name == "Float" then
1155 return "double"
1156 else if mclass.name == "NativeString" then
1157 return "char*"
1158 else if mclass.name == "NativeArray" then
1159 return "val*"
1160 else if mclass.kind == extern_kind then
1161 return "void*"
1162 else
1163 return "val*"
1164 end
1165 end
1166
1167 redef fun ctypename: String
1168 do
1169 if mclass.name == "Int" then
1170 return "l"
1171 else if mclass.name == "Bool" then
1172 return "s"
1173 else if mclass.name == "Char" then
1174 return "c"
1175 else if mclass.name == "Float" then
1176 return "d"
1177 else if mclass.name == "NativeString" then
1178 return "str"
1179 else if mclass.name == "NativeArray" then
1180 #return "{self.arguments.first.ctype}*"
1181 return "val"
1182 else if mclass.kind == extern_kind then
1183 return "ptr"
1184 else
1185 return "val"
1186 end
1187 end
1188 end
1189
1190 redef class MGenericType
1191 redef fun c_name
1192 do
1193 var res = self.c_name_cache
1194 if res != null then return res
1195 res = super
1196 for t in self.arguments do
1197 res = res + t.c_name
1198 end
1199 self.c_name_cache = res
1200 return res
1201 end
1202 end
1203
1204 redef class MParameterType
1205 redef fun c_name
1206 do
1207 var res = self.c_name_cache
1208 if res != null then return res
1209 res = "{self.mclass.c_name}_FT{self.rank}"
1210 self.c_name_cache = res
1211 return res
1212 end
1213 end
1214
1215 redef class MVirtualType
1216 redef fun c_name
1217 do
1218 var res = self.c_name_cache
1219 if res != null then return res
1220 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1221 self.c_name_cache = res
1222 return res
1223 end
1224 end
1225
1226 redef class MNullableType
1227 redef fun c_name
1228 do
1229 var res = self.c_name_cache
1230 if res != null then return res
1231 res = "nullable_{self.mtype.c_name}"
1232 self.c_name_cache = res
1233 return res
1234 end
1235 end
1236
1237 redef class MClass
1238 # Return the name of the C structure associated to a Nit class
1239 fun c_name: String do
1240 var res = self.c_name_cache
1241 if res != null then return res
1242 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1243 self.c_name_cache = res
1244 return res
1245 end
1246 private var c_name_cache: nullable String
1247 end
1248
1249 redef class MProperty
1250 fun c_name: String do
1251 var res = self.c_name_cache
1252 if res != null then return res
1253 res = "{self.intro.c_name}"
1254 self.c_name_cache = res
1255 return res
1256 end
1257 private var c_name_cache: nullable String
1258 end
1259
1260 redef class MPropDef
1261 type VISITOR: AbstractCompilerVisitor
1262
1263 private var c_name_cache: nullable String
1264
1265 # The mangled name associated to the property
1266 fun c_name: String
1267 do
1268 var res = self.c_name_cache
1269 if res != null then return res
1270 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1271 self.c_name_cache = res
1272 return res
1273 end
1274 end
1275
1276 redef class MMethodDef
1277 # Can the body be inlined?
1278 fun can_inline(v: VISITOR): Bool
1279 do
1280 var modelbuilder = v.compiler.modelbuilder
1281 if modelbuilder.mpropdef2npropdef.has_key(self) then
1282 var npropdef = modelbuilder.mpropdef2npropdef[self]
1283 return npropdef.can_inline
1284 else if self.mproperty.name == "init" then
1285 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1286 return true
1287 else
1288 abort
1289 end
1290 end
1291
1292 # Inline the body in another visitor
1293 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1294 do
1295 var modelbuilder = v.compiler.modelbuilder
1296 if modelbuilder.mpropdef2npropdef.has_key(self) then
1297 var npropdef = modelbuilder.mpropdef2npropdef[self]
1298 var oldnode = v.current_node
1299 v.current_node = npropdef
1300 self.compile_parameter_check(v, arguments)
1301 npropdef.compile_to_c(v, self, arguments)
1302 v.current_node = oldnode
1303 else if self.mproperty.name == "init" then
1304 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1305 var oldnode = v.current_node
1306 v.current_node = nclassdef
1307 self.compile_parameter_check(v, arguments)
1308 nclassdef.compile_to_c(v, self, arguments)
1309 v.current_node = oldnode
1310 else
1311 abort
1312 end
1313 return null
1314 end
1315
1316 # Generate type checks in the C code to check covariant parameters
1317 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1318 do
1319 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1320
1321 for i in [0..msignature.arity[ do
1322 # skip test for vararg since the array is instantiated with the correct polymorphic type
1323 if msignature.vararg_rank == i then continue
1324
1325 # skip if the cast is not required
1326 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1327 if not origmtype.need_anchor then continue
1328
1329 # get the parameter type
1330 var mtype = self.msignature.mparameters[i].mtype
1331
1332 # generate the cast
1333 # note that v decides if and how to implements the cast
1334 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1335 var cond = v.type_test(arguments[i+1], mtype, "covariance")
1336 v.add("if (!{cond}) \{")
1337 v.add_abort("Cast failed")
1338 v.add("\}")
1339 end
1340 end
1341 end
1342
1343 # Node visit
1344
1345 redef class APropdef
1346 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1347 do
1348 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1349 debug("Not yet implemented")
1350 end
1351
1352 fun can_inline: Bool do return true
1353 end
1354
1355 redef class AConcreteMethPropdef
1356 redef fun compile_to_c(v, mpropdef, arguments)
1357 do
1358 for i in [0..mpropdef.msignature.arity[ do
1359 var variable = self.n_signature.n_params[i].variable.as(not null)
1360 v.assign(v.variable(variable), arguments[i+1])
1361 end
1362 # Call the implicit super-init
1363 var auto_super_inits = self.auto_super_inits
1364 if auto_super_inits != null then
1365 var selfarg = [arguments.first]
1366 for auto_super_init in auto_super_inits do
1367 if auto_super_init.intro.msignature.arity == 0 then
1368 v.send(auto_super_init, selfarg)
1369 else
1370 v.send(auto_super_init, arguments)
1371 end
1372 end
1373 end
1374 v.stmt(self.n_block)
1375 end
1376
1377 redef fun can_inline
1378 do
1379 if self.auto_super_inits != null then return false
1380 var nblock = self.n_block
1381 if nblock == null then return true
1382 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1383 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1384 return false
1385 end
1386 end
1387
1388 redef class AInternMethPropdef
1389 redef fun compile_to_c(v, mpropdef, arguments)
1390 do
1391 var pname = mpropdef.mproperty.name
1392 var cname = mpropdef.mclassdef.mclass.name
1393 var ret = mpropdef.msignature.return_mtype
1394 if ret != null then
1395 ret = v.resolve_for(ret, arguments.first)
1396 end
1397 if pname != "==" and pname != "!=" then
1398 v.adapt_signature(mpropdef, arguments)
1399 end
1400 if cname == "Int" then
1401 if pname == "output" then
1402 v.add("printf(\"%ld\\n\", {arguments.first});")
1403 return
1404 else if pname == "object_id" then
1405 v.ret(arguments.first)
1406 return
1407 else if pname == "+" then
1408 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1409 return
1410 else if pname == "-" then
1411 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1412 return
1413 else if pname == "unary -" then
1414 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1415 return
1416 else if pname == "succ" then
1417 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1418 return
1419 else if pname == "prec" then
1420 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1421 return
1422 else if pname == "*" then
1423 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1424 return
1425 else if pname == "/" then
1426 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1427 return
1428 else if pname == "%" then
1429 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1430 return
1431 else if pname == "lshift" then
1432 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1433 return
1434 else if pname == "rshift" then
1435 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1436 return
1437 else if pname == "==" then
1438 v.ret(v.equal_test(arguments[0], arguments[1]))
1439 return
1440 else if pname == "!=" then
1441 var res = v.equal_test(arguments[0], arguments[1])
1442 v.ret(v.new_expr("!{res}", ret.as(not null)))
1443 return
1444 else if pname == "<" then
1445 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1446 return
1447 else if pname == ">" then
1448 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1449 return
1450 else if pname == "<=" then
1451 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1452 return
1453 else if pname == ">=" then
1454 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1455 return
1456 else if pname == "to_f" then
1457 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1458 return
1459 else if pname == "ascii" then
1460 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1461 return
1462 end
1463 else if cname == "Char" then
1464 if pname == "output" then
1465 v.add("printf(\"%c\", {arguments.first});")
1466 return
1467 else if pname == "object_id" then
1468 v.ret(arguments.first)
1469 return
1470 else if pname == "+" then
1471 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1472 return
1473 else if pname == "-" then
1474 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1475 return
1476 else if pname == "==" then
1477 v.ret(v.equal_test(arguments[0], arguments[1]))
1478 return
1479 else if pname == "!=" then
1480 var res = v.equal_test(arguments[0], arguments[1])
1481 v.ret(v.new_expr("!{res}", ret.as(not null)))
1482 return
1483 else if pname == "succ" then
1484 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1485 return
1486 else if pname == "prec" then
1487 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1488 return
1489 else if pname == "<" then
1490 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1491 return
1492 else if pname == ">" then
1493 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1494 return
1495 else if pname == "<=" then
1496 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1497 return
1498 else if pname == ">=" then
1499 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1500 return
1501 else if pname == "to_i" then
1502 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1503 return
1504 else if pname == "ascii" then
1505 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1506 return
1507 end
1508 else if cname == "Bool" then
1509 if pname == "output" then
1510 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1511 return
1512 else if pname == "object_id" then
1513 v.ret(arguments.first)
1514 return
1515 else if pname == "==" then
1516 v.ret(v.equal_test(arguments[0], arguments[1]))
1517 return
1518 else if pname == "!=" then
1519 var res = v.equal_test(arguments[0], arguments[1])
1520 v.ret(v.new_expr("!{res}", ret.as(not null)))
1521 return
1522 end
1523 else if cname == "Float" then
1524 if pname == "output" then
1525 v.add("printf(\"%f\\n\", {arguments.first});")
1526 return
1527 else if pname == "object_id" then
1528 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1529 return
1530 else if pname == "+" then
1531 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1532 return
1533 else if pname == "-" then
1534 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1535 return
1536 else if pname == "unary -" then
1537 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1538 return
1539 else if pname == "succ" then
1540 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1541 return
1542 else if pname == "prec" then
1543 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
1544 return
1545 else if pname == "*" then
1546 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1547 return
1548 else if pname == "/" then
1549 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1550 return
1551 else if pname == "==" then
1552 v.ret(v.equal_test(arguments[0], arguments[1]))
1553 return
1554 else if pname == "!=" then
1555 var res = v.equal_test(arguments[0], arguments[1])
1556 v.ret(v.new_expr("!{res}", ret.as(not null)))
1557 return
1558 else if pname == "<" then
1559 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1560 return
1561 else if pname == ">" then
1562 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1563 return
1564 else if pname == "<=" then
1565 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1566 return
1567 else if pname == ">=" then
1568 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1569 return
1570 else if pname == "to_i" then
1571 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1572 return
1573 end
1574 else if cname == "Char" then
1575 if pname == "output" then
1576 v.add("printf(\"%c\", {arguments.first});")
1577 return
1578 else if pname == "object_id" then
1579 v.ret(arguments.first)
1580 return
1581 else if pname == "==" then
1582 v.ret(v.equal_test(arguments[0], arguments[1]))
1583 return
1584 else if pname == "!=" then
1585 var res = v.equal_test(arguments[0], arguments[1])
1586 v.ret(v.new_expr("!{res}", ret.as(not null)))
1587 return
1588 else if pname == "ascii" then
1589 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1590 return
1591 end
1592 else if cname == "NativeString" then
1593 if pname == "[]" then
1594 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1595 return
1596 else if pname == "[]=" then
1597 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1598 return
1599 else if pname == "copy_to" then
1600 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1601 return
1602 else if pname == "atoi" then
1603 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1604 return
1605 end
1606 else if cname == "NativeArray" then
1607 v.native_array_def(pname, ret, arguments)
1608 return
1609 end
1610 if pname == "exit" then
1611 v.add("exit({arguments[1]});")
1612 return
1613 else if pname == "sys" then
1614 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1615 return
1616 else if pname == "calloc_string" then
1617 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1618 return
1619 else if pname == "calloc_array" then
1620 v.calloc_array(ret.as(not null), arguments)
1621 return
1622 else if pname == "object_id" then
1623 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1624 return
1625 else if pname == "is_same_type" then
1626 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1627 return
1628 else if pname == "output_class_name" then
1629 var nat = v.class_name_string(arguments.first)
1630 v.add("printf(\"%s\\n\", {nat});")
1631 return
1632 else if pname == "native_class_name" then
1633 var nat = v.class_name_string(arguments.first)
1634 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1635 return
1636 else if pname == "force_garbage_collection" then
1637 v.add("nit_gcollect();")
1638 return
1639 else if pname == "native_argc" then
1640 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1641 return
1642 else if pname == "native_argv" then
1643 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1644 return
1645 end
1646 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1647 debug("Not implemented {mpropdef}")
1648 end
1649 end
1650
1651 redef class AExternMethPropdef
1652 redef fun compile_to_c(v, mpropdef, arguments)
1653 do
1654 var externname
1655 var nextern = self.n_extern
1656 if nextern == null then
1657 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1658 v.add("exit(1);")
1659 return
1660 end
1661 externname = nextern.text.substring(1, nextern.text.length-2)
1662 if location.file != null then
1663 var file = location.file.filename
1664 v.add_extern(file)
1665 end
1666 var res: nullable RuntimeVariable = null
1667 var ret = mpropdef.msignature.return_mtype
1668 if ret != null then
1669 ret = v.resolve_for(ret, arguments.first)
1670 res = v.new_var(ret)
1671 end
1672 v.adapt_signature(mpropdef, arguments)
1673
1674 if res == null then
1675 v.add("{externname}({arguments.join(", ")});")
1676 else
1677 v.add("{res} = {externname}({arguments.join(", ")});")
1678 v.ret(res)
1679 end
1680 end
1681 end
1682
1683 redef class AExternInitPropdef
1684 redef fun compile_to_c(v, mpropdef, arguments)
1685 do
1686 var externname
1687 var nextern = self.n_extern
1688 if nextern == null then
1689 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1690 v.add("exit(1);")
1691 return
1692 end
1693 externname = nextern.text.substring(1, nextern.text.length-2)
1694 if location.file != null then
1695 var file = location.file.filename
1696 v.add_extern(file)
1697 end
1698 v.adapt_signature(mpropdef, arguments)
1699 var ret = arguments.first.mtype
1700 var res = v.new_var(ret)
1701
1702 arguments.shift
1703
1704 v.add("{res} = {externname}({arguments.join(", ")});")
1705 v.ret(res)
1706 end
1707 end
1708
1709 redef class AAttrPropdef
1710 redef fun compile_to_c(v, mpropdef, arguments)
1711 do
1712 if arguments.length == 1 then
1713 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1714 v.assign(v.frame.returnvar.as(not null), res)
1715 else
1716 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1717 end
1718 end
1719
1720 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1721 do
1722 var nexpr = self.n_expr
1723 if nexpr != null then
1724 var oldnode = v.current_node
1725 v.current_node = self
1726 var old_frame = v.frame
1727 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1728 v.frame = frame
1729 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1730 v.write_attribute(self.mpropdef.mproperty, recv, value)
1731 v.frame = old_frame
1732 v.current_node = oldnode
1733 end
1734 end
1735
1736 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1737 do
1738 var nexpr = self.n_expr
1739 if nexpr != null then return
1740
1741 var oldnode = v.current_node
1742 v.current_node = self
1743 var old_frame = v.frame
1744 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1745 v.frame = frame
1746 # Force read to check the initialization
1747 v.read_attribute(self.mpropdef.mproperty, recv)
1748 v.frame = old_frame
1749 v.current_node = oldnode
1750 end
1751 end
1752
1753 redef class AClassdef
1754 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1755 do
1756 if mpropdef == self.mfree_init then
1757 var super_inits = self.super_inits
1758 if super_inits != null then
1759 assert arguments.length == 1
1760 for su in super_inits do
1761 v.send(su, arguments)
1762 end
1763 return
1764 end
1765 var recv = arguments.first
1766 var i = 1
1767 # Collect undefined attributes
1768 for npropdef in self.n_propdefs do
1769 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
1770 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
1771 i += 1
1772 end
1773 end
1774 else
1775 abort
1776 end
1777 end
1778 end
1779
1780 redef class ADeferredMethPropdef
1781 redef fun compile_to_c(v, mpropdef, arguments) do v.add_abort("Deferred method called")
1782 redef fun can_inline do return true
1783 end
1784
1785 redef class AExpr
1786 # Try to compile self as an expression
1787 # Do not call this method directly, use `v.expr` instead
1788 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
1789 do
1790 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
1791 var mtype = self.mtype
1792 if mtype == null then
1793 return null
1794 else
1795 var res = v.new_var(mtype)
1796 v.add("/* {res} = NOT YET {class_name} */")
1797 return res
1798 end
1799 end
1800
1801 # Try to compile self as a statement
1802 # Do not call this method directly, use `v.stmt` instead
1803 private fun stmt(v: AbstractCompilerVisitor)
1804 do
1805 var res = expr(v)
1806 if res != null then v.add("{res};")
1807 end
1808 end
1809
1810 redef class ABlockExpr
1811 redef fun stmt(v)
1812 do
1813 for e in self.n_expr do v.stmt(e)
1814 end
1815 redef fun expr(v)
1816 do
1817 var last = self.n_expr.last
1818 for e in self.n_expr do
1819 if e == last then break
1820 v.stmt(e)
1821 end
1822 return v.expr(last, null)
1823 end
1824 end
1825
1826 redef class AVardeclExpr
1827 redef fun stmt(v)
1828 do
1829 var variable = self.variable.as(not null)
1830 var ne = self.n_expr
1831 if ne != null then
1832 var i = v.expr(ne, variable.declared_type)
1833 v.assign(v.variable(variable), i)
1834 end
1835 end
1836 end
1837
1838 redef class AVarExpr
1839 redef fun expr(v)
1840 do
1841 var res = v.variable(self.variable.as(not null))
1842 var mtype = self.mtype.as(not null)
1843 return v.autoadapt(res, mtype)
1844 end
1845 end
1846
1847 redef class AVarAssignExpr
1848 redef fun stmt(v)
1849 do
1850 var variable = self.variable.as(not null)
1851 var i = v.expr(self.n_value, variable.declared_type)
1852 v.assign(v.variable(variable), i)
1853 end
1854 redef fun expr(v)
1855 do
1856 var variable = self.variable.as(not null)
1857 var i = v.expr(self.n_value, variable.declared_type)
1858 v.assign(v.variable(variable), i)
1859 return i
1860 end
1861 end
1862
1863 redef class AVarReassignExpr
1864 redef fun stmt(v)
1865 do
1866 var variable = self.variable.as(not null)
1867 var vari = v.variable(variable)
1868 var value = v.expr(self.n_value, variable.declared_type)
1869 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
1870 assert res != null
1871 v.assign(v.variable(variable), res)
1872 end
1873 end
1874
1875 redef class ASelfExpr
1876 redef fun expr(v) do return v.frame.arguments.first
1877 end
1878
1879 redef class AContinueExpr
1880 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
1881 end
1882
1883 redef class ABreakExpr
1884 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
1885 end
1886
1887 redef class AReturnExpr
1888 redef fun stmt(v)
1889 do
1890 var nexpr = self.n_expr
1891 if nexpr != null then
1892 var returnvar = v.frame.returnvar.as(not null)
1893 var i = v.expr(nexpr, returnvar.mtype)
1894 v.assign(returnvar, i)
1895 end
1896 v.add("goto {v.frame.returnlabel.as(not null)};")
1897 end
1898 end
1899
1900 redef class AAbortExpr
1901 redef fun stmt(v) do v.add_abort("Aborted")
1902 end
1903
1904 redef class AIfExpr
1905 redef fun stmt(v)
1906 do
1907 var cond = v.expr_bool(self.n_expr)
1908 v.add("if ({cond})\{")
1909 v.stmt(self.n_then)
1910 v.add("\} else \{")
1911 v.stmt(self.n_else)
1912 v.add("\}")
1913 end
1914
1915 redef fun expr(v)
1916 do
1917 var res = v.new_var(self.mtype.as(not null))
1918 var cond = v.expr_bool(self.n_expr)
1919 v.add("if ({cond})\{")
1920 v.assign(res, v.expr(self.n_then.as(not null), null))
1921 v.add("\} else \{")
1922 v.assign(res, v.expr(self.n_else.as(not null), null))
1923 v.add("\}")
1924 return res
1925 end
1926 end
1927
1928 redef class AIfexprExpr
1929 redef fun expr(v)
1930 do
1931 var res = v.new_var(self.mtype.as(not null))
1932 var cond = v.expr_bool(self.n_expr)
1933 v.add("if ({cond})\{")
1934 v.assign(res, v.expr(self.n_then, null))
1935 v.add("\} else \{")
1936 v.assign(res, v.expr(self.n_else, null))
1937 v.add("\}")
1938 return res
1939 end
1940 end
1941
1942 redef class ADoExpr
1943 redef fun stmt(v)
1944 do
1945 v.stmt(self.n_block)
1946 var escapemark = self.escapemark
1947 if escapemark != null then
1948 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1949 end
1950 end
1951 end
1952
1953 redef class AWhileExpr
1954 redef fun stmt(v)
1955 do
1956 v.add("for(;;) \{")
1957 var cond = v.expr_bool(self.n_expr)
1958 v.add("if (!{cond}) break;")
1959 v.stmt(self.n_block)
1960 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1961 v.add("\}")
1962 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1963 end
1964 end
1965
1966 redef class ALoopExpr
1967 redef fun stmt(v)
1968 do
1969 v.add("for(;;) \{")
1970 v.stmt(self.n_block)
1971 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1972 v.add("\}")
1973 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1974 end
1975 end
1976
1977 redef class AForExpr
1978 redef fun stmt(v)
1979 do
1980 # Shortcut on explicit range
1981 # Avoid the instantiation of the range and the iterator
1982 var nexpr = self.n_expr
1983 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
1984 var from = v.expr(nexpr.n_expr, null)
1985 var to = v.expr(nexpr.n_expr2, null)
1986 var variable = v.variable(variables.first)
1987
1988 v.assign(variable, from)
1989 v.add("for(;;) \{ /* shortcut range */")
1990
1991 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
1992 assert ok != null
1993 v.add("if(!{ok}) break;")
1994
1995 v.stmt(self.n_block)
1996
1997 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1998 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
1999 assert succ != null
2000 v.assign(variable, succ)
2001 v.add("\}")
2002 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2003 return
2004 end
2005
2006 var cl = v.expr(self.n_expr, null)
2007 var it_meth = self.method_iterator
2008 assert it_meth != null
2009 var it = v.send(it_meth, [cl])
2010 assert it != null
2011 v.add("for(;;) \{")
2012 var isok_meth = self.method_is_ok
2013 assert isok_meth != null
2014 var ok = v.send(isok_meth, [it])
2015 assert ok != null
2016 v.add("if(!{ok}) break;")
2017 if self.variables.length == 1 then
2018 var item_meth = self.method_item
2019 assert item_meth != null
2020 var i = v.send(item_meth, [it])
2021 assert i != null
2022 v.assign(v.variable(variables.first), i)
2023 else if self.variables.length == 2 then
2024 var key_meth = self.method_key
2025 assert key_meth != null
2026 var i = v.send(key_meth, [it])
2027 assert i != null
2028 v.assign(v.variable(variables[0]), i)
2029 var item_meth = self.method_item
2030 assert item_meth != null
2031 i = v.send(item_meth, [it])
2032 assert i != null
2033 v.assign(v.variable(variables[1]), i)
2034 else
2035 abort
2036 end
2037 v.stmt(self.n_block)
2038 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2039 var next_meth = self.method_next
2040 assert next_meth != null
2041 v.send(next_meth, [it])
2042 v.add("\}")
2043 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2044 end
2045 end
2046
2047 redef class AAssertExpr
2048 redef fun stmt(v)
2049 do
2050 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2051
2052 var cond = v.expr_bool(self.n_expr)
2053 v.add("if (!{cond}) \{")
2054 v.stmt(self.n_else)
2055 var nid = self.n_id
2056 if nid != null then
2057 v.add_abort("Assert '{nid.text}' failed")
2058 else
2059 v.add_abort("Assert failed")
2060 end
2061 v.add("\}")
2062 end
2063 end
2064
2065 redef class AOrExpr
2066 redef fun expr(v)
2067 do
2068 var res = v.new_var(self.mtype.as(not null))
2069 var i1 = v.expr_bool(self.n_expr)
2070 v.add("if ({i1}) \{")
2071 v.add("{res} = 1;")
2072 v.add("\} else \{")
2073 var i2 = v.expr_bool(self.n_expr2)
2074 v.add("{res} = {i2};")
2075 v.add("\}")
2076 return res
2077 end
2078 end
2079
2080 redef class AAndExpr
2081 redef fun expr(v)
2082 do
2083 var res = v.new_var(self.mtype.as(not null))
2084 var i1 = v.expr_bool(self.n_expr)
2085 v.add("if (!{i1}) \{")
2086 v.add("{res} = 0;")
2087 v.add("\} else \{")
2088 var i2 = v.expr_bool(self.n_expr2)
2089 v.add("{res} = {i2};")
2090 v.add("\}")
2091 return res
2092 end
2093 end
2094
2095 redef class ANotExpr
2096 redef fun expr(v)
2097 do
2098 var cond = v.expr_bool(self.n_expr)
2099 return v.new_expr("!{cond}", self.mtype.as(not null))
2100 end
2101 end
2102
2103 redef class AOrElseExpr
2104 redef fun expr(v)
2105 do
2106 var res = v.new_var(self.mtype.as(not null))
2107 var i1 = v.expr(self.n_expr, null)
2108 v.add("if ({i1}!=NULL) \{")
2109 v.assign(res, i1)
2110 v.add("\} else \{")
2111 var i2 = v.expr(self.n_expr2, null)
2112 v.assign(res, i2)
2113 v.add("\}")
2114 return res
2115 end
2116 end
2117
2118 redef class AEeExpr
2119 redef fun expr(v)
2120 do
2121 var value1 = v.expr(self.n_expr, null)
2122 var value2 = v.expr(self.n_expr2, null)
2123 return v.equal_test(value1, value2)
2124 end
2125 end
2126
2127 redef class AIntExpr
2128 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2129 end
2130
2131 redef class AFloatExpr
2132 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2133 end
2134
2135 redef class ACharExpr
2136 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2137 end
2138
2139 redef class AArrayExpr
2140 redef fun expr(v)
2141 do
2142 var mtype = self.mtype.as(MClassType).arguments.first
2143 var array = new Array[RuntimeVariable]
2144 for nexpr in self.n_exprs.n_exprs do
2145 var i = v.expr(nexpr, mtype)
2146 array.add(i)
2147 end
2148 return v.array_instance(array, mtype)
2149 end
2150 end
2151
2152 redef class AStringFormExpr
2153 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2154 end
2155
2156 redef class ASuperstringExpr
2157 redef fun expr(v)
2158 do
2159 var array = new Array[RuntimeVariable]
2160 for ne in self.n_exprs do
2161 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2162 var i = v.expr(ne, null)
2163 array.add(i)
2164 end
2165 var a = v.array_instance(array, v.object_type)
2166 var res = v.send(v.get_property("to_s", a.mtype), [a])
2167 return res
2168 end
2169 end
2170
2171 redef class ACrangeExpr
2172 redef fun expr(v)
2173 do
2174 var i1 = v.expr(self.n_expr, null)
2175 var i2 = v.expr(self.n_expr2, null)
2176 var mtype = self.mtype.as(MClassType)
2177 var res = v.init_instance(mtype)
2178 var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
2179 v.check_init_instance(res, mtype)
2180 return res
2181 end
2182 end
2183
2184 redef class AOrangeExpr
2185 redef fun expr(v)
2186 do
2187 var i1 = v.expr(self.n_expr, null)
2188 var i2 = v.expr(self.n_expr2, null)
2189 var mtype = self.mtype.as(MClassType)
2190 var res = v.init_instance(mtype)
2191 var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
2192 v.check_init_instance(res, mtype)
2193 return res
2194 end
2195 end
2196
2197 redef class ATrueExpr
2198 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2199 end
2200
2201 redef class AFalseExpr
2202 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2203 end
2204
2205 redef class ANullExpr
2206 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2207 end
2208
2209 redef class AIsaExpr
2210 redef fun expr(v)
2211 do
2212 var i = v.expr(self.n_expr, null)
2213 return v.type_test(i, self.cast_type.as(not null), "isa")
2214 end
2215 end
2216
2217 redef class AAsCastExpr
2218 redef fun expr(v)
2219 do
2220 var i = v.expr(self.n_expr, null)
2221 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2222
2223 var cond = v.type_test(i, self.mtype.as(not null), "as")
2224 v.add("if (!{cond}) \{")
2225 v.add_abort("Cast failed")
2226 v.add("\}")
2227 return i
2228 end
2229 end
2230
2231 redef class AAsNotnullExpr
2232 redef fun expr(v)
2233 do
2234 var i = v.expr(self.n_expr, null)
2235 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2236
2237 v.add("if ({i} == NULL) \{")
2238 v.add_abort("Cast failed")
2239 v.add("\}")
2240 return i
2241 end
2242 end
2243
2244 redef class AParExpr
2245 redef fun expr(v) do return v.expr(self.n_expr, null)
2246 end
2247
2248 redef class AOnceExpr
2249 redef fun expr(v)
2250 do
2251 var mtype = self.mtype.as(not null)
2252 var name = v.get_name("varonce")
2253 var guard = v.get_name(name + "_guard")
2254 v.add_decl("static {mtype.ctype} {name};")
2255 v.add_decl("static int {guard};")
2256 var res = v.new_var(mtype)
2257 v.add("if ({guard}) \{")
2258 v.add("{res} = {name};")
2259 v.add("\} else \{")
2260 var i = v.expr(self.n_expr, mtype)
2261 v.add("{res} = {i};")
2262 v.add("{name} = {res};")
2263 v.add("{guard} = 1;")
2264 v.add("\}")
2265 return res
2266 end
2267 end
2268
2269 redef class ASendExpr
2270 redef fun expr(v)
2271 do
2272 var recv = v.expr(self.n_expr, null)
2273 var args = [recv]
2274 for a in self.raw_arguments.as(not null) do
2275 args.add(v.expr(a, null))
2276 end
2277 return v.compile_callsite(self.callsite.as(not null), args)
2278 end
2279 end
2280
2281 redef class ASendReassignFormExpr
2282 redef fun stmt(v)
2283 do
2284 var recv = v.expr(self.n_expr, null)
2285 var args = [recv]
2286 for a in self.raw_arguments.as(not null) do
2287 args.add(v.expr(a, null))
2288 end
2289 var value = v.expr(self.n_value, null)
2290
2291 var left = v.compile_callsite(self.callsite.as(not null), args)
2292 assert left != null
2293
2294 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2295 assert res != null
2296
2297 args.add(res)
2298 v.compile_callsite(self.write_callsite.as(not null), args)
2299 end
2300 end
2301
2302 redef class ASuperExpr
2303 redef fun expr(v)
2304 do
2305 var recv = v.frame.arguments.first
2306 var args = [recv]
2307 for a in self.n_args.n_exprs do
2308 args.add(v.expr(a, null))
2309 end
2310 if args.length == 1 then
2311 args = v.frame.arguments
2312 end
2313
2314 var mproperty = self.mproperty
2315 if mproperty != null then
2316 if mproperty.intro.msignature.arity == 0 then
2317 args = [recv]
2318 end
2319 # Super init call
2320 var res = v.send(mproperty, args)
2321 return res
2322 end
2323
2324 # stantard call-next-method
2325 return v.supercall(v.frame.mpropdef.as(MMethodDef), recv.mtype.as(MClassType), args)
2326 end
2327 end
2328
2329 redef class ANewExpr
2330 redef fun expr(v)
2331 do
2332 var mtype = self.mtype.as(MClassType)
2333 var recv
2334 var ctype = mtype.ctype
2335 if ctype == "val*" then
2336 recv = v.init_instance(mtype)
2337 else if ctype == "void*" then
2338 recv = v.new_expr("NULL/*special!*/", mtype)
2339 else
2340 debug("cannot new {mtype}")
2341 abort
2342 end
2343 var args = [recv]
2344 for a in self.n_args.n_exprs do
2345 args.add(v.expr(a, null))
2346 end
2347 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2348 if res2 != null then
2349 #self.debug("got {res2} from {mproperty}. drop {recv}")
2350 return res2
2351 end
2352 v.check_init_instance(recv, mtype)
2353 return recv
2354 end
2355 end
2356
2357 redef class AAttrExpr
2358 redef fun expr(v)
2359 do
2360 var recv = v.expr(self.n_expr, null)
2361 var mproperty = self.mproperty.as(not null)
2362 return v.read_attribute(mproperty, recv)
2363 end
2364 end
2365
2366 redef class AAttrAssignExpr
2367 redef fun stmt(v)
2368 do
2369 var recv = v.expr(self.n_expr, null)
2370 var i = v.expr(self.n_value, null)
2371 var mproperty = self.mproperty.as(not null)
2372 v.write_attribute(mproperty, recv, i)
2373 end
2374 end
2375
2376 redef class AAttrReassignExpr
2377 redef fun stmt(v)
2378 do
2379 var recv = v.expr(self.n_expr, null)
2380 var value = v.expr(self.n_value, null)
2381 var mproperty = self.mproperty.as(not null)
2382 var attr = v.read_attribute(mproperty, recv)
2383 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2384 assert res != null
2385 v.write_attribute(mproperty, recv, res)
2386 end
2387 end
2388
2389 redef class AIssetAttrExpr
2390 redef fun expr(v)
2391 do
2392 var recv = v.expr(self.n_expr, null)
2393 var mproperty = self.mproperty.as(not null)
2394 return v.isset_attribute(mproperty, recv)
2395 end
2396 end
2397
2398 redef class ADebugTypeExpr
2399 redef fun stmt(v)
2400 do
2401 # do nothing
2402 end
2403 end
2404
2405 # Utils
2406
2407 redef class Array[E]
2408 # Return a new `Array` with the elements only contened in self and not in `o`
2409 fun -(o: Array[E]): Array[E] do
2410 var res = new Array[E]
2411 for e in self do if not o.has(e) then res.add(e)
2412 return res
2413 end
2414 end
2415
2416 redef class MModule
2417 # All `MProperty` associated to all `MClassDef` of `mclass`
2418 fun properties(mclass: MClass): Set[MProperty] do
2419 if not self.properties_cache.has_key(mclass) then
2420 var properties = new HashSet[MProperty]
2421 var parents = new Array[MClass]
2422 if self.flatten_mclass_hierarchy.has(mclass) then
2423 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2424 end
2425 for parent in parents do
2426 properties.add_all(self.properties(parent))
2427 end
2428 for mclassdef in mclass.mclassdefs do
2429 for mprop in mclassdef.intro_mproperties do
2430 properties.add(mprop)
2431 end
2432 end
2433 self.properties_cache[mclass] = properties
2434 end
2435 return properties_cache[mclass]
2436 end
2437 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2438 end