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