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