modelize: add and use method `collect_attr_propdef`
[nit.git] / src / compiler / 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 semantize
22 import platform
23 import c_tools
24 private import annotation
25 import mixin
26
27 # Add compiling options
28 redef class ToolContext
29 # --output
30 var opt_output = new OptionString("Output file", "-o", "--output")
31 # --dir
32 var opt_dir = new OptionString("Output directory", "--dir")
33 # --no-cc
34 var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
35 # --no-main
36 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
37 # --make-flags
38 var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
39 # --max-c-lines
40 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
41 # --group-c-files
42 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
43 # --compile-dir
44 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
45 # --hardening
46 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
47 # --no-check-covariance
48 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
49 # --no-check-attr-isset
50 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
51 # --no-check-assert
52 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
53 # --no-check-autocast
54 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
55 # --no-check-null
56 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
57 # --no-check-all
58 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
59 # --typing-test-metrics
60 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
61 # --invocation-metrics
62 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
63 # --isset-checks-metrics
64 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
65 # --stacktrace
66 var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
67 # --no-gcc-directives
68 var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
69 # --release
70 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
71
72 redef init
73 do
74 super
75 self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
76 self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
77 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
78 self.option_context.add_option(self.opt_stacktrace)
79 self.option_context.add_option(self.opt_no_gcc_directive)
80 self.option_context.add_option(self.opt_release)
81 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
82
83 opt_no_main.hidden = true
84 end
85
86 redef fun process_options(args)
87 do
88 super
89
90 var st = opt_stacktrace.value
91 if st == "none" or st == "libunwind" or st == "nitstack" then
92 # Fine, do nothing
93 else if st == "auto" or st == null then
94 # Default is nitstack
95 opt_stacktrace.value = "nitstack"
96 else
97 print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
98 exit(1)
99 end
100
101 if opt_output.value != null and opt_dir.value != null then
102 print "Error: cannot use both --dir and --output"
103 exit(1)
104 end
105
106 if opt_no_check_all.value then
107 opt_no_check_covariance.value = true
108 opt_no_check_attr_isset.value = true
109 opt_no_check_assert.value = true
110 opt_no_check_autocast.value = true
111 opt_no_check_null.value = true
112 end
113 end
114 end
115
116 redef class ModelBuilder
117 # The compilation directory
118 var compile_dir: String
119
120 # Simple indirection to `Toolchain::write_and_make`
121 protected fun write_and_make(compiler: AbstractCompiler)
122 do
123 var platform = compiler.mainmodule.target_platform
124 var toolchain
125 if platform == null then
126 toolchain = new MakefileToolchain(toolcontext)
127 else
128 toolchain = platform.toolchain(toolcontext)
129 end
130 compile_dir = toolchain.compile_dir
131 toolchain.write_and_make compiler
132 end
133 end
134
135 redef class Platform
136 fun toolchain(toolcontext: ToolContext): Toolchain is abstract
137 end
138
139 class Toolchain
140 var toolcontext: ToolContext
141
142 fun compile_dir: String
143 do
144 var compile_dir = toolcontext.opt_compile_dir.value
145 if compile_dir == null then compile_dir = ".nit_compile"
146 return compile_dir
147 end
148
149 fun write_and_make(compiler: AbstractCompiler) is abstract
150 end
151
152 class MakefileToolchain
153 super Toolchain
154
155 redef fun write_and_make(compiler)
156 do
157 var compile_dir = compile_dir
158
159 # Generate the .h and .c files
160 # A single C file regroups many compiled rumtime functions
161 # 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
162 var time0 = get_time
163 self.toolcontext.info("*** WRITING C ***", 1)
164
165 compile_dir.mkdir
166
167 var cfiles = new Array[String]
168 write_files(compiler, compile_dir, cfiles)
169
170 # Generate the Makefile
171
172 write_makefile(compiler, compile_dir, cfiles)
173
174 var time1 = get_time
175 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
176
177 # Execute the Makefile
178
179 if self.toolcontext.opt_no_cc.value then return
180
181 time0 = time1
182 self.toolcontext.info("*** COMPILING C ***", 1)
183
184 compile_c_code(compiler, compile_dir)
185
186 time1 = get_time
187 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
188 end
189
190 fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
191 do
192 var platform = compiler.mainmodule.target_platform
193 if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
194 var cc_opt_with_libgc = "-DWITH_LIBGC"
195 if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
196
197 # Add gc_choser.h to aditionnal bodies
198 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
199 if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
200 compiler.extern_bodies.add(gc_chooser)
201 var clib = toolcontext.nit_dir / "clib"
202 compiler.files_to_copy.add "{clib}/gc_chooser.c"
203 compiler.files_to_copy.add "{clib}/gc_chooser.h"
204
205 # FFI
206 for m in compiler.mainmodule.in_importation.greaters do
207 compiler.finalize_ffi_for_module(m)
208 end
209
210 # Copy original .[ch] files to compile_dir
211 for src in compiler.files_to_copy do
212 var basename = src.basename("")
213 var dst = "{compile_dir}/{basename}"
214 src.file_copy_to dst
215 end
216
217 var hfilename = compiler.header.file.name + ".h"
218 var hfilepath = "{compile_dir}/{hfilename}"
219 var h = new OFStream.open(hfilepath)
220 for l in compiler.header.decl_lines do
221 h.write l
222 h.write "\n"
223 end
224 for l in compiler.header.lines do
225 h.write l
226 h.write "\n"
227 end
228 h.close
229
230 var max_c_lines = toolcontext.opt_max_c_lines.value
231 for f in compiler.files do
232 var i = 0
233 var count = 0
234 var file: nullable OFStream = null
235 for vis in f.writers do
236 if vis == compiler.header then continue
237 var total_lines = vis.lines.length + vis.decl_lines.length
238 if total_lines == 0 then continue
239 count += total_lines
240 if file == null or (count > max_c_lines and max_c_lines > 0) then
241 i += 1
242 if file != null then file.close
243 var cfilename = "{f.name}.{i}.c"
244 var cfilepath = "{compile_dir}/{cfilename}"
245 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
246 cfiles.add(cfilename)
247 file = new OFStream.open(cfilepath)
248 file.write "#include \"{f.name}.0.h\"\n"
249 count = total_lines
250 end
251 for l in vis.decl_lines do
252 file.write l
253 file.write "\n"
254 end
255 for l in vis.lines do
256 file.write l
257 file.write "\n"
258 end
259 end
260 if file == null then continue
261 file.close
262
263 var cfilename = "{f.name}.0.h"
264 var cfilepath = "{compile_dir}/{cfilename}"
265 var hfile: nullable OFStream = null
266 hfile = new OFStream.open(cfilepath)
267 hfile.write "#include \"{hfilename}\"\n"
268 for key in f.required_declarations do
269 if not compiler.provided_declarations.has_key(key) then
270 var node = compiler.requirers_of_declarations.get_or_null(key)
271 if node != null then
272 node.debug "No provided declaration for {key}"
273 else
274 print "No provided declaration for {key}"
275 end
276 abort
277 end
278 hfile.write compiler.provided_declarations[key]
279 hfile.write "\n"
280 end
281 hfile.close
282 end
283
284 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
285 end
286
287 fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
288
289 fun default_outname(mainmodule: MModule): String
290 do
291 # Search a non fictive module
292 var res = mainmodule.name
293 while mainmodule.is_fictive do
294 mainmodule = mainmodule.in_importation.direct_greaters.first
295 res = mainmodule.name
296 end
297 return res
298 end
299
300 # Combine options and platform informations to get the final path of the outfile
301 fun outfile(mainmodule: MModule): String
302 do
303 var res = self.toolcontext.opt_output.value
304 if res != null then return res
305 res = default_outname(mainmodule)
306 var dir = self.toolcontext.opt_dir.value
307 if dir != null then return dir.join_path(res)
308 return res
309 end
310
311 fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
312 do
313 var mainmodule = compiler.mainmodule
314 var platform = compiler.mainmodule.target_platform
315
316 var outname = outfile(mainmodule)
317
318 var real_outpath = compile_dir.relpath(outname)
319 var outpath = real_outpath.escape_to_mk
320 if outpath != real_outpath then
321 # If the name is crazy and need escaping, we will do an indirection
322 # 1. generate the binary in the .nit_compile dir under an escaped name
323 # 2. copy the binary at the right place in the `all` goal.
324 outpath = mainmodule.c_name
325 end
326 var makename = makefile_name(mainmodule)
327 var makepath = "{compile_dir}/{makename}"
328 var makefile = new OFStream.open(makepath)
329
330 var linker_options = new HashSet[String]
331 for m in mainmodule.in_importation.greaters do
332 var libs = m.collect_linker_libs
333 if libs != null then linker_options.add_all(libs)
334 end
335
336 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
337
338 var ost = toolcontext.opt_stacktrace.value
339 if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
340
341 # Dynamic adaptations
342 # While `platform` enable complex toolchains, they are statically applied
343 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
344
345 # Check and adapt the targeted system
346 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
347 makefile.write("ifeq ($(uname_S),Darwin)\n")
348 # remove -lunwind since it is already included on macosx
349 makefile.write("\tNEED_LIBUNWIND :=\n")
350 makefile.write("endif\n\n")
351
352 # Check and adapt for the compiler used
353 # clang need an additionnal `-Qunused-arguments`
354 makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
355
356 makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
357
358 makefile.write("all: {outpath}\n")
359 if outpath != real_outpath then
360 makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
361 end
362 makefile.write("\n")
363
364 var ofiles = new Array[String]
365 var dep_rules = new Array[String]
366 # Compile each generated file
367 for f in cfiles do
368 var o = f.strip_extension(".c") + ".o"
369 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
370 ofiles.add(o)
371 dep_rules.add(o)
372 end
373
374 var java_files = new Array[ExternFile]
375
376 var pkgconfigs = new Array[String]
377 for f in compiler.extern_bodies do
378 pkgconfigs.add_all f.pkgconfigs
379 end
380 # Protect pkg-config
381 if not pkgconfigs.is_empty then
382 makefile.write """
383 # does pkg-config exists?
384 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
385 $(error "Command `pkg-config` not found. Please install it")
386 endif
387 """
388 for p in pkgconfigs do
389 makefile.write """
390 # Check for library {{{p}}}
391 ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
392 $(error "pkg-config: package {{{p}}} is not found.")
393 endif
394 """
395 end
396 end
397
398 # Compile each required extern body into a specific .o
399 for f in compiler.extern_bodies do
400 var o = f.makefile_rule_name
401 var ff = f.filename.basename("")
402 makefile.write("{o}: {ff}\n")
403 makefile.write("\t{f.makefile_rule_content}\n\n")
404 dep_rules.add(f.makefile_rule_name)
405
406 if f.compiles_to_o_file then ofiles.add(o)
407 if f.add_to_jar then java_files.add(f)
408 end
409
410 if not java_files.is_empty then
411 var jar_file = "{outpath}.jar"
412
413 var class_files_array = new Array[String]
414 for f in java_files do class_files_array.add(f.makefile_rule_name)
415 var class_files = class_files_array.join(" ")
416
417 makefile.write("{jar_file}: {class_files}\n")
418 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
419 dep_rules.add jar_file
420 end
421
422 # Link edition
423 var pkg = ""
424 if not pkgconfigs.is_empty then
425 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
426 end
427 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
428 # Clean
429 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
430 if outpath != real_outpath then
431 makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
432 end
433 makefile.close
434 self.toolcontext.info("Generated makefile: {makepath}", 2)
435
436 makepath.file_copy_to "{compile_dir}/Makefile"
437 end
438
439 fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
440 do
441 var makename = makefile_name(compiler.mainmodule)
442
443 var makeflags = self.toolcontext.opt_make_flags.value
444 if makeflags == null then makeflags = ""
445 self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
446
447 var res
448 if self.toolcontext.verbose_level >= 3 then
449 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
450 else
451 res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
452 end
453 if res != 0 then
454 toolcontext.error(null, "make failed! Error code: {res}.")
455 end
456 end
457 end
458
459 # Singleton that store the knowledge about the compilation process
460 abstract class AbstractCompiler
461 type VISITOR: AbstractCompilerVisitor
462
463 # Table corresponding c_names to nit names (methods)
464 var names = new HashMap[String, String]
465
466 # The main module of the program currently compiled
467 # Is assigned during the separate compilation
468 var mainmodule: MModule is writable
469
470 # The real main module of the program
471 var realmainmodule: MModule is noinit
472
473 # The modelbuilder used to know the model and the AST
474 var modelbuilder: ModelBuilder is protected writable
475
476 # Is hardening asked? (see --hardening)
477 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
478
479 init
480 do
481 self.realmainmodule = mainmodule
482 end
483
484 # Force the creation of a new file
485 # The point is to avoid contamination between must-be-compiled-separately files
486 fun new_file(name: String): CodeFile
487 do
488 if modelbuilder.toolcontext.opt_group_c_files.value then
489 if self.files.is_empty then
490 var f = new CodeFile(mainmodule.c_name)
491 self.files.add(f)
492 end
493 return self.files.first
494 end
495 var f = new CodeFile(name)
496 self.files.add(f)
497 return f
498 end
499
500 # The list of all associated files
501 # Used to generate .c files
502 var files = new List[CodeFile]
503
504 # Initialize a visitor specific for a compiler engine
505 fun new_visitor: VISITOR is abstract
506
507 # Where global declaration are stored (the main .h)
508 var header: CodeWriter is writable, noinit
509
510 # Provide a declaration that can be requested (before or latter) by a visitor
511 fun provide_declaration(key: String, s: String)
512 do
513 if self.provided_declarations.has_key(key) then
514 assert self.provided_declarations[key] == s
515 end
516 self.provided_declarations[key] = s
517 end
518
519 private var provided_declarations = new HashMap[String, String]
520
521 private var requirers_of_declarations = new HashMap[String, ANode]
522
523 # Builds the .c and .h files to be used when generating a Stack Trace
524 # Binds the generated C function names to Nit function names
525 fun build_c_to_nit_bindings
526 do
527 var compile_dir = modelbuilder.compile_dir
528
529 var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
530 stream.write("#include <string.h>\n")
531 stream.write("#include <stdlib.h>\n")
532 stream.write("#include \"c_functions_hash.h\"\n")
533 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
534 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
535 stream.write("char* procname = malloc(len+1);")
536 stream.write("memcpy(procname, procproc, len);")
537 stream.write("procname[len] = '\\0';")
538 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
539 for i in names.keys do
540 stream.write("\{\"")
541 stream.write(i.escape_to_c)
542 stream.write("\",\"")
543 stream.write(names[i].escape_to_c)
544 stream.write("\"\},\n")
545 end
546 stream.write("\};\n")
547 stream.write("int i;")
548 stream.write("for(i = 0; i < {names.length}; i++)\{")
549 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
550 stream.write("free(procname);")
551 stream.write("return map[i].nit_name;")
552 stream.write("\}")
553 stream.write("\}")
554 stream.write("free(procname);")
555 stream.write("return NULL;")
556 stream.write("\}\n")
557 stream.close
558
559 stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
560 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
561 stream.close
562
563 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
564 end
565
566 # Compile C headers
567 # This method call compile_header_strucs method that has to be refined
568 fun compile_header do
569 self.header.add_decl("#include <stdlib.h>")
570 self.header.add_decl("#include <stdio.h>")
571 self.header.add_decl("#include <string.h>")
572 self.header.add_decl("#include \"gc_chooser.h\"")
573 self.header.add_decl("#ifdef ANDROID")
574 self.header.add_decl(" #include <android/log.h>")
575 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
576 self.header.add_decl("#else")
577 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
578 self.header.add_decl("#endif")
579
580 compile_header_structs
581 compile_nitni_structs
582
583 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
584 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
585 # Signal handler function prototype
586 self.header.add_decl("void show_backtrace(int);")
587 else
588 self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
589 end
590
591 if gccd_disable.has("likely") or gccd_disable.has("all") then
592 self.header.add_decl("#define likely(x) (x)")
593 self.header.add_decl("#define unlikely(x) (x)")
594 else if gccd_disable.has("correct-likely") then
595 # invert the `likely` definition
596 # Used by masochists to bench the worst case
597 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
598 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
599 else
600 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
601 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
602 end
603
604 # Global variable used by intern methods
605 self.header.add_decl("extern int glob_argc;")
606 self.header.add_decl("extern char **glob_argv;")
607 self.header.add_decl("extern val *glob_sys;")
608 end
609
610 # Declaration of structures for live Nit types
611 protected fun compile_header_structs is abstract
612
613 # Declaration of structures for nitni undelying the FFI
614 protected fun compile_nitni_structs
615 do
616 self.header.add_decl """
617 /* Native reference to Nit objects */
618 /* This structure is used to represent every Nit type in extern methods and custom C code. */
619 struct nitni_ref {
620 struct nitni_ref *next,
621 *prev; /* adjacent global references in global list */
622 int count; /* number of time this global reference has been marked */
623 };
624
625 /* List of global references from C code to Nit objects */
626 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
627 struct nitni_global_ref_list_t {
628 struct nitni_ref *head, *tail;
629 };
630 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
631
632 /* Initializer of global reference list */
633 extern void nitni_global_ref_list_init();
634
635 /* Intern function to add a global reference to the list */
636 extern void nitni_global_ref_add( struct nitni_ref *ref );
637
638 /* Intern function to remove a global reference from the list */
639 extern void nitni_global_ref_remove( struct nitni_ref *ref );
640
641 /* Increase count on an existing global reference */
642 extern void nitni_global_ref_incr( struct nitni_ref *ref );
643
644 /* Decrease count on an existing global reference */
645 extern void nitni_global_ref_decr( struct nitni_ref *ref );
646 """
647 end
648
649 fun compile_finalizer_function
650 do
651 var finalizable_type = mainmodule.finalizable_type
652 if finalizable_type == null then return
653
654 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
655
656 if finalize_meth == null then
657 modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
658 return
659 end
660
661 var v = self.new_visitor
662 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
663 var recv = v.new_expr("obj", finalizable_type)
664 v.send(finalize_meth, [recv])
665 v.add "\}"
666 end
667
668 # Generate the main C function.
669 #
670 # This function:
671 #
672 # * allocate the Sys object if it exists
673 # * call init if is exists
674 # * call main if it exists
675 fun compile_main_function
676 do
677 var v = self.new_visitor
678 v.add_decl("#include <signal.h>")
679 var ost = modelbuilder.toolcontext.opt_stacktrace.value
680 var platform = mainmodule.target_platform
681
682 if platform != null and not platform.supports_libunwind then ost = "none"
683
684 var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
685
686 if ost == "nitstack" or ost == "libunwind" then
687 v.add_decl("#define UNW_LOCAL_ONLY")
688 v.add_decl("#include <libunwind.h>")
689 if ost == "nitstack" then
690 v.add_decl("#include \"c_functions_hash.h\"")
691 end
692 end
693 v.add_decl("int glob_argc;")
694 v.add_decl("char **glob_argv;")
695 v.add_decl("val *glob_sys;")
696
697 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
698 for tag in count_type_test_tags do
699 v.add_decl("long count_type_test_resolved_{tag};")
700 v.add_decl("long count_type_test_unresolved_{tag};")
701 v.add_decl("long count_type_test_skipped_{tag};")
702 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
703 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
704 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
705 end
706 end
707
708 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
709 v.add_decl("long count_invoke_by_tables;")
710 v.add_decl("long count_invoke_by_direct;")
711 v.add_decl("long count_invoke_by_inline;")
712 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
713 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
714 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
715 end
716
717 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
718 v.add_decl("long count_attr_reads = 0;")
719 v.add_decl("long count_isset_checks = 0;")
720 v.compiler.header.add_decl("extern long count_attr_reads;")
721 v.compiler.header.add_decl("extern long count_isset_checks;")
722 end
723
724 v.add_decl("void sig_handler(int signo)\{")
725 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
726 v.add_decl("show_backtrace(signo);")
727 v.add_decl("\}")
728
729 v.add_decl("void show_backtrace (int signo) \{")
730 if ost == "nitstack" or ost == "libunwind" then
731 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
732 v.add_decl("unw_cursor_t cursor;")
733 v.add_decl("if(opt==NULL)\{")
734 v.add_decl("unw_context_t uc;")
735 v.add_decl("unw_word_t ip;")
736 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
737 v.add_decl("unw_getcontext(&uc);")
738 v.add_decl("unw_init_local(&cursor, &uc);")
739 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
740 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
741 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
742 v.add_decl("while (unw_step(&cursor) > 0) \{")
743 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
744 if ost == "nitstack" then
745 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
746 v.add_decl(" if (recv != NULL)\{")
747 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
748 v.add_decl(" \}else\{")
749 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
750 v.add_decl(" \}")
751 else
752 v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
753 end
754 v.add_decl("\}")
755 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
756 v.add_decl("free(procname);")
757 v.add_decl("\}")
758 end
759 v.add_decl("exit(signo);")
760 v.add_decl("\}")
761
762 if no_main then
763 v.add_decl("int nit_main(int argc, char** argv) \{")
764 else
765 v.add_decl("int main(int argc, char** argv) \{")
766 end
767
768 v.add("signal(SIGABRT, sig_handler);")
769 v.add("signal(SIGFPE, sig_handler);")
770 v.add("signal(SIGILL, sig_handler);")
771 v.add("signal(SIGINT, sig_handler);")
772 v.add("signal(SIGTERM, sig_handler);")
773 v.add("signal(SIGSEGV, sig_handler);")
774 v.add("signal(SIGPIPE, sig_handler);")
775
776 v.add("glob_argc = argc; glob_argv = argv;")
777 v.add("initialize_gc_option();")
778
779 v.add "initialize_nitni_global_refs();"
780
781 var main_type = mainmodule.sys_type
782 if main_type != null then
783 var mainmodule = v.compiler.mainmodule
784 var glob_sys = v.init_instance(main_type)
785 v.add("glob_sys = {glob_sys};")
786 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
787 if main_init != null then
788 v.send(main_init, [glob_sys])
789 end
790 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
791 mainmodule.try_get_primitive_method("main", main_type.mclass)
792 if main_method != null then
793 v.send(main_method, [glob_sys])
794 end
795 end
796
797 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
798 v.add_decl("long count_type_test_resolved_total = 0;")
799 v.add_decl("long count_type_test_unresolved_total = 0;")
800 v.add_decl("long count_type_test_skipped_total = 0;")
801 v.add_decl("long count_type_test_total_total = 0;")
802 for tag in count_type_test_tags do
803 v.add_decl("long count_type_test_total_{tag};")
804 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
805 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
806 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
807 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
808 v.add("count_type_test_total_total += count_type_test_total_{tag};")
809 end
810 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
811 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
812 var tags = count_type_test_tags.to_a
813 tags.add("total")
814 for tag in tags do
815 v.add("printf(\"{tag}\");")
816 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
817 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
818 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
819 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
820 end
821 end
822
823 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
824 v.add_decl("long count_invoke_total;")
825 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
826 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
827 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
828 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
829 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
830 end
831
832 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
833 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
834 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
835 end
836
837 v.add("return 0;")
838 v.add("\}")
839 end
840
841 # Copile all C functions related to the [incr|decr]_ref features of the FFI
842 fun compile_nitni_global_ref_functions
843 do
844 var v = self.new_visitor
845 v.add """
846 struct nitni_global_ref_list_t *nitni_global_ref_list;
847 void initialize_nitni_global_refs() {
848 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
849 nitni_global_ref_list->head = NULL;
850 nitni_global_ref_list->tail = NULL;
851 }
852
853 void nitni_global_ref_add( struct nitni_ref *ref ) {
854 if ( nitni_global_ref_list->head == NULL ) {
855 nitni_global_ref_list->head = ref;
856 ref->prev = NULL;
857 } else {
858 nitni_global_ref_list->tail->next = ref;
859 ref->prev = nitni_global_ref_list->tail;
860 }
861 nitni_global_ref_list->tail = ref;
862
863 ref->next = NULL;
864 }
865
866 void nitni_global_ref_remove( struct nitni_ref *ref ) {
867 if ( ref->prev == NULL ) {
868 nitni_global_ref_list->head = ref->next;
869 } else {
870 ref->prev->next = ref->next;
871 }
872
873 if ( ref->next == NULL ) {
874 nitni_global_ref_list->tail = ref->prev;
875 } else {
876 ref->next->prev = ref->prev;
877 }
878 }
879
880 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
881 if ( ref->count == 0 ) /* not registered */
882 {
883 /* add to list */
884 nitni_global_ref_add( ref );
885 }
886
887 ref->count ++;
888 }
889
890 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
891 if ( ref->count == 1 ) /* was last reference */
892 {
893 /* remove from list */
894 nitni_global_ref_remove( ref );
895 }
896
897 ref->count --;
898 }
899 """
900 end
901
902 # List of additional files required to compile (FFI)
903 var extern_bodies = new Array[ExternFile]
904
905 # List of source files to copy over to the compile dir
906 var files_to_copy = new Array[String]
907
908 # This is used to avoid adding an extern file more than once
909 private var seen_extern = new ArraySet[String]
910
911 # Generate code that initialize the attributes on a new instance
912 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
913 do
914 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
915 self.mainmodule.linearize_mclassdefs(cds)
916 for cd in cds do
917 for npropdef in modelbuilder.collect_attr_propdef(cd) do
918 npropdef.init_expr(v, recv)
919 end
920 end
921 end
922
923 # Generate code that check if an attribute is correctly initialized
924 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
925 do
926 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
927 self.mainmodule.linearize_mclassdefs(cds)
928 for cd in cds do
929 for npropdef in modelbuilder.collect_attr_propdef(cd) do
930 npropdef.check_expr(v, recv)
931 end
932 end
933 end
934
935 # stats
936
937 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
938 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
939 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
940 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
941
942 protected fun init_count_type_test_tags: HashMap[String, Int]
943 do
944 var res = new HashMap[String, Int]
945 for tag in count_type_test_tags do
946 res[tag] = 0
947 end
948 return res
949 end
950
951 # Display stats about compilation process
952 #
953 # Metrics used:
954 #
955 # * type tests against resolved types (`x isa Collection[Animal]`)
956 # * type tests against unresolved types (`x isa Collection[E]`)
957 # * type tests skipped
958 # * type tests total
959 fun display_stats
960 do
961 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
962 print "# static count_type_test"
963 print "\tresolved:\tunresolved\tskipped\ttotal"
964 var count_type_test_total = init_count_type_test_tags
965 count_type_test_resolved["total"] = 0
966 count_type_test_unresolved["total"] = 0
967 count_type_test_skipped["total"] = 0
968 count_type_test_total["total"] = 0
969 for tag in count_type_test_tags do
970 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
971 count_type_test_resolved["total"] += count_type_test_resolved[tag]
972 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
973 count_type_test_skipped["total"] += count_type_test_skipped[tag]
974 count_type_test_total["total"] += count_type_test_total[tag]
975 end
976 var count_type_test = count_type_test_total["total"]
977 var tags = count_type_test_tags.to_a
978 tags.add("total")
979 for tag in tags do
980 printn tag
981 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
982 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
983 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
984 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
985 print ""
986 end
987 end
988 end
989
990 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
991
992 # Division facility
993 # Avoid division by zero by returning the string "n/a"
994 fun div(a,b:Int):String
995 do
996 if b == 0 then return "n/a"
997 return ((a*10000/b).to_f / 100.0).to_precision(2)
998 end
999 end
1000
1001 # A file unit (may be more than one file if
1002 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1003 class CodeFile
1004 var name: String
1005 var writers = new Array[CodeWriter]
1006 var required_declarations = new HashSet[String]
1007 end
1008
1009 # Where to store generated lines
1010 class CodeWriter
1011 var file: CodeFile
1012 var lines: List[String] = new List[String]
1013 var decl_lines: List[String] = new List[String]
1014
1015 # Add a line in the main part of the generated C
1016 fun add(s: String) do self.lines.add(s)
1017
1018 # Add a line in the
1019 # (used for local or global declaration)
1020 fun add_decl(s: String) do self.decl_lines.add(s)
1021
1022 init
1023 do
1024 file.writers.add(self)
1025 end
1026 end
1027
1028 # A visitor on the AST of property definition that generate the C code.
1029 abstract class AbstractCompilerVisitor
1030
1031 type COMPILER: AbstractCompiler
1032
1033 # The associated compiler
1034 var compiler: COMPILER
1035
1036 # The current visited AST node
1037 var current_node: nullable ANode = null is writable
1038
1039 # The current `Frame`
1040 var frame: nullable Frame = null is writable
1041
1042 # Alias for self.compiler.mainmodule.object_type
1043 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1044
1045 # Alias for self.compiler.mainmodule.bool_type
1046 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1047
1048 var writer: CodeWriter is noinit
1049
1050 init
1051 do
1052 self.writer = new CodeWriter(compiler.files.last)
1053 end
1054
1055 # Force to get the primitive class named `name` or abort
1056 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
1057
1058 # Force to get the primitive property named `name` in the instance `recv` or abort
1059 fun get_property(name: String, recv: MType): MMethod
1060 do
1061 assert recv isa MClassType
1062 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1063 end
1064
1065 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1066 do
1067 var initializers = callsite.mpropdef.initializers
1068 if not initializers.is_empty then
1069 var recv = arguments.first
1070
1071 var i = 1
1072 for p in initializers do
1073 if p isa MMethod then
1074 var args = [recv]
1075 for x in p.intro.msignature.mparameters do
1076 args.add arguments[i]
1077 i += 1
1078 end
1079 self.send(p, args)
1080 else if p isa MAttribute then
1081 self.write_attribute(p, recv, arguments[i])
1082 i += 1
1083 else abort
1084 end
1085 assert i == arguments.length
1086
1087 return self.send(callsite.mproperty, [recv])
1088 end
1089
1090 return self.send(callsite.mproperty, arguments)
1091 end
1092
1093 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1094
1095 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1096
1097 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1098
1099 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1100 # This method is used to manage varargs in signatures and returns the real array
1101 # of runtime variables to use in the call.
1102 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1103 do
1104 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1105 var res = new Array[RuntimeVariable]
1106 res.add(recv)
1107
1108 if args.is_empty then return res
1109
1110 var vararg_rank = msignature.vararg_rank
1111 var vararg_len = args.length - msignature.arity
1112 if vararg_len < 0 then vararg_len = 0
1113
1114 for i in [0..msignature.arity[ do
1115 if i == vararg_rank then
1116 var ne = args[i]
1117 if ne isa AVarargExpr then
1118 var e = self.expr(ne.n_expr, null)
1119 res.add(e)
1120 continue
1121 end
1122 var vararg = new Array[RuntimeVariable]
1123 for j in [vararg_rank..vararg_rank+vararg_len] do
1124 var e = self.expr(args[j], null)
1125 vararg.add(e)
1126 end
1127 var elttype = msignature.mparameters[vararg_rank].mtype
1128 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1129 res.add(arg)
1130 else
1131 var j = i
1132 if i > vararg_rank then j += vararg_len
1133 var e = self.expr(args[j], null)
1134 res.add(e)
1135 end
1136 end
1137 return res
1138 end
1139
1140 # Type handling
1141
1142 # Anchor a type to the main module and the current receiver
1143 fun anchor(mtype: MType): MType
1144 do
1145 if not mtype.need_anchor then return mtype
1146 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1147 end
1148
1149 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1150 do
1151 if not mtype.need_anchor then return mtype
1152 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1153 end
1154
1155 # Unsafely cast a value to a new type
1156 # ie the result share the same C variable but my have a different mcasttype
1157 # NOTE: if the adaptation is useless then `value` is returned as it.
1158 # ENSURE: `result.name == value.name`
1159 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1160 do
1161 mtype = self.anchor(mtype)
1162 var valmtype = value.mcasttype
1163 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1164 return value
1165 end
1166
1167 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1168 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1169 return res
1170 else
1171 var res = new RuntimeVariable(value.name, valmtype, mtype)
1172 return res
1173 end
1174 end
1175
1176 # Generate a super call from a method definition
1177 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1178
1179 # Adapt the arguments of a method according to targetted `MMethodDef`
1180 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1181
1182 # Unbox all the arguments of a method when implemented `extern` or `intern`
1183 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1184
1185 # Box or unbox a value to another type iff a C type conversion is needed
1186 # ENSURE: `result.mtype.ctype == mtype.ctype`
1187 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1188
1189 # Box extern classes to be used in the generated code
1190 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1191
1192 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1193 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1194
1195 # Generate a polymorphic subtype test
1196 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1197
1198 # Generate the code required to dynamically check if 2 objects share the same runtime type
1199 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1200
1201 # Generate a Nit "is" for two runtime_variables
1202 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1203
1204 # Sends
1205
1206 # Generate a static call on a method definition
1207 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1208
1209 # Generate a polymorphic send for the method `m` and the arguments `args`
1210 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1211
1212 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1213 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1214 do
1215 assert t isa MClassType
1216 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1217 return self.call(propdef, t, args)
1218 end
1219
1220 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1221 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1222 do
1223 assert t isa MClassType
1224 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1225 return self.call(m, t, args)
1226 end
1227
1228 # Attributes handling
1229
1230 # Generate a polymorphic attribute is_set test
1231 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1232
1233 # Generate a polymorphic attribute read
1234 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1235
1236 # Generate a polymorphic attribute write
1237 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1238
1239 # Checks
1240
1241 # Add a check and an abort for a null receiver if needed
1242 fun check_recv_notnull(recv: RuntimeVariable)
1243 do
1244 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1245
1246 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1247 if maybenull then
1248 self.add("if (unlikely({recv} == NULL)) \{")
1249 self.add_abort("Receiver is null")
1250 self.add("\}")
1251 end
1252 end
1253
1254 # Names handling
1255
1256 private var names = new HashSet[String]
1257 private var last: Int = 0
1258
1259 # Return a new name based on `s` and unique in the visitor
1260 fun get_name(s: String): String
1261 do
1262 if not self.names.has(s) then
1263 self.names.add(s)
1264 return s
1265 end
1266 var i = self.last + 1
1267 loop
1268 var s2 = s + i.to_s
1269 if not self.names.has(s2) then
1270 self.last = i
1271 self.names.add(s2)
1272 return s2
1273 end
1274 i = i + 1
1275 end
1276 end
1277
1278 # Return an unique and stable identifier associated with an escapemark
1279 fun escapemark_name(e: nullable EscapeMark): String
1280 do
1281 assert e != null
1282 if escapemark_names.has_key(e) then return escapemark_names[e]
1283 var name = e.name
1284 if name == null then name = "label"
1285 name = get_name(name)
1286 escapemark_names[e] = name
1287 return name
1288 end
1289
1290 # Insert a C label for associated with an escapemark
1291 fun add_escape_label(e: nullable EscapeMark)
1292 do
1293 if e == null then return
1294 if e.escapes.is_empty then return
1295 add("BREAK_{escapemark_name(e)}: (void)0;")
1296 end
1297
1298 private var escapemark_names = new HashMap[EscapeMark, String]
1299
1300 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1301 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1302 fun class_name_string(value: RuntimeVariable): String is abstract
1303
1304 # Variables handling
1305
1306 protected var variables = new HashMap[Variable, RuntimeVariable]
1307
1308 # Return the local runtime_variable associated to a Nit local variable
1309 fun variable(variable: Variable): RuntimeVariable
1310 do
1311 if self.variables.has_key(variable) then
1312 return self.variables[variable]
1313 else
1314 var name = self.get_name("var_{variable.name}")
1315 var mtype = variable.declared_type.as(not null)
1316 mtype = self.anchor(mtype)
1317 var res = new RuntimeVariable(name, mtype, mtype)
1318 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1319 self.variables[variable] = res
1320 return res
1321 end
1322 end
1323
1324 # Return a new uninitialized local runtime_variable
1325 fun new_var(mtype: MType): RuntimeVariable
1326 do
1327 mtype = self.anchor(mtype)
1328 var name = self.get_name("var")
1329 var res = new RuntimeVariable(name, mtype, mtype)
1330 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1331 return res
1332 end
1333
1334 # The difference with `new_var` is the C static type of the local variable
1335 fun new_var_extern(mtype: MType): RuntimeVariable
1336 do
1337 mtype = self.anchor(mtype)
1338 var name = self.get_name("var")
1339 var res = new RuntimeVariable(name, mtype, mtype)
1340 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1341 return res
1342 end
1343
1344 # Return a new uninitialized named runtime_variable
1345 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1346 do
1347 mtype = self.anchor(mtype)
1348 var res = new RuntimeVariable(name, mtype, mtype)
1349 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1350 return res
1351 end
1352
1353 # Correctly assign a left and a right value
1354 # Boxing and unboxing is performed if required
1355 fun assign(left, right: RuntimeVariable)
1356 do
1357 right = self.autobox(right, left.mtype)
1358 self.add("{left} = {right};")
1359 end
1360
1361 # Generate instances
1362
1363 # Generate a alloc-instance + init-attributes
1364 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1365
1366 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1367 fun set_finalizer(recv: RuntimeVariable)
1368 do
1369 var mtype = recv.mtype
1370 var finalizable_type = compiler.mainmodule.finalizable_type
1371 if finalizable_type != null and not mtype.need_anchor and
1372 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1373 add "gc_register_finalizer({recv});"
1374 end
1375 end
1376
1377 # Generate an integer value
1378 fun int_instance(value: Int): RuntimeVariable
1379 do
1380 var res = self.new_var(self.get_class("Int").mclass_type)
1381 self.add("{res} = {value};")
1382 return res
1383 end
1384
1385 # Generate an integer value
1386 fun bool_instance(value: Bool): RuntimeVariable
1387 do
1388 var res = self.new_var(self.get_class("Bool").mclass_type)
1389 if value then
1390 self.add("{res} = 1;")
1391 else
1392 self.add("{res} = 0;")
1393 end
1394 return res
1395 end
1396
1397 # Generate a string value
1398 fun string_instance(string: String): RuntimeVariable
1399 do
1400 var mtype = self.get_class("String").mclass_type
1401 var name = self.get_name("varonce")
1402 self.add_decl("static {mtype.ctype} {name};")
1403 var res = self.new_var(mtype)
1404 self.add("if ({name}) \{")
1405 self.add("{res} = {name};")
1406 self.add("\} else \{")
1407 var native_mtype = self.get_class("NativeString").mclass_type
1408 var nat = self.new_var(native_mtype)
1409 self.add("{nat} = \"{string.escape_to_c}\";")
1410 var length = self.int_instance(string.length)
1411 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1412 self.add("{name} = {res};")
1413 self.add("\}")
1414 return res
1415 end
1416
1417 fun value_instance(object: Object): RuntimeVariable
1418 do
1419 if object isa Int then
1420 return int_instance(object)
1421 else if object isa Bool then
1422 return bool_instance(object)
1423 else if object isa String then
1424 return string_instance(object)
1425 else
1426 abort
1427 end
1428 end
1429
1430 # Generate an array value
1431 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1432
1433 # Get an instance of a array for a vararg
1434 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1435
1436 # Code generation
1437
1438 # Add a line in the main part of the generated C
1439 fun add(s: String) do self.writer.lines.add(s)
1440
1441 # Add a line in the
1442 # (used for local or global declaration)
1443 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1444
1445 # Request the presence of a global declaration
1446 fun require_declaration(key: String)
1447 do
1448 var reqs = self.writer.file.required_declarations
1449 if reqs.has(key) then return
1450 reqs.add(key)
1451 var node = current_node
1452 if node != null then compiler.requirers_of_declarations[key] = node
1453 end
1454
1455 # Add a declaration in the local-header
1456 # The declaration is ensured to be present once
1457 fun declare_once(s: String)
1458 do
1459 self.compiler.provide_declaration(s, s)
1460 self.require_declaration(s)
1461 end
1462
1463 # look for a needed .h and .c file for a given .nit source-file
1464 # FIXME: bad API, parameter should be a `MModule`, not its source-file
1465 fun add_extern(file: String)
1466 do
1467 file = file.strip_extension(".nit")
1468 var tryfile = file + ".nit.h"
1469 if tryfile.file_exists then
1470 self.declare_once("#include \"{tryfile.basename("")}\"")
1471 self.compiler.files_to_copy.add(tryfile)
1472 end
1473 tryfile = file + "_nit.h"
1474 if tryfile.file_exists then
1475 self.declare_once("#include \"{tryfile.basename("")}\"")
1476 self.compiler.files_to_copy.add(tryfile)
1477 end
1478
1479 if self.compiler.seen_extern.has(file) then return
1480 self.compiler.seen_extern.add(file)
1481 tryfile = file + ".nit.c"
1482 if not tryfile.file_exists then
1483 tryfile = file + "_nit.c"
1484 if not tryfile.file_exists then return
1485 end
1486 var f = new ExternCFile(tryfile.basename(""), "")
1487 self.compiler.extern_bodies.add(f)
1488 self.compiler.files_to_copy.add(tryfile)
1489 end
1490
1491 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1492 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1493 do
1494 var res = new_var(mtype)
1495 self.add("{res} = {cexpr};")
1496 return res
1497 end
1498
1499 # Generate generic abort
1500 # used by aborts, asserts, casts, etc.
1501 fun add_abort(message: String)
1502 do
1503 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1504 add_raw_abort
1505 end
1506
1507 fun add_raw_abort
1508 do
1509 if self.current_node != null and self.current_node.location.file != null then
1510 self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
1511 else
1512 self.add("PRINT_ERROR(\"\\n\");")
1513 end
1514 self.add("show_backtrace(1);")
1515 end
1516
1517 # Add a dynamic cast
1518 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1519 do
1520 var res = self.type_test(value, mtype, tag)
1521 self.add("if (unlikely(!{res})) \{")
1522 var cn = self.class_name_string(value)
1523 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1524 self.add_raw_abort
1525 self.add("\}")
1526 end
1527
1528 # Generate a return with the value `s`
1529 fun ret(s: RuntimeVariable)
1530 do
1531 self.assign(self.frame.returnvar.as(not null), s)
1532 self.add("goto {self.frame.returnlabel.as(not null)};")
1533 end
1534
1535 # Compile a statement (if any)
1536 fun stmt(nexpr: nullable AExpr)
1537 do
1538 if nexpr == null then return
1539 var old = self.current_node
1540 self.current_node = nexpr
1541 nexpr.stmt(self)
1542 self.current_node = old
1543 end
1544
1545 # Compile an expression an return its result
1546 # `mtype` is the expected return type, pass null if no specific type is expected.
1547 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1548 do
1549 var old = self.current_node
1550 self.current_node = nexpr
1551 var res = nexpr.expr(self).as(not null)
1552 if mtype != null then
1553 mtype = self.anchor(mtype)
1554 res = self.autobox(res, mtype)
1555 end
1556 res = autoadapt(res, nexpr.mtype.as(not null))
1557 var implicit_cast_to = nexpr.implicit_cast_to
1558 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1559 add_cast(res, implicit_cast_to, "auto")
1560 res = autoadapt(res, implicit_cast_to)
1561 end
1562 self.current_node = old
1563 return res
1564 end
1565
1566 # Alias for `self.expr(nexpr, self.bool_type)`
1567 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1568
1569 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1570 fun debug(message: String)
1571 do
1572 var node = self.current_node
1573 if node == null then
1574 print "?: {message}"
1575 else
1576 node.debug(message)
1577 end
1578 self.add("/* DEBUG: {message} */")
1579 end
1580 end
1581
1582 # A C function associated to a Nit method
1583 # Because of customization, a given Nit method can be compiler more that once
1584 abstract class AbstractRuntimeFunction
1585
1586 type COMPILER: AbstractCompiler
1587 type VISITOR: AbstractCompilerVisitor
1588
1589 # The associated Nit method
1590 var mmethoddef: MMethodDef
1591
1592 # The mangled c name of the runtime_function
1593 # Subclasses should redefine `build_c_name` instead
1594 fun c_name: String
1595 do
1596 var res = self.c_name_cache
1597 if res != null then return res
1598 res = self.build_c_name
1599 self.c_name_cache = res
1600 return res
1601 end
1602
1603 # Non cached version of `c_name`
1604 protected fun build_c_name: String is abstract
1605
1606 protected var c_name_cache: nullable String = null is writable
1607
1608 # Implements a call of the runtime_function
1609 # May inline the body or generate a C function call
1610 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1611
1612 # Generate the code for the `AbstractRuntimeFunction`
1613 # Warning: compile more than once compilation makes CC unhappy
1614 fun compile_to_c(compiler: COMPILER) is abstract
1615 end
1616
1617 # A runtime variable hold a runtime value in C.
1618 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1619 #
1620 # 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.
1621 class RuntimeVariable
1622 # The name of the variable in the C code
1623 var name: String
1624
1625 # The static type of the variable (as declard in C)
1626 var mtype: MType
1627
1628 # The current casted type of the variable (as known in Nit)
1629 var mcasttype: MType is writable
1630
1631 # If the variable exaclty a mcasttype?
1632 # false (usual value) means that the variable is a mcasttype or a subtype.
1633 var is_exact: Bool = false is writable
1634
1635 init
1636 do
1637 assert not mtype.need_anchor
1638 assert not mcasttype.need_anchor
1639 end
1640
1641 redef fun to_s do return name
1642
1643 redef fun inspect
1644 do
1645 var exact_str
1646 if self.is_exact then
1647 exact_str = " exact"
1648 else
1649 exact_str = ""
1650 end
1651 var type_str
1652 if self.mtype == self.mcasttype then
1653 type_str = "{mtype}{exact_str}"
1654 else
1655 type_str = "{mtype}({mcasttype}{exact_str})"
1656 end
1657 return "<{name}:{type_str}>"
1658 end
1659 end
1660
1661 # A frame correspond to a visited property in a `GlobalCompilerVisitor`
1662 class Frame
1663
1664 type VISITOR: AbstractCompilerVisitor
1665
1666 # The associated visitor
1667 var visitor: VISITOR
1668
1669 # The executed property.
1670 # A Method in case of a call, an attribute in case of a default initialization.
1671 var mpropdef: MPropDef
1672
1673 # The static type of the receiver
1674 var receiver: MClassType
1675
1676 # Arguments of the method (the first is the receiver)
1677 var arguments: Array[RuntimeVariable]
1678
1679 # The runtime_variable associated to the return (in a function)
1680 var returnvar: nullable RuntimeVariable = null is writable
1681
1682 # The label at the end of the property
1683 var returnlabel: nullable String = null is writable
1684 end
1685
1686 redef class MType
1687 # Return the C type associated to a given Nit static type
1688 fun ctype: String do return "val*"
1689
1690 # C type outside of the compiler code and in boxes
1691 fun ctype_extern: String do return "val*"
1692
1693 # Short name of the `ctype` to use in unions
1694 fun ctypename: String do return "val"
1695
1696 # Return the name of the C structure associated to a Nit live type
1697 fun c_name: String is abstract
1698 protected var c_name_cache: nullable String is protected writable
1699 end
1700
1701 redef class MClassType
1702 redef fun c_name
1703 do
1704 var res = self.c_name_cache
1705 if res != null then return res
1706 res = "{mclass.intro_mmodule.c_name}__{mclass.name.to_cmangle}"
1707 self.c_name_cache = res
1708 return res
1709 end
1710
1711 redef fun ctype: String
1712 do
1713 if mclass.name == "Int" then
1714 return "long"
1715 else if mclass.name == "Bool" then
1716 return "short int"
1717 else if mclass.name == "Char" then
1718 return "char"
1719 else if mclass.name == "Float" then
1720 return "double"
1721 else if mclass.name == "NativeString" then
1722 return "char*"
1723 else if mclass.name == "NativeArray" then
1724 return "val*"
1725 else
1726 return "val*"
1727 end
1728 end
1729
1730 redef fun ctype_extern: String
1731 do
1732 if mclass.kind == extern_kind then
1733 return "void*"
1734 else
1735 return ctype
1736 end
1737 end
1738
1739 redef fun ctypename: String
1740 do
1741 if mclass.name == "Int" then
1742 return "l"
1743 else if mclass.name == "Bool" then
1744 return "s"
1745 else if mclass.name == "Char" then
1746 return "c"
1747 else if mclass.name == "Float" then
1748 return "d"
1749 else if mclass.name == "NativeString" then
1750 return "str"
1751 else if mclass.name == "NativeArray" then
1752 #return "{self.arguments.first.ctype}*"
1753 return "val"
1754 else
1755 return "val"
1756 end
1757 end
1758 end
1759
1760 redef class MGenericType
1761 redef fun c_name
1762 do
1763 var res = self.c_name_cache
1764 if res != null then return res
1765 res = super
1766 for t in self.arguments do
1767 res = res + t.c_name
1768 end
1769 self.c_name_cache = res
1770 return res
1771 end
1772 end
1773
1774 redef class MParameterType
1775 redef fun c_name
1776 do
1777 var res = self.c_name_cache
1778 if res != null then return res
1779 res = "{self.mclass.c_name}_FT{self.rank}"
1780 self.c_name_cache = res
1781 return res
1782 end
1783 end
1784
1785 redef class MVirtualType
1786 redef fun c_name
1787 do
1788 var res = self.c_name_cache
1789 if res != null then return res
1790 res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
1791 self.c_name_cache = res
1792 return res
1793 end
1794 end
1795
1796 redef class MNullableType
1797 redef fun c_name
1798 do
1799 var res = self.c_name_cache
1800 if res != null then return res
1801 res = "nullable_{self.mtype.c_name}"
1802 self.c_name_cache = res
1803 return res
1804 end
1805 end
1806
1807 redef class MClass
1808 # Return the name of the C structure associated to a Nit class
1809 fun c_name: String do
1810 var res = self.c_name_cache
1811 if res != null then return res
1812 res = "{intro_mmodule.c_name}__{name.to_cmangle}"
1813 self.c_name_cache = res
1814 return res
1815 end
1816 private var c_name_cache: nullable String
1817 end
1818
1819 redef class MProperty
1820 fun c_name: String do
1821 var res = self.c_name_cache
1822 if res != null then return res
1823 res = "{self.intro.c_name}"
1824 self.c_name_cache = res
1825 return res
1826 end
1827 private var c_name_cache: nullable String
1828 end
1829
1830 redef class MPropDef
1831 type VISITOR: AbstractCompilerVisitor
1832
1833 private var c_name_cache: nullable String
1834
1835 # The mangled name associated to the property
1836 fun c_name: String
1837 do
1838 var res = self.c_name_cache
1839 if res != null then return res
1840 res = "{self.mclassdef.mmodule.c_name}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
1841 self.c_name_cache = res
1842 return res
1843 end
1844 end
1845
1846 redef class MMethodDef
1847 # Can the body be inlined?
1848 fun can_inline(v: VISITOR): Bool
1849 do
1850 if is_abstract then return true
1851 var modelbuilder = v.compiler.modelbuilder
1852 var node = modelbuilder.mpropdef2node(self)
1853 if node isa APropdef then
1854 return node.can_inline
1855 else if node isa AClassdef then
1856 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1857 return true
1858 else
1859 abort
1860 end
1861 end
1862
1863 # Inline the body in another visitor
1864 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1865 do
1866 var modelbuilder = v.compiler.modelbuilder
1867 var val = constant_value
1868 var node = modelbuilder.mpropdef2node(self)
1869 if node isa APropdef then
1870 var oldnode = v.current_node
1871 v.current_node = node
1872 self.compile_parameter_check(v, arguments)
1873 node.compile_to_c(v, self, arguments)
1874 v.current_node = oldnode
1875 else if node isa AClassdef then
1876 var oldnode = v.current_node
1877 v.current_node = node
1878 self.compile_parameter_check(v, arguments)
1879 node.compile_to_c(v, self, arguments)
1880 v.current_node = oldnode
1881 else if val != null then
1882 v.ret(v.value_instance(val))
1883 else
1884 abort
1885 end
1886 return null
1887 end
1888
1889 # Generate type checks in the C code to check covariant parameters
1890 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1891 do
1892 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1893
1894 for i in [0..msignature.arity[ do
1895 # skip test for vararg since the array is instantiated with the correct polymorphic type
1896 if msignature.vararg_rank == i then continue
1897
1898 # skip if the cast is not required
1899 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1900 if not origmtype.need_anchor then continue
1901
1902 # get the parameter type
1903 var mtype = self.msignature.mparameters[i].mtype
1904
1905 # generate the cast
1906 # note that v decides if and how to implements the cast
1907 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1908 v.add_cast(arguments[i+1], mtype, "covariance")
1909 end
1910 end
1911 end
1912
1913 # Node visit
1914
1915 redef class APropdef
1916 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1917 do
1918 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1919 debug("Not yet implemented")
1920 end
1921
1922 fun can_inline: Bool do return true
1923 end
1924
1925 redef class AMethPropdef
1926 redef fun compile_to_c(v, mpropdef, arguments)
1927 do
1928 if mpropdef.is_abstract then
1929 var cn = v.class_name_string(arguments.first)
1930 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1931 v.add_raw_abort
1932 return
1933 end
1934
1935 # Call the implicit super-init
1936 var auto_super_inits = self.auto_super_inits
1937 if auto_super_inits != null then
1938 var args = [arguments.first]
1939 for auto_super_init in auto_super_inits do
1940 assert auto_super_init.mproperty != mpropdef.mproperty
1941 args.clear
1942 for i in [0..auto_super_init.msignature.arity+1[ do
1943 args.add(arguments[i])
1944 end
1945 assert auto_super_init.mproperty != mpropdef.mproperty
1946 v.compile_callsite(auto_super_init, args)
1947 end
1948 end
1949 if auto_super_call then
1950 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1951 end
1952
1953 # Try special compilation
1954 if mpropdef.is_intern then
1955 if compile_intern_to_c(v, mpropdef, arguments) then return
1956 else if mpropdef.is_extern then
1957 if mpropdef.mproperty.is_init then
1958 if compile_externinit_to_c(v, mpropdef, arguments) then return
1959 else
1960 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1961 end
1962 end
1963
1964 # Compile block if any
1965 var n_block = n_block
1966 if n_block != null then
1967 for i in [0..mpropdef.msignature.arity[ do
1968 var variable = self.n_signature.n_params[i].variable.as(not null)
1969 v.assign(v.variable(variable), arguments[i+1])
1970 end
1971 v.stmt(n_block)
1972 return
1973 end
1974
1975 # We have a problem
1976 var cn = v.class_name_string(arguments.first)
1977 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1978 v.add_raw_abort
1979 end
1980
1981 redef fun can_inline
1982 do
1983 if self.auto_super_inits != null then return false
1984 var nblock = self.n_block
1985 if nblock == null then return true
1986 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1987 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1988 return false
1989 end
1990
1991 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1992 do
1993 var pname = mpropdef.mproperty.name
1994 var cname = mpropdef.mclassdef.mclass.name
1995 var ret = mpropdef.msignature.return_mtype
1996 if ret != null then
1997 ret = v.resolve_for(ret, arguments.first)
1998 end
1999 if pname != "==" and pname != "!=" then
2000 v.adapt_signature(mpropdef, arguments)
2001 v.unbox_signature_extern(mpropdef, arguments)
2002 end
2003 if cname == "Int" then
2004 if pname == "output" then
2005 v.add("printf(\"%ld\\n\", {arguments.first});")
2006 return true
2007 else if pname == "object_id" then
2008 v.ret(arguments.first)
2009 return true
2010 else if pname == "+" then
2011 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2012 return true
2013 else if pname == "-" then
2014 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2015 return true
2016 else if pname == "unary -" then
2017 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2018 return true
2019 else if pname == "*" then
2020 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2021 return true
2022 else if pname == "/" then
2023 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2024 return true
2025 else if pname == "%" then
2026 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2027 return true
2028 else if pname == "lshift" then
2029 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2030 return true
2031 else if pname == "rshift" then
2032 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2033 return true
2034 else if pname == "==" then
2035 v.ret(v.equal_test(arguments[0], arguments[1]))
2036 return true
2037 else if pname == "!=" then
2038 var res = v.equal_test(arguments[0], arguments[1])
2039 v.ret(v.new_expr("!{res}", ret.as(not null)))
2040 return true
2041 else if pname == "<" then
2042 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2043 return true
2044 else if pname == ">" then
2045 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2046 return true
2047 else if pname == "<=" then
2048 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2049 return true
2050 else if pname == ">=" then
2051 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2052 return true
2053 else if pname == "to_f" then
2054 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2055 return true
2056 else if pname == "ascii" then
2057 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2058 return true
2059 end
2060 else if cname == "Char" then
2061 if pname == "output" then
2062 v.add("printf(\"%c\", {arguments.first});")
2063 return true
2064 else if pname == "object_id" then
2065 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2066 return true
2067 else if pname == "successor" then
2068 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2069 return true
2070 else if pname == "predecessor" then
2071 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2072 return true
2073 else if pname == "==" then
2074 v.ret(v.equal_test(arguments[0], arguments[1]))
2075 return true
2076 else if pname == "!=" then
2077 var res = v.equal_test(arguments[0], arguments[1])
2078 v.ret(v.new_expr("!{res}", ret.as(not null)))
2079 return true
2080 else if pname == "<" then
2081 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2082 return true
2083 else if pname == ">" then
2084 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2085 return true
2086 else if pname == "<=" then
2087 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2088 return true
2089 else if pname == ">=" then
2090 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2091 return true
2092 else if pname == "to_i" then
2093 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2094 return true
2095 else if pname == "ascii" then
2096 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2097 return true
2098 end
2099 else if cname == "Bool" then
2100 if pname == "output" then
2101 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2102 return true
2103 else if pname == "object_id" then
2104 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2105 return true
2106 else if pname == "==" then
2107 v.ret(v.equal_test(arguments[0], arguments[1]))
2108 return true
2109 else if pname == "!=" then
2110 var res = v.equal_test(arguments[0], arguments[1])
2111 v.ret(v.new_expr("!{res}", ret.as(not null)))
2112 return true
2113 end
2114 else if cname == "Float" then
2115 if pname == "output" then
2116 v.add("printf(\"%f\\n\", {arguments.first});")
2117 return true
2118 else if pname == "object_id" then
2119 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2120 return true
2121 else if pname == "+" then
2122 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2123 return true
2124 else if pname == "-" then
2125 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2126 return true
2127 else if pname == "unary -" then
2128 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2129 return true
2130 else if pname == "succ" then
2131 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2132 return true
2133 else if pname == "prec" then
2134 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2135 return true
2136 else if pname == "*" then
2137 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2138 return true
2139 else if pname == "/" then
2140 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2141 return true
2142 else if pname == "==" then
2143 v.ret(v.equal_test(arguments[0], arguments[1]))
2144 return true
2145 else if pname == "!=" then
2146 var res = v.equal_test(arguments[0], arguments[1])
2147 v.ret(v.new_expr("!{res}", ret.as(not null)))
2148 return true
2149 else if pname == "<" then
2150 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2151 return true
2152 else if pname == ">" then
2153 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2154 return true
2155 else if pname == "<=" then
2156 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2157 return true
2158 else if pname == ">=" then
2159 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2160 return true
2161 else if pname == "to_i" then
2162 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2163 return true
2164 end
2165 else if cname == "NativeString" then
2166 if pname == "[]" then
2167 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2168 return true
2169 else if pname == "[]=" then
2170 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2171 return true
2172 else if pname == "copy_to" then
2173 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2174 return true
2175 else if pname == "atoi" then
2176 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2177 return true
2178 else if pname == "new" then
2179 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2180 return true
2181 end
2182 else if cname == "NativeArray" then
2183 v.native_array_def(pname, ret, arguments)
2184 return true
2185 end
2186 if pname == "exit" then
2187 v.add("exit({arguments[1]});")
2188 return true
2189 else if pname == "sys" then
2190 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2191 return true
2192 else if pname == "calloc_string" then
2193 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2194 return true
2195 else if pname == "calloc_array" then
2196 v.calloc_array(ret.as(not null), arguments)
2197 return true
2198 else if pname == "object_id" then
2199 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2200 return true
2201 else if pname == "is_same_type" then
2202 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2203 return true
2204 else if pname == "is_same_instance" then
2205 v.ret(v.equal_test(arguments[0], arguments[1]))
2206 return true
2207 else if pname == "output_class_name" then
2208 var nat = v.class_name_string(arguments.first)
2209 v.add("printf(\"%s\\n\", {nat});")
2210 return true
2211 else if pname == "native_class_name" then
2212 var nat = v.class_name_string(arguments.first)
2213 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2214 return true
2215 else if pname == "force_garbage_collection" then
2216 v.add("nit_gcollect();")
2217 return true
2218 else if pname == "native_argc" then
2219 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2220 return true
2221 else if pname == "native_argv" then
2222 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2223 return true
2224 end
2225 return false
2226 end
2227
2228 # Compile an extern method
2229 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2230 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2231 do
2232 var externname
2233 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2234 if at != null then
2235 externname = at.arg_as_string(v.compiler.modelbuilder)
2236 if externname == null then return false
2237 else
2238 return false
2239 end
2240 if location.file != null then
2241 var file = location.file.filename
2242 v.add_extern(file)
2243 end
2244 var res: nullable RuntimeVariable = null
2245 var ret = mpropdef.msignature.return_mtype
2246 if ret != null then
2247 ret = v.resolve_for(ret, arguments.first)
2248 res = v.new_var_extern(ret)
2249 end
2250 v.adapt_signature(mpropdef, arguments)
2251 v.unbox_signature_extern(mpropdef, arguments)
2252
2253 if res == null then
2254 v.add("{externname}({arguments.join(", ")});")
2255 else
2256 v.add("{res} = {externname}({arguments.join(", ")});")
2257 res = v.box_extern(res, ret.as(not null))
2258 v.ret(res)
2259 end
2260 return true
2261 end
2262
2263 # Compile an extern factory
2264 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2265 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2266 do
2267 var externname
2268 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2269 if at != null then
2270 externname = at.arg_as_string(v.compiler.modelbuilder)
2271 if externname == null then return false
2272 else
2273 return false
2274 end
2275 if location.file != null then
2276 var file = location.file.filename
2277 v.add_extern(file)
2278 end
2279 v.adapt_signature(mpropdef, arguments)
2280 v.unbox_signature_extern(mpropdef, arguments)
2281 var ret = arguments.first.mtype
2282 var res = v.new_var_extern(ret)
2283
2284 arguments.shift
2285
2286 v.add("{res} = {externname}({arguments.join(", ")});")
2287 res = v.box_extern(res, ret)
2288 v.ret(res)
2289 return true
2290 end
2291 end
2292
2293 redef class AAttrPropdef
2294 redef fun compile_to_c(v, mpropdef, arguments)
2295 do
2296 if mpropdef == mreadpropdef then
2297 assert arguments.length == 1
2298 var recv = arguments.first
2299 var res
2300 if is_lazy then
2301 var set
2302 var ret = self.mpropdef.static_mtype
2303 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2304 var guard = self.mlazypropdef.mproperty
2305 if useiset then
2306 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2307 else
2308 set = v.read_attribute(guard, recv)
2309 end
2310 v.add("if(likely({set})) \{")
2311 res = v.read_attribute(self.mpropdef.mproperty, recv)
2312 v.add("\} else \{")
2313
2314 var value = evaluate_expr(v, recv)
2315
2316 v.assign(res, value)
2317 if not useiset then
2318 var true_v = v.new_expr("1", v.bool_type)
2319 v.write_attribute(guard, arguments.first, true_v)
2320 end
2321 v.add("\}")
2322 else
2323 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2324 end
2325 v.assign(v.frame.returnvar.as(not null), res)
2326 else if mpropdef == mwritepropdef then
2327 assert arguments.length == 2
2328 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2329 if is_lazy then
2330 var ret = self.mpropdef.static_mtype
2331 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2332 if not useiset then
2333 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2334 end
2335 end
2336 else
2337 abort
2338 end
2339 end
2340
2341 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2342 do
2343 if has_value and not is_lazy then evaluate_expr(v, recv)
2344 end
2345
2346 # Evaluate, store and return the default value of the attribute
2347 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2348 do
2349 var oldnode = v.current_node
2350 v.current_node = self
2351 var old_frame = v.frame
2352 var frame = new Frame(v, self.mpropdef.as(not null), recv.mcasttype.as(MClassType), [recv])
2353 v.frame = frame
2354
2355 var value
2356 var mtype = self.mpropdef.static_mtype
2357 assert mtype != null
2358
2359 var nexpr = self.n_expr
2360 var nblock = self.n_block
2361 if nexpr != null then
2362 value = v.expr(nexpr, mtype)
2363 else if nblock != null then
2364 value = v.new_var(mtype)
2365 frame.returnvar = value
2366 frame.returnlabel = v.get_name("RET_LABEL")
2367 v.add("\{")
2368 v.stmt(nblock)
2369 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2370 v.add("\}")
2371 else
2372 abort
2373 end
2374
2375 v.write_attribute(self.mpropdef.mproperty, recv, value)
2376
2377 v.frame = old_frame
2378 v.current_node = oldnode
2379
2380 return value
2381 end
2382
2383 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2384 do
2385 var nexpr = self.n_expr
2386 if nexpr != null then return
2387
2388 var oldnode = v.current_node
2389 v.current_node = self
2390 var old_frame = v.frame
2391 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2392 v.frame = frame
2393 # Force read to check the initialization
2394 v.read_attribute(self.mpropdef.mproperty, recv)
2395 v.frame = old_frame
2396 v.current_node = oldnode
2397 end
2398 end
2399
2400 redef class AClassdef
2401 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2402 do
2403 if mpropdef == self.mfree_init then
2404 assert mpropdef.mproperty.is_root_init
2405 assert arguments.length == 1
2406 if not mpropdef.is_intro then
2407 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2408 end
2409 return
2410 else
2411 abort
2412 end
2413 end
2414 end
2415
2416 redef class AExpr
2417 # Try to compile self as an expression
2418 # Do not call this method directly, use `v.expr` instead
2419 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2420 do
2421 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2422 var mtype = self.mtype
2423 if mtype == null then
2424 return null
2425 else
2426 var res = v.new_var(mtype)
2427 v.add("/* {res} = NOT YET {class_name} */")
2428 return res
2429 end
2430 end
2431
2432 # Try to compile self as a statement
2433 # Do not call this method directly, use `v.stmt` instead
2434 private fun stmt(v: AbstractCompilerVisitor)
2435 do
2436 expr(v)
2437 end
2438 end
2439
2440 redef class ABlockExpr
2441 redef fun stmt(v)
2442 do
2443 for e in self.n_expr do v.stmt(e)
2444 end
2445 redef fun expr(v)
2446 do
2447 var last = self.n_expr.last
2448 for e in self.n_expr do
2449 if e == last then break
2450 v.stmt(e)
2451 end
2452 return v.expr(last, null)
2453 end
2454 end
2455
2456 redef class AVardeclExpr
2457 redef fun stmt(v)
2458 do
2459 var variable = self.variable.as(not null)
2460 var ne = self.n_expr
2461 if ne != null then
2462 var i = v.expr(ne, variable.declared_type)
2463 v.assign(v.variable(variable), i)
2464 end
2465 end
2466 end
2467
2468 redef class AVarExpr
2469 redef fun expr(v)
2470 do
2471 var res = v.variable(self.variable.as(not null))
2472 var mtype = self.mtype.as(not null)
2473 return v.autoadapt(res, mtype)
2474 end
2475 end
2476
2477 redef class AVarAssignExpr
2478 redef fun expr(v)
2479 do
2480 var variable = self.variable.as(not null)
2481 var i = v.expr(self.n_value, variable.declared_type)
2482 v.assign(v.variable(variable), i)
2483 return i
2484 end
2485 end
2486
2487 redef class AVarReassignExpr
2488 redef fun stmt(v)
2489 do
2490 var variable = self.variable.as(not null)
2491 var vari = v.variable(variable)
2492 var value = v.expr(self.n_value, variable.declared_type)
2493 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2494 assert res != null
2495 v.assign(v.variable(variable), res)
2496 end
2497 end
2498
2499 redef class ASelfExpr
2500 redef fun expr(v) do return v.frame.arguments.first
2501 end
2502
2503 redef class AEscapeExpr
2504 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2505 end
2506
2507 redef class AReturnExpr
2508 redef fun stmt(v)
2509 do
2510 var nexpr = self.n_expr
2511 if nexpr != null then
2512 var returnvar = v.frame.returnvar.as(not null)
2513 var i = v.expr(nexpr, returnvar.mtype)
2514 v.assign(returnvar, i)
2515 end
2516 v.add("goto {v.frame.returnlabel.as(not null)};")
2517 end
2518 end
2519
2520 redef class AAbortExpr
2521 redef fun stmt(v) do v.add_abort("Aborted")
2522 end
2523
2524 redef class AIfExpr
2525 redef fun stmt(v)
2526 do
2527 var cond = v.expr_bool(self.n_expr)
2528 v.add("if ({cond})\{")
2529 v.stmt(self.n_then)
2530 v.add("\} else \{")
2531 v.stmt(self.n_else)
2532 v.add("\}")
2533 end
2534
2535 redef fun expr(v)
2536 do
2537 var res = v.new_var(self.mtype.as(not null))
2538 var cond = v.expr_bool(self.n_expr)
2539 v.add("if ({cond})\{")
2540 v.assign(res, v.expr(self.n_then.as(not null), null))
2541 v.add("\} else \{")
2542 v.assign(res, v.expr(self.n_else.as(not null), null))
2543 v.add("\}")
2544 return res
2545 end
2546 end
2547
2548 redef class AIfexprExpr
2549 redef fun expr(v)
2550 do
2551 var res = v.new_var(self.mtype.as(not null))
2552 var cond = v.expr_bool(self.n_expr)
2553 v.add("if ({cond})\{")
2554 v.assign(res, v.expr(self.n_then, null))
2555 v.add("\} else \{")
2556 v.assign(res, v.expr(self.n_else, null))
2557 v.add("\}")
2558 return res
2559 end
2560 end
2561
2562 redef class ADoExpr
2563 redef fun stmt(v)
2564 do
2565 v.stmt(self.n_block)
2566 v.add_escape_label(break_mark)
2567 end
2568 end
2569
2570 redef class AWhileExpr
2571 redef fun stmt(v)
2572 do
2573 v.add("for(;;) \{")
2574 var cond = v.expr_bool(self.n_expr)
2575 v.add("if (!{cond}) break;")
2576 v.stmt(self.n_block)
2577 v.add_escape_label(continue_mark)
2578 v.add("\}")
2579 v.add_escape_label(break_mark)
2580 end
2581 end
2582
2583 redef class ALoopExpr
2584 redef fun stmt(v)
2585 do
2586 v.add("for(;;) \{")
2587 v.stmt(self.n_block)
2588 v.add_escape_label(continue_mark)
2589 v.add("\}")
2590 v.add_escape_label(break_mark)
2591 end
2592 end
2593
2594 redef class AForExpr
2595 redef fun stmt(v)
2596 do
2597 var cl = v.expr(self.n_expr, null)
2598 var it_meth = self.method_iterator
2599 assert it_meth != null
2600 var it = v.compile_callsite(it_meth, [cl])
2601 assert it != null
2602 v.add("for(;;) \{")
2603 var isok_meth = self.method_is_ok
2604 assert isok_meth != null
2605 var ok = v.compile_callsite(isok_meth, [it])
2606 assert ok != null
2607 v.add("if(!{ok}) break;")
2608 if self.variables.length == 1 then
2609 var item_meth = self.method_item
2610 assert item_meth != null
2611 var i = v.compile_callsite(item_meth, [it])
2612 assert i != null
2613 v.assign(v.variable(variables.first), i)
2614 else if self.variables.length == 2 then
2615 var key_meth = self.method_key
2616 assert key_meth != null
2617 var i = v.compile_callsite(key_meth, [it])
2618 assert i != null
2619 v.assign(v.variable(variables[0]), i)
2620 var item_meth = self.method_item
2621 assert item_meth != null
2622 i = v.compile_callsite(item_meth, [it])
2623 assert i != null
2624 v.assign(v.variable(variables[1]), i)
2625 else
2626 abort
2627 end
2628 v.stmt(self.n_block)
2629 v.add_escape_label(continue_mark)
2630 var next_meth = self.method_next
2631 assert next_meth != null
2632 v.compile_callsite(next_meth, [it])
2633 v.add("\}")
2634 v.add_escape_label(break_mark)
2635
2636 var method_finish = self.method_finish
2637 if method_finish != null then
2638 # TODO: Find a way to call this also in long escape (e.g. return)
2639 v.compile_callsite(method_finish, [it])
2640 end
2641 end
2642 end
2643
2644 redef class AAssertExpr
2645 redef fun stmt(v)
2646 do
2647 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2648
2649 var cond = v.expr_bool(self.n_expr)
2650 v.add("if (unlikely(!{cond})) \{")
2651 v.stmt(self.n_else)
2652 var nid = self.n_id
2653 if nid != null then
2654 v.add_abort("Assert '{nid.text}' failed")
2655 else
2656 v.add_abort("Assert failed")
2657 end
2658 v.add("\}")
2659 end
2660 end
2661
2662 redef class AOrExpr
2663 redef fun expr(v)
2664 do
2665 var res = v.new_var(self.mtype.as(not null))
2666 var i1 = v.expr_bool(self.n_expr)
2667 v.add("if ({i1}) \{")
2668 v.add("{res} = 1;")
2669 v.add("\} else \{")
2670 var i2 = v.expr_bool(self.n_expr2)
2671 v.add("{res} = {i2};")
2672 v.add("\}")
2673 return res
2674 end
2675 end
2676
2677 redef class AImpliesExpr
2678 redef fun expr(v)
2679 do
2680 var res = v.new_var(self.mtype.as(not null))
2681 var i1 = v.expr_bool(self.n_expr)
2682 v.add("if (!{i1}) \{")
2683 v.add("{res} = 1;")
2684 v.add("\} else \{")
2685 var i2 = v.expr_bool(self.n_expr2)
2686 v.add("{res} = {i2};")
2687 v.add("\}")
2688 return res
2689 end
2690 end
2691
2692 redef class AAndExpr
2693 redef fun expr(v)
2694 do
2695 var res = v.new_var(self.mtype.as(not null))
2696 var i1 = v.expr_bool(self.n_expr)
2697 v.add("if (!{i1}) \{")
2698 v.add("{res} = 0;")
2699 v.add("\} else \{")
2700 var i2 = v.expr_bool(self.n_expr2)
2701 v.add("{res} = {i2};")
2702 v.add("\}")
2703 return res
2704 end
2705 end
2706
2707 redef class ANotExpr
2708 redef fun expr(v)
2709 do
2710 var cond = v.expr_bool(self.n_expr)
2711 return v.new_expr("!{cond}", self.mtype.as(not null))
2712 end
2713 end
2714
2715 redef class AOrElseExpr
2716 redef fun expr(v)
2717 do
2718 var res = v.new_var(self.mtype.as(not null))
2719 var i1 = v.expr(self.n_expr, null)
2720 v.add("if ({i1}!=NULL) \{")
2721 v.assign(res, i1)
2722 v.add("\} else \{")
2723 var i2 = v.expr(self.n_expr2, null)
2724 v.assign(res, i2)
2725 v.add("\}")
2726 return res
2727 end
2728 end
2729
2730 redef class AIntExpr
2731 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2732 end
2733
2734 redef class AFloatExpr
2735 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2736 end
2737
2738 redef class ACharExpr
2739 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2740 end
2741
2742 redef class AArrayExpr
2743 redef fun expr(v)
2744 do
2745 var mtype = self.mtype.as(MClassType).arguments.first
2746 var array = new Array[RuntimeVariable]
2747 for nexpr in self.n_exprs.n_exprs do
2748 var i = v.expr(nexpr, mtype)
2749 array.add(i)
2750 end
2751 return v.array_instance(array, mtype)
2752 end
2753 end
2754
2755 redef class AStringFormExpr
2756 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2757 end
2758
2759 redef class ASuperstringExpr
2760 redef fun expr(v)
2761 do
2762 var array = new Array[RuntimeVariable]
2763 for ne in self.n_exprs do
2764 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2765 var i = v.expr(ne, null)
2766 array.add(i)
2767 end
2768 var a = v.array_instance(array, v.object_type)
2769 var res = v.send(v.get_property("to_s", a.mtype), [a])
2770 return res
2771 end
2772 end
2773
2774 redef class ACrangeExpr
2775 redef fun expr(v)
2776 do
2777 var i1 = v.expr(self.n_expr, null)
2778 var i2 = v.expr(self.n_expr2, null)
2779 var mtype = self.mtype.as(MClassType)
2780 var res = v.init_instance(mtype)
2781 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2782 return res
2783 end
2784 end
2785
2786 redef class AOrangeExpr
2787 redef fun expr(v)
2788 do
2789 var i1 = v.expr(self.n_expr, null)
2790 var i2 = v.expr(self.n_expr2, null)
2791 var mtype = self.mtype.as(MClassType)
2792 var res = v.init_instance(mtype)
2793 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2794 return res
2795 end
2796 end
2797
2798 redef class ATrueExpr
2799 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2800 end
2801
2802 redef class AFalseExpr
2803 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2804 end
2805
2806 redef class ANullExpr
2807 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2808 end
2809
2810 redef class AIsaExpr
2811 redef fun expr(v)
2812 do
2813 var i = v.expr(self.n_expr, null)
2814 return v.type_test(i, self.cast_type.as(not null), "isa")
2815 end
2816 end
2817
2818 redef class AAsCastExpr
2819 redef fun expr(v)
2820 do
2821 var i = v.expr(self.n_expr, null)
2822 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2823
2824 v.add_cast(i, self.mtype.as(not null), "as")
2825 return i
2826 end
2827 end
2828
2829 redef class AAsNotnullExpr
2830 redef fun expr(v)
2831 do
2832 var i = v.expr(self.n_expr, null)
2833 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2834
2835 if i.mtype.ctype != "val*" then return i
2836
2837 v.add("if (unlikely({i} == NULL)) \{")
2838 v.add_abort("Cast failed")
2839 v.add("\}")
2840 return i
2841 end
2842 end
2843
2844 redef class AParExpr
2845 redef fun expr(v) do return v.expr(self.n_expr, null)
2846 end
2847
2848 redef class AOnceExpr
2849 redef fun expr(v)
2850 do
2851 var mtype = self.mtype.as(not null)
2852 var name = v.get_name("varonce")
2853 var guard = v.get_name(name + "_guard")
2854 v.add_decl("static {mtype.ctype} {name};")
2855 v.add_decl("static int {guard};")
2856 var res = v.new_var(mtype)
2857 v.add("if ({guard}) \{")
2858 v.add("{res} = {name};")
2859 v.add("\} else \{")
2860 var i = v.expr(self.n_expr, mtype)
2861 v.add("{res} = {i};")
2862 v.add("{name} = {res};")
2863 v.add("{guard} = 1;")
2864 v.add("\}")
2865 return res
2866 end
2867 end
2868
2869 redef class ASendExpr
2870 redef fun expr(v)
2871 do
2872 var recv = v.expr(self.n_expr, null)
2873 var callsite = self.callsite.as(not null)
2874 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2875 return v.compile_callsite(callsite, args)
2876 end
2877 end
2878
2879 redef class ASendReassignFormExpr
2880 redef fun stmt(v)
2881 do
2882 var recv = v.expr(self.n_expr, null)
2883 var callsite = self.callsite.as(not null)
2884 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2885
2886 var value = v.expr(self.n_value, null)
2887
2888 var left = v.compile_callsite(callsite, args)
2889 assert left != null
2890
2891 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2892 assert res != null
2893
2894 args.add(res)
2895 v.compile_callsite(self.write_callsite.as(not null), args)
2896 end
2897 end
2898
2899 redef class ASuperExpr
2900 redef fun expr(v)
2901 do
2902 var recv = v.frame.arguments.first
2903
2904 var callsite = self.callsite
2905 if callsite != null then
2906 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2907
2908 # Add additional arguments for the super init call
2909 if args.length == 1 then
2910 for i in [0..callsite.msignature.arity[ do
2911 args.add(v.frame.arguments[i+1])
2912 end
2913 end
2914 # Super init call
2915 var res = v.compile_callsite(callsite, args)
2916 return res
2917 end
2918
2919 var mpropdef = self.mpropdef.as(not null)
2920 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2921 if args.length == 1 then
2922 args = v.frame.arguments
2923 end
2924
2925 # stantard call-next-method
2926 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
2927 end
2928 end
2929
2930 redef class ANewExpr
2931 redef fun expr(v)
2932 do
2933 var mtype = self.recvtype
2934 assert mtype != null
2935 var recv
2936 var ctype = mtype.ctype
2937 if mtype.mclass.name == "NativeArray" then
2938 assert self.n_args.n_exprs.length == 1
2939 var l = v.expr(self.n_args.n_exprs.first, null)
2940 assert mtype isa MGenericType
2941 var elttype = mtype.arguments.first
2942 return v.native_array_instance(elttype, l)
2943 else if ctype == "val*" then
2944 recv = v.init_instance(mtype)
2945 else if ctype == "char*" then
2946 recv = v.new_expr("NULL/*special!*/", mtype)
2947 else
2948 recv = v.new_expr("({ctype})0/*special!*/", mtype)
2949 end
2950
2951 var callsite = self.callsite.as(not null)
2952 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2953 var res2 = v.compile_callsite(callsite, args)
2954 if res2 != null then
2955 #self.debug("got {res2} from {mproperty}. drop {recv}")
2956 return res2
2957 end
2958 return recv
2959 end
2960 end
2961
2962 redef class AAttrExpr
2963 redef fun expr(v)
2964 do
2965 var recv = v.expr(self.n_expr, null)
2966 var mproperty = self.mproperty.as(not null)
2967 return v.read_attribute(mproperty, recv)
2968 end
2969 end
2970
2971 redef class AAttrAssignExpr
2972 redef fun expr(v)
2973 do
2974 var recv = v.expr(self.n_expr, null)
2975 var i = v.expr(self.n_value, null)
2976 var mproperty = self.mproperty.as(not null)
2977 v.write_attribute(mproperty, recv, i)
2978 return i
2979 end
2980 end
2981
2982 redef class AAttrReassignExpr
2983 redef fun stmt(v)
2984 do
2985 var recv = v.expr(self.n_expr, null)
2986 var value = v.expr(self.n_value, null)
2987 var mproperty = self.mproperty.as(not null)
2988 var attr = v.read_attribute(mproperty, recv)
2989 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2990 assert res != null
2991 v.write_attribute(mproperty, recv, res)
2992 end
2993 end
2994
2995 redef class AIssetAttrExpr
2996 redef fun expr(v)
2997 do
2998 var recv = v.expr(self.n_expr, null)
2999 var mproperty = self.mproperty.as(not null)
3000 return v.isset_attribute(mproperty, recv)
3001 end
3002 end
3003
3004 redef class ADebugTypeExpr
3005 redef fun stmt(v)
3006 do
3007 # do nothing
3008 end
3009 end
3010
3011 # Utils
3012
3013 redef class Array[E]
3014 # Return a new `Array` with the elements only contened in self and not in `o`
3015 fun -(o: Array[E]): Array[E] do
3016 var res = new Array[E]
3017 for e in self do if not o.has(e) then res.add(e)
3018 return res
3019 end
3020 end
3021
3022 redef class MModule
3023 # Return the name of the global C identifier associated to `self`.
3024 # This name is used to prefix files and other C identifiers associated with `self`.
3025 var c_name: String is lazy do
3026 var g = mgroup
3027 var res
3028 if g != null and g.mproject.name != name then
3029 res = g.mproject.name.to_cmangle + "__" + name.to_cmangle
3030 else
3031 res = name.to_cmangle
3032 end
3033 return res
3034 end
3035
3036 # All `MProperty` associated to all `MClassDef` of `mclass`
3037 fun properties(mclass: MClass): Set[MProperty] do
3038 if not self.properties_cache.has_key(mclass) then
3039 var properties = new HashSet[MProperty]
3040 var parents = new Array[MClass]
3041 if self.flatten_mclass_hierarchy.has(mclass) then
3042 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3043 end
3044 for parent in parents do
3045 properties.add_all(self.properties(parent))
3046 end
3047 for mclassdef in mclass.mclassdefs do
3048 if not self.in_importation <= mclassdef.mmodule then continue
3049 for mprop in mclassdef.intro_mproperties do
3050 properties.add(mprop)
3051 end
3052 end
3053 self.properties_cache[mclass] = properties
3054 end
3055 return properties_cache[mclass]
3056 end
3057 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3058
3059 # Write FFI and nitni results to file
3060 fun finalize_ffi(c: AbstractCompiler) do end
3061
3062 # Give requided addinional system libraries (as given to LD_LIBS)
3063 # Note: can return null instead of an empty set
3064 fun collect_linker_libs: nullable Set[String] do return null
3065 end
3066
3067 # Create a tool context to handle options and paths
3068 var toolcontext = new ToolContext
3069
3070 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
3071
3072 # We do not add other options, so process them now!
3073 toolcontext.process_options(args)
3074
3075 # We need a model to collect stufs
3076 var model = new Model
3077 # An a model builder to parse files
3078 var modelbuilder = new ModelBuilder(model, toolcontext)
3079
3080 var arguments = toolcontext.option_context.rest
3081 if arguments.length > 1 and toolcontext.opt_output.value != null then
3082 print "Error: --output needs a single source file. Do you prefer --dir?"
3083 exit 1
3084 end
3085
3086 # Here we load an process all modules passed on the command line
3087 var mmodules = modelbuilder.parse(arguments)
3088
3089 if mmodules.is_empty then return
3090 modelbuilder.run_phases
3091
3092 for mmodule in mmodules do
3093 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3094 var ms = [mmodule]
3095 toolcontext.run_global_phases(ms)
3096 end