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