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