cdd1535016f8d5206312d2528b0abd36a1a13aac
[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 # Add a dynamic cast
912 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
913 do
914 var res = self.type_test(value, mtype, tag)
915 self.add("if (!{res}) \{")
916 self.add_abort("Cast failed")
917 self.add("\}")
918 end
919
920 # Generate a return with the value `s`
921 fun ret(s: RuntimeVariable)
922 do
923 self.assign(self.frame.returnvar.as(not null), s)
924 self.add("goto {self.frame.returnlabel.as(not null)};")
925 end
926
927 # Compile a statement (if any)
928 fun stmt(nexpr: nullable AExpr)
929 do
930 if nexpr == null then return
931 var old = self.current_node
932 self.current_node = nexpr
933 nexpr.stmt(self)
934 self.current_node = old
935 end
936
937 # Compile an expression an return its result
938 # `mtype` is the expected return type, pass null if no specific type is expected.
939 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
940 do
941 var old = self.current_node
942 self.current_node = nexpr
943 var res = nexpr.expr(self).as(not null)
944 if mtype != null then
945 mtype = self.anchor(mtype)
946 res = self.autobox(res, mtype)
947 end
948 res = autoadapt(res, nexpr.mtype.as(not null))
949 var implicit_cast_to = nexpr.implicit_cast_to
950 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
951 add_cast(res, implicit_cast_to, "auto")
952 res = autoadapt(res, implicit_cast_to)
953 end
954 self.current_node = old
955 return res
956 end
957
958 # Alias for `self.expr(nexpr, self.bool_type)`
959 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
960
961 # Safely show a debug message on the current node and repeat the message in the C code as a comment
962 fun debug(message: String)
963 do
964 var node = self.current_node
965 if node == null then
966 print "?: {message}"
967 else
968 node.debug(message)
969 end
970 self.add("/* DEBUG: {message} */")
971 end
972 end
973
974 # A C function associated to a Nit method
975 # Because of customization, a given Nit method can be compiler more that once
976 abstract class AbstractRuntimeFunction
977
978 type COMPILER: AbstractCompiler
979 type VISITOR: AbstractCompilerVisitor
980
981 # The associated Nit method
982 var mmethoddef: MMethodDef
983
984 # The mangled c name of the runtime_function
985 # Subclasses should redefine `build_c_name` instead
986 fun c_name: String
987 do
988 var res = self.c_name_cache
989 if res != null then return res
990 res = self.build_c_name
991 self.c_name_cache = res
992 return res
993 end
994
995 # Non cached version of `c_name`
996 protected fun build_c_name: String is abstract
997
998 protected var c_name_cache: nullable String writable = null
999
1000 # Implements a call of the runtime_function
1001 # May inline the body or generate a C function call
1002 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1003
1004 # Generate the code for the `AbstractRuntimeFunction`
1005 # Warning: compile more than once compilation makes CC unhappy
1006 fun compile_to_c(compiler: COMPILER) is abstract
1007 end
1008
1009 # A runtime variable hold a runtime value in C.
1010 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1011 #
1012 # 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.
1013 class RuntimeVariable
1014 # The name of the variable in the C code
1015 var name: String
1016
1017 # The static type of the variable (as declard in C)
1018 var mtype: MType
1019
1020 # The current casted type of the variable (as known in Nit)
1021 var mcasttype: MType writable
1022
1023 # If the variable exaclty a mcasttype?
1024 # false (usual value) means that the variable is a mcasttype or a subtype.
1025 var is_exact: Bool writable = false
1026
1027 init(name: String, mtype: MType, mcasttype: MType)
1028 do
1029 self.name = name
1030 self.mtype = mtype
1031 self.mcasttype = mcasttype
1032 assert not mtype.need_anchor
1033 assert not mcasttype.need_anchor
1034 end
1035
1036 redef fun to_s do return name
1037
1038 redef fun inspect
1039 do
1040 var exact_str
1041 if self.is_exact then
1042 exact_str = " exact"
1043 else
1044 exact_str = ""
1045 end
1046 var type_str
1047 if self.mtype == self.mcasttype then
1048 type_str = "{mtype}{exact_str}"
1049 else
1050 type_str = "{mtype}({mcasttype}{exact_str})"
1051 end
1052 return "<{name}:{type_str}>"
1053 end
1054 end
1055
1056 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1057 class Frame
1058
1059 type VISITOR: AbstractCompilerVisitor
1060
1061 # The associated visitor
1062 var visitor: VISITOR
1063
1064 # The executed property.
1065 # A Method in case of a call, an attribute in case of a default initialization.
1066 var mpropdef: MPropDef
1067
1068 # The static type of the receiver
1069 var receiver: MClassType
1070
1071 # Arguments of the method (the first is the receiver)
1072 var arguments: Array[RuntimeVariable]
1073
1074 # The runtime_variable associated to the return (in a function)
1075 var returnvar: nullable RuntimeVariable writable = null
1076
1077 # The label at the end of the property
1078 var returnlabel: nullable String writable = null
1079 end
1080
1081 # An extern C file to compile
1082 class ExternCFile
1083 # The filename of the file
1084 var filename: String
1085 # Additionnal specific CC compiler -c flags
1086 var cflags: String
1087 end
1088
1089 redef class MType
1090 # Return the C type associated to a given Nit static type
1091 fun ctype: String do return "val*"
1092
1093 fun ctypename: String do return "val"
1094
1095 # Return the name of the C structure associated to a Nit live type
1096 fun c_name: String is abstract
1097 protected var c_name_cache: nullable String protected writable
1098 end
1099
1100 redef class MClassType
1101 redef fun c_name
1102 do
1103 var res = self.c_name_cache
1104 if res != null then return res
1105 res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
1106 self.c_name_cache = res
1107 return res
1108 end
1109
1110 redef fun ctype: String
1111 do
1112 if mclass.name == "Int" then
1113 return "long"
1114 else if mclass.name == "Bool" then
1115 return "short int"
1116 else if mclass.name == "Char" then
1117 return "char"
1118 else if mclass.name == "Float" then
1119 return "double"
1120 else if mclass.name == "NativeString" then
1121 return "char*"
1122 else if mclass.name == "NativeArray" then
1123 return "val*"
1124 else if mclass.kind == extern_kind then
1125 return "void*"
1126 else
1127 return "val*"
1128 end
1129 end
1130
1131 redef fun ctypename: String
1132 do
1133 if mclass.name == "Int" then
1134 return "l"
1135 else if mclass.name == "Bool" then
1136 return "s"
1137 else if mclass.name == "Char" then
1138 return "c"
1139 else if mclass.name == "Float" then
1140 return "d"
1141 else if mclass.name == "NativeString" then
1142 return "str"
1143 else if mclass.name == "NativeArray" then
1144 #return "{self.arguments.first.ctype}*"
1145 return "val"
1146 else if mclass.kind == extern_kind then
1147 return "ptr"
1148 else
1149 return "val"
1150 end
1151 end
1152 end
1153
1154 redef class MGenericType
1155 redef fun c_name
1156 do
1157 var res = self.c_name_cache
1158 if res != null then return res
1159 res = super
1160 for t in self.arguments do
1161 res = res + t.c_name
1162 end
1163 self.c_name_cache = res
1164 return res
1165 end
1166 end
1167
1168 redef class MParameterType
1169 redef fun c_name
1170 do
1171 var res = self.c_name_cache
1172 if res != null then return res
1173 res = "{self.mclass.c_name}_FT{self.rank}"
1174 self.c_name_cache = res
1175 return res
1176 end
1177 end
1178
1179 redef class MVirtualType
1180 redef fun c_name
1181 do
1182 var res = self.c_name_cache
1183 if res != null then return res
1184 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1185 self.c_name_cache = res
1186 return res
1187 end
1188 end
1189
1190 redef class MNullableType
1191 redef fun c_name
1192 do
1193 var res = self.c_name_cache
1194 if res != null then return res
1195 res = "nullable_{self.mtype.c_name}"
1196 self.c_name_cache = res
1197 return res
1198 end
1199 end
1200
1201 redef class MClass
1202 # Return the name of the C structure associated to a Nit class
1203 fun c_name: String do
1204 var res = self.c_name_cache
1205 if res != null then return res
1206 res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
1207 self.c_name_cache = res
1208 return res
1209 end
1210 private var c_name_cache: nullable String
1211 end
1212
1213 redef class MProperty
1214 fun c_name: String do
1215 var res = self.c_name_cache
1216 if res != null then return res
1217 res = "{self.intro.c_name}"
1218 self.c_name_cache = res
1219 return res
1220 end
1221 private var c_name_cache: nullable String
1222 end
1223
1224 redef class MPropDef
1225 type VISITOR: AbstractCompilerVisitor
1226
1227 private var c_name_cache: nullable String
1228
1229 # The mangled name associated to the property
1230 fun c_name: String
1231 do
1232 var res = self.c_name_cache
1233 if res != null then return res
1234 res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1235 self.c_name_cache = res
1236 return res
1237 end
1238 end
1239
1240 redef class MMethodDef
1241 # Can the body be inlined?
1242 fun can_inline(v: VISITOR): Bool
1243 do
1244 var modelbuilder = v.compiler.modelbuilder
1245 if modelbuilder.mpropdef2npropdef.has_key(self) then
1246 var npropdef = modelbuilder.mpropdef2npropdef[self]
1247 return npropdef.can_inline
1248 else if self.mproperty.name == "init" then
1249 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1250 return true
1251 else
1252 abort
1253 end
1254 end
1255
1256 # Inline the body in another visitor
1257 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1258 do
1259 var modelbuilder = v.compiler.modelbuilder
1260 if modelbuilder.mpropdef2npropdef.has_key(self) then
1261 var npropdef = modelbuilder.mpropdef2npropdef[self]
1262 var oldnode = v.current_node
1263 v.current_node = npropdef
1264 self.compile_parameter_check(v, arguments)
1265 npropdef.compile_to_c(v, self, arguments)
1266 v.current_node = oldnode
1267 else if self.mproperty.name == "init" then
1268 var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
1269 var oldnode = v.current_node
1270 v.current_node = nclassdef
1271 self.compile_parameter_check(v, arguments)
1272 nclassdef.compile_to_c(v, self, arguments)
1273 v.current_node = oldnode
1274 else
1275 abort
1276 end
1277 return null
1278 end
1279
1280 # Generate type checks in the C code to check covariant parameters
1281 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1282 do
1283 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1284
1285 for i in [0..msignature.arity[ do
1286 # skip test for vararg since the array is instantiated with the correct polymorphic type
1287 if msignature.vararg_rank == i then continue
1288
1289 # skip if the cast is not required
1290 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1291 if not origmtype.need_anchor then continue
1292
1293 # get the parameter type
1294 var mtype = self.msignature.mparameters[i].mtype
1295
1296 # generate the cast
1297 # note that v decides if and how to implements the cast
1298 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1299 v.add_cast(arguments[i+1], mtype, "covariance")
1300 end
1301 end
1302 end
1303
1304 # Node visit
1305
1306 redef class APropdef
1307 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1308 do
1309 v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1310 debug("Not yet implemented")
1311 end
1312
1313 fun can_inline: Bool do return true
1314 end
1315
1316 redef class AConcreteMethPropdef
1317 redef fun compile_to_c(v, mpropdef, arguments)
1318 do
1319 for i in [0..mpropdef.msignature.arity[ do
1320 var variable = self.n_signature.n_params[i].variable.as(not null)
1321 v.assign(v.variable(variable), arguments[i+1])
1322 end
1323 # Call the implicit super-init
1324 var auto_super_inits = self.auto_super_inits
1325 if auto_super_inits != null then
1326 var selfarg = [arguments.first]
1327 for auto_super_init in auto_super_inits do
1328 if auto_super_init.intro.msignature.arity == 0 then
1329 v.send(auto_super_init, selfarg)
1330 else
1331 v.send(auto_super_init, arguments)
1332 end
1333 end
1334 end
1335 v.stmt(self.n_block)
1336 end
1337
1338 redef fun can_inline
1339 do
1340 if self.auto_super_inits != null then return false
1341 var nblock = self.n_block
1342 if nblock == null then return true
1343 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1344 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1345 return false
1346 end
1347 end
1348
1349 redef class AInternMethPropdef
1350 redef fun compile_to_c(v, mpropdef, arguments)
1351 do
1352 var pname = mpropdef.mproperty.name
1353 var cname = mpropdef.mclassdef.mclass.name
1354 var ret = mpropdef.msignature.return_mtype
1355 if ret != null then
1356 ret = v.resolve_for(ret, arguments.first)
1357 end
1358 if pname != "==" and pname != "!=" then
1359 v.adapt_signature(mpropdef, arguments)
1360 end
1361 if cname == "Int" then
1362 if pname == "output" then
1363 v.add("printf(\"%ld\\n\", {arguments.first});")
1364 return
1365 else if pname == "object_id" then
1366 v.ret(arguments.first)
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 == "-" then
1372 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1373 return
1374 else if pname == "unary -" then
1375 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1376 return
1377 else if pname == "succ" then
1378 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1379 return
1380 else if pname == "prec" then
1381 v.ret(v.new_expr("{arguments[0]}-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 == "%" then
1390 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1391 return
1392 else if pname == "lshift" then
1393 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1394 return
1395 else if pname == "rshift" then
1396 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1397 return
1398 else if pname == "==" then
1399 v.ret(v.equal_test(arguments[0], arguments[1]))
1400 return
1401 else if pname == "!=" then
1402 var res = v.equal_test(arguments[0], arguments[1])
1403 v.ret(v.new_expr("!{res}", 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 == ">=" then
1415 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1416 return
1417 else if pname == "to_f" then
1418 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1419 return
1420 else if pname == "ascii" then
1421 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1422 return
1423 end
1424 else if cname == "Char" then
1425 if pname == "output" then
1426 v.add("printf(\"%c\", {arguments.first});")
1427 return
1428 else if pname == "object_id" then
1429 v.ret(v.new_expr("(long){arguments.first}", 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.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 == "succ" then
1445 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1446 return
1447 else if pname == "prec" then
1448 v.ret(v.new_expr("{arguments[0]}-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 == ">=" then
1460 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1461 return
1462 else if pname == "to_i" then
1463 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1464 return
1465 else if pname == "ascii" then
1466 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1467 return
1468 end
1469 else if cname == "Bool" then
1470 if pname == "output" then
1471 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1472 return
1473 else if pname == "object_id" then
1474 v.ret(v.new_expr("(long){arguments.first}", 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 end
1484 else if cname == "Float" then
1485 if pname == "output" then
1486 v.add("printf(\"%f\\n\", {arguments.first});")
1487 return
1488 else if pname == "object_id" then
1489 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
1490 return
1491 else if pname == "+" then
1492 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1493 return
1494 else if pname == "-" then
1495 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1496 return
1497 else if pname == "unary -" then
1498 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1499 return
1500 else if pname == "succ" then
1501 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
1502 return
1503 else if pname == "prec" then
1504 v.ret(v.new_expr("{arguments[0]}-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.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1511 return
1512 else if pname == "==" then
1513 v.ret(v.equal_test(arguments[0], arguments[1]))
1514 return
1515 else if pname == "!=" then
1516 var res = v.equal_test(arguments[0], arguments[1])
1517 v.ret(v.new_expr("!{res}", 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 == ">=" then
1529 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1530 return
1531 else if pname == "to_i" then
1532 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
1533 return
1534 end
1535 else if cname == "NativeString" then
1536 if pname == "[]" then
1537 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
1538 return
1539 else if pname == "[]=" then
1540 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
1541 return
1542 else if pname == "copy_to" then
1543 v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
1544 return
1545 else if pname == "atoi" then
1546 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
1547 return
1548 end
1549 else if cname == "NativeArray" then
1550 v.native_array_def(pname, ret, arguments)
1551 return
1552 end
1553 if pname == "exit" then
1554 v.add("exit({arguments[1]});")
1555 return
1556 else if pname == "sys" then
1557 v.ret(v.new_expr("glob_sys", ret.as(not null)))
1558 return
1559 else if pname == "calloc_string" then
1560 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
1561 return
1562 else if pname == "calloc_array" then
1563 v.calloc_array(ret.as(not null), arguments)
1564 return
1565 else if pname == "object_id" then
1566 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1567 return
1568 else if pname == "is_same_type" then
1569 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1570 return
1571 else if pname == "output_class_name" then
1572 var nat = v.class_name_string(arguments.first)
1573 v.add("printf(\"%s\\n\", {nat});")
1574 return
1575 else if pname == "native_class_name" then
1576 var nat = v.class_name_string(arguments.first)
1577 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
1578 return
1579 else if pname == "force_garbage_collection" then
1580 v.add("nit_gcollect();")
1581 return
1582 else if pname == "native_argc" then
1583 v.ret(v.new_expr("glob_argc", ret.as(not null)))
1584 return
1585 else if pname == "native_argv" then
1586 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
1587 return
1588 end
1589 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
1590 debug("Not implemented {mpropdef}")
1591 end
1592 end
1593
1594 redef class AExternMethPropdef
1595 redef fun compile_to_c(v, mpropdef, arguments)
1596 do
1597 var externname
1598 var nextern = self.n_extern
1599 if nextern == null then
1600 v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1601 v.add("exit(1);")
1602 return
1603 end
1604 externname = nextern.text.substring(1, nextern.text.length-2)
1605 if location.file != null then
1606 var file = location.file.filename
1607 v.add_extern(file)
1608 end
1609 var res: nullable RuntimeVariable = null
1610 var ret = mpropdef.msignature.return_mtype
1611 if ret != null then
1612 ret = v.resolve_for(ret, arguments.first)
1613 res = v.new_var(ret)
1614 end
1615 v.adapt_signature(mpropdef, arguments)
1616
1617 if res == null then
1618 v.add("{externname}({arguments.join(", ")});")
1619 else
1620 v.add("{res} = {externname}({arguments.join(", ")});")
1621 v.ret(res)
1622 end
1623 end
1624 end
1625
1626 redef class AExternInitPropdef
1627 redef fun compile_to_c(v, mpropdef, arguments)
1628 do
1629 var externname
1630 var nextern = self.n_extern
1631 if nextern == null then
1632 v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
1633 v.add("exit(1);")
1634 return
1635 end
1636 externname = nextern.text.substring(1, nextern.text.length-2)
1637 if location.file != null then
1638 var file = location.file.filename
1639 v.add_extern(file)
1640 end
1641 v.adapt_signature(mpropdef, arguments)
1642 var ret = arguments.first.mtype
1643 var res = v.new_var(ret)
1644
1645 arguments.shift
1646
1647 v.add("{res} = {externname}({arguments.join(", ")});")
1648 v.ret(res)
1649 end
1650 end
1651
1652 redef class AAttrPropdef
1653 redef fun compile_to_c(v, mpropdef, arguments)
1654 do
1655 if arguments.length == 1 then
1656 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
1657 v.assign(v.frame.returnvar.as(not null), res)
1658 else
1659 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
1660 end
1661 end
1662
1663 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1664 do
1665 var nexpr = self.n_expr
1666 if nexpr != null then
1667 var oldnode = v.current_node
1668 v.current_node = self
1669 var old_frame = v.frame
1670 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1671 v.frame = frame
1672 var value = v.expr(nexpr, self.mpropdef.static_mtype)
1673 v.write_attribute(self.mpropdef.mproperty, recv, value)
1674 v.frame = old_frame
1675 v.current_node = oldnode
1676 end
1677 end
1678
1679 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
1680 do
1681 var nexpr = self.n_expr
1682 if nexpr != null then return
1683
1684 var oldnode = v.current_node
1685 v.current_node = self
1686 var old_frame = v.frame
1687 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
1688 v.frame = frame
1689 # Force read to check the initialization
1690 v.read_attribute(self.mpropdef.mproperty, recv)
1691 v.frame = old_frame
1692 v.current_node = oldnode
1693 end
1694 end
1695
1696 redef class AClassdef
1697 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1698 do
1699 if mpropdef == self.mfree_init then
1700 var super_inits = self.super_inits
1701 if super_inits != null then
1702 assert arguments.length == 1
1703 for su in super_inits do
1704 v.send(su, arguments)
1705 end
1706 return
1707 end
1708 var recv = arguments.first
1709 var i = 1
1710 # Collect undefined attributes
1711 for npropdef in self.n_propdefs do
1712 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
1713 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
1714 i += 1
1715 end
1716 end
1717 else
1718 abort
1719 end
1720 end
1721 end
1722
1723 redef class ADeferredMethPropdef
1724 redef fun compile_to_c(v, mpropdef, arguments) do
1725 var cn = v.class_name_string(arguments.first)
1726 v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1727 v.add_raw_abort
1728 end
1729 redef fun can_inline do return true
1730 end
1731
1732 redef class AExpr
1733 # Try to compile self as an expression
1734 # Do not call this method directly, use `v.expr` instead
1735 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
1736 do
1737 v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
1738 var mtype = self.mtype
1739 if mtype == null then
1740 return null
1741 else
1742 var res = v.new_var(mtype)
1743 v.add("/* {res} = NOT YET {class_name} */")
1744 return res
1745 end
1746 end
1747
1748 # Try to compile self as a statement
1749 # Do not call this method directly, use `v.stmt` instead
1750 private fun stmt(v: AbstractCompilerVisitor)
1751 do
1752 var res = expr(v)
1753 if res != null then v.add("{res};")
1754 end
1755 end
1756
1757 redef class ABlockExpr
1758 redef fun stmt(v)
1759 do
1760 for e in self.n_expr do v.stmt(e)
1761 end
1762 redef fun expr(v)
1763 do
1764 var last = self.n_expr.last
1765 for e in self.n_expr do
1766 if e == last then break
1767 v.stmt(e)
1768 end
1769 return v.expr(last, null)
1770 end
1771 end
1772
1773 redef class AVardeclExpr
1774 redef fun stmt(v)
1775 do
1776 var variable = self.variable.as(not null)
1777 var ne = self.n_expr
1778 if ne != null then
1779 var i = v.expr(ne, variable.declared_type)
1780 v.assign(v.variable(variable), i)
1781 end
1782 end
1783 end
1784
1785 redef class AVarExpr
1786 redef fun expr(v)
1787 do
1788 var res = v.variable(self.variable.as(not null))
1789 var mtype = self.mtype.as(not null)
1790 return v.autoadapt(res, mtype)
1791 end
1792 end
1793
1794 redef class AVarAssignExpr
1795 redef fun stmt(v)
1796 do
1797 var variable = self.variable.as(not null)
1798 var i = v.expr(self.n_value, variable.declared_type)
1799 v.assign(v.variable(variable), i)
1800 end
1801 redef fun expr(v)
1802 do
1803 var variable = self.variable.as(not null)
1804 var i = v.expr(self.n_value, variable.declared_type)
1805 v.assign(v.variable(variable), i)
1806 return i
1807 end
1808 end
1809
1810 redef class AVarReassignExpr
1811 redef fun stmt(v)
1812 do
1813 var variable = self.variable.as(not null)
1814 var vari = v.variable(variable)
1815 var value = v.expr(self.n_value, variable.declared_type)
1816 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
1817 assert res != null
1818 v.assign(v.variable(variable), res)
1819 end
1820 end
1821
1822 redef class ASelfExpr
1823 redef fun expr(v) do return v.frame.arguments.first
1824 end
1825
1826 redef class AContinueExpr
1827 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
1828 end
1829
1830 redef class ABreakExpr
1831 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
1832 end
1833
1834 redef class AReturnExpr
1835 redef fun stmt(v)
1836 do
1837 var nexpr = self.n_expr
1838 if nexpr != null then
1839 var returnvar = v.frame.returnvar.as(not null)
1840 var i = v.expr(nexpr, returnvar.mtype)
1841 v.assign(returnvar, i)
1842 end
1843 v.add("goto {v.frame.returnlabel.as(not null)};")
1844 end
1845 end
1846
1847 redef class AAbortExpr
1848 redef fun stmt(v) do v.add_abort("Aborted")
1849 end
1850
1851 redef class AIfExpr
1852 redef fun stmt(v)
1853 do
1854 var cond = v.expr_bool(self.n_expr)
1855 v.add("if ({cond})\{")
1856 v.stmt(self.n_then)
1857 v.add("\} else \{")
1858 v.stmt(self.n_else)
1859 v.add("\}")
1860 end
1861
1862 redef fun expr(v)
1863 do
1864 var res = v.new_var(self.mtype.as(not null))
1865 var cond = v.expr_bool(self.n_expr)
1866 v.add("if ({cond})\{")
1867 v.assign(res, v.expr(self.n_then.as(not null), null))
1868 v.add("\} else \{")
1869 v.assign(res, v.expr(self.n_else.as(not null), null))
1870 v.add("\}")
1871 return res
1872 end
1873 end
1874
1875 redef class AIfexprExpr
1876 redef fun expr(v)
1877 do
1878 var res = v.new_var(self.mtype.as(not null))
1879 var cond = v.expr_bool(self.n_expr)
1880 v.add("if ({cond})\{")
1881 v.assign(res, v.expr(self.n_then, null))
1882 v.add("\} else \{")
1883 v.assign(res, v.expr(self.n_else, null))
1884 v.add("\}")
1885 return res
1886 end
1887 end
1888
1889 redef class ADoExpr
1890 redef fun stmt(v)
1891 do
1892 v.stmt(self.n_block)
1893 var escapemark = self.escapemark
1894 if escapemark != null then
1895 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1896 end
1897 end
1898 end
1899
1900 redef class AWhileExpr
1901 redef fun stmt(v)
1902 do
1903 v.add("for(;;) \{")
1904 var cond = v.expr_bool(self.n_expr)
1905 v.add("if (!{cond}) break;")
1906 v.stmt(self.n_block)
1907 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1908 v.add("\}")
1909 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1910 end
1911 end
1912
1913 redef class ALoopExpr
1914 redef fun stmt(v)
1915 do
1916 v.add("for(;;) \{")
1917 v.stmt(self.n_block)
1918 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1919 v.add("\}")
1920 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1921 end
1922 end
1923
1924 redef class AForExpr
1925 redef fun stmt(v)
1926 do
1927 # Shortcut on explicit range
1928 # Avoid the instantiation of the range and the iterator
1929 var nexpr = self.n_expr
1930 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
1931 var from = v.expr(nexpr.n_expr, null)
1932 var to = v.expr(nexpr.n_expr2, null)
1933 var variable = v.variable(variables.first)
1934
1935 v.assign(variable, from)
1936 v.add("for(;;) \{ /* shortcut range */")
1937
1938 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
1939 assert ok != null
1940 v.add("if(!{ok}) break;")
1941
1942 v.stmt(self.n_block)
1943
1944 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1945 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
1946 assert succ != null
1947 v.assign(variable, succ)
1948 v.add("\}")
1949 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1950 return
1951 end
1952
1953 var cl = v.expr(self.n_expr, null)
1954 var it_meth = self.method_iterator
1955 assert it_meth != null
1956 var it = v.send(it_meth, [cl])
1957 assert it != null
1958 v.add("for(;;) \{")
1959 var isok_meth = self.method_is_ok
1960 assert isok_meth != null
1961 var ok = v.send(isok_meth, [it])
1962 assert ok != null
1963 v.add("if(!{ok}) break;")
1964 if self.variables.length == 1 then
1965 var item_meth = self.method_item
1966 assert item_meth != null
1967 var i = v.send(item_meth, [it])
1968 assert i != null
1969 v.assign(v.variable(variables.first), i)
1970 else if self.variables.length == 2 then
1971 var key_meth = self.method_key
1972 assert key_meth != null
1973 var i = v.send(key_meth, [it])
1974 assert i != null
1975 v.assign(v.variable(variables[0]), i)
1976 var item_meth = self.method_item
1977 assert item_meth != null
1978 i = v.send(item_meth, [it])
1979 assert i != null
1980 v.assign(v.variable(variables[1]), i)
1981 else
1982 abort
1983 end
1984 v.stmt(self.n_block)
1985 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
1986 var next_meth = self.method_next
1987 assert next_meth != null
1988 v.send(next_meth, [it])
1989 v.add("\}")
1990 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
1991 end
1992 end
1993
1994 redef class AAssertExpr
1995 redef fun stmt(v)
1996 do
1997 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
1998
1999 var cond = v.expr_bool(self.n_expr)
2000 v.add("if (!{cond}) \{")
2001 v.stmt(self.n_else)
2002 var nid = self.n_id
2003 if nid != null then
2004 v.add_abort("Assert '{nid.text}' failed")
2005 else
2006 v.add_abort("Assert failed")
2007 end
2008 v.add("\}")
2009 end
2010 end
2011
2012 redef class AOrExpr
2013 redef fun expr(v)
2014 do
2015 var res = v.new_var(self.mtype.as(not null))
2016 var i1 = v.expr_bool(self.n_expr)
2017 v.add("if ({i1}) \{")
2018 v.add("{res} = 1;")
2019 v.add("\} else \{")
2020 var i2 = v.expr_bool(self.n_expr2)
2021 v.add("{res} = {i2};")
2022 v.add("\}")
2023 return res
2024 end
2025 end
2026
2027 redef class AImpliesExpr
2028 redef fun expr(v)
2029 do
2030 var res = v.new_var(self.mtype.as(not null))
2031 var i1 = v.expr_bool(self.n_expr)
2032 v.add("if (!{i1}) \{")
2033 v.add("{res} = 1;")
2034 v.add("\} else \{")
2035 var i2 = v.expr_bool(self.n_expr2)
2036 v.add("{res} = {i2};")
2037 v.add("\}")
2038 return res
2039 end
2040 end
2041
2042 redef class AAndExpr
2043 redef fun expr(v)
2044 do
2045 var res = v.new_var(self.mtype.as(not null))
2046 var i1 = v.expr_bool(self.n_expr)
2047 v.add("if (!{i1}) \{")
2048 v.add("{res} = 0;")
2049 v.add("\} else \{")
2050 var i2 = v.expr_bool(self.n_expr2)
2051 v.add("{res} = {i2};")
2052 v.add("\}")
2053 return res
2054 end
2055 end
2056
2057 redef class ANotExpr
2058 redef fun expr(v)
2059 do
2060 var cond = v.expr_bool(self.n_expr)
2061 return v.new_expr("!{cond}", self.mtype.as(not null))
2062 end
2063 end
2064
2065 redef class AOrElseExpr
2066 redef fun expr(v)
2067 do
2068 var res = v.new_var(self.mtype.as(not null))
2069 var i1 = v.expr(self.n_expr, null)
2070 v.add("if ({i1}!=NULL) \{")
2071 v.assign(res, i1)
2072 v.add("\} else \{")
2073 var i2 = v.expr(self.n_expr2, null)
2074 v.assign(res, i2)
2075 v.add("\}")
2076 return res
2077 end
2078 end
2079
2080 redef class AEeExpr
2081 redef fun expr(v)
2082 do
2083 var value1 = v.expr(self.n_expr, null)
2084 var value2 = v.expr(self.n_expr2, null)
2085 return v.equal_test(value1, value2)
2086 end
2087 end
2088
2089 redef class AIntExpr
2090 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2091 end
2092
2093 redef class AFloatExpr
2094 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2095 end
2096
2097 redef class ACharExpr
2098 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2099 end
2100
2101 redef class AArrayExpr
2102 redef fun expr(v)
2103 do
2104 var mtype = self.mtype.as(MClassType).arguments.first
2105 var array = new Array[RuntimeVariable]
2106 for nexpr in self.n_exprs.n_exprs do
2107 var i = v.expr(nexpr, mtype)
2108 array.add(i)
2109 end
2110 return v.array_instance(array, mtype)
2111 end
2112 end
2113
2114 redef class AStringFormExpr
2115 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2116 end
2117
2118 redef class ASuperstringExpr
2119 redef fun expr(v)
2120 do
2121 var array = new Array[RuntimeVariable]
2122 for ne in self.n_exprs do
2123 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2124 var i = v.expr(ne, null)
2125 array.add(i)
2126 end
2127 var a = v.array_instance(array, v.object_type)
2128 var res = v.send(v.get_property("to_s", a.mtype), [a])
2129 return res
2130 end
2131 end
2132
2133 redef class ACrangeExpr
2134 redef fun expr(v)
2135 do
2136 var i1 = v.expr(self.n_expr, null)
2137 var i2 = v.expr(self.n_expr2, null)
2138 var mtype = self.mtype.as(MClassType)
2139 var res = v.init_instance(mtype)
2140 var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
2141 v.check_init_instance(res, mtype)
2142 return res
2143 end
2144 end
2145
2146 redef class AOrangeExpr
2147 redef fun expr(v)
2148 do
2149 var i1 = v.expr(self.n_expr, null)
2150 var i2 = v.expr(self.n_expr2, null)
2151 var mtype = self.mtype.as(MClassType)
2152 var res = v.init_instance(mtype)
2153 var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
2154 v.check_init_instance(res, mtype)
2155 return res
2156 end
2157 end
2158
2159 redef class ATrueExpr
2160 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2161 end
2162
2163 redef class AFalseExpr
2164 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2165 end
2166
2167 redef class ANullExpr
2168 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2169 end
2170
2171 redef class AIsaExpr
2172 redef fun expr(v)
2173 do
2174 var i = v.expr(self.n_expr, null)
2175 return v.type_test(i, self.cast_type.as(not null), "isa")
2176 end
2177 end
2178
2179 redef class AAsCastExpr
2180 redef fun expr(v)
2181 do
2182 var i = v.expr(self.n_expr, null)
2183 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2184
2185 v.add_cast(i, self.mtype.as(not null), "as")
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