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