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