compiler: introduce and use `MType::is_c_primitive`
[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 class named `name` or abort
1082 fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
1083
1084 # Force to get the primitive property named `name` in the instance `recv` or abort
1085 fun get_property(name: String, recv: MType): MMethod
1086 do
1087 assert recv isa MClassType
1088 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1089 end
1090
1091 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1092 do
1093 var initializers = callsite.mpropdef.initializers
1094 if not initializers.is_empty then
1095 var recv = arguments.first
1096
1097 var i = 1
1098 for p in initializers do
1099 if p isa MMethod then
1100 var args = [recv]
1101 for x in p.intro.msignature.mparameters do
1102 args.add arguments[i]
1103 i += 1
1104 end
1105 self.send(p, args)
1106 else if p isa MAttribute then
1107 self.write_attribute(p, recv, arguments[i])
1108 i += 1
1109 else abort
1110 end
1111 assert i == arguments.length
1112
1113 return self.send(callsite.mproperty, [recv])
1114 end
1115
1116 return self.send(callsite.mproperty, arguments)
1117 end
1118
1119 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1120
1121 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1122
1123 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1124
1125 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1126 # This method is used to manage varargs in signatures and returns the real array
1127 # of runtime variables to use in the call.
1128 fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1129 do
1130 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1131 var res = new Array[RuntimeVariable]
1132 res.add(recv)
1133
1134 if args.is_empty then return res
1135
1136 var vararg_rank = msignature.vararg_rank
1137 var vararg_len = args.length - msignature.arity
1138 if vararg_len < 0 then vararg_len = 0
1139
1140 for i in [0..msignature.arity[ do
1141 if i == vararg_rank then
1142 var ne = args[i]
1143 if ne isa AVarargExpr then
1144 var e = self.expr(ne.n_expr, null)
1145 res.add(e)
1146 continue
1147 end
1148 var vararg = new Array[RuntimeVariable]
1149 for j in [vararg_rank..vararg_rank+vararg_len] do
1150 var e = self.expr(args[j], null)
1151 vararg.add(e)
1152 end
1153 var elttype = msignature.mparameters[vararg_rank].mtype
1154 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1155 res.add(arg)
1156 else
1157 var j = i
1158 if i > vararg_rank then j += vararg_len
1159 var e = self.expr(args[j], null)
1160 res.add(e)
1161 end
1162 end
1163 return res
1164 end
1165
1166 # Type handling
1167
1168 # Anchor a type to the main module and the current receiver
1169 fun anchor(mtype: MType): MType
1170 do
1171 if not mtype.need_anchor then return mtype
1172 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1173 end
1174
1175 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1176 do
1177 if not mtype.need_anchor then return mtype
1178 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1179 end
1180
1181 # Unsafely cast a value to a new type
1182 # ie the result share the same C variable but my have a different mcasttype
1183 # NOTE: if the adaptation is useless then `value` is returned as it.
1184 # ENSURE: `result.name == value.name`
1185 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1186 do
1187 mtype = self.anchor(mtype)
1188 var valmtype = value.mcasttype
1189 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1190 return value
1191 end
1192
1193 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1194 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1195 return res
1196 else
1197 var res = new RuntimeVariable(value.name, valmtype, mtype)
1198 return res
1199 end
1200 end
1201
1202 # Generate a super call from a method definition
1203 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1204
1205 # Adapt the arguments of a method according to targetted `MMethodDef`
1206 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1207
1208 # Unbox all the arguments of a method when implemented `extern` or `intern`
1209 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1210
1211 # Box or unbox a value to another type iff a C type conversion is needed
1212 # ENSURE: `result.mtype.ctype == mtype.ctype`
1213 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1214
1215 # Box extern classes to be used in the generated code
1216 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1217
1218 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1219 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1220
1221 # Generate a polymorphic subtype test
1222 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1223
1224 # Generate the code required to dynamically check if 2 objects share the same runtime type
1225 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1226
1227 # Generate a Nit "is" for two runtime_variables
1228 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1229
1230 # Sends
1231
1232 # Generate a static call on a method definition
1233 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1234
1235 # Generate a polymorphic send for the method `m` and the arguments `args`
1236 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1237
1238 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1239 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1240 do
1241 assert t isa MClassType
1242 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1243 return self.call(propdef, t, args)
1244 end
1245
1246 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1247 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1248 do
1249 assert t isa MClassType
1250 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1251 return self.call(m, t, args)
1252 end
1253
1254 # Attributes handling
1255
1256 # Generate a polymorphic attribute is_set test
1257 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1258
1259 # Generate a polymorphic attribute read
1260 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1261
1262 # Generate a polymorphic attribute write
1263 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1264
1265 # Checks
1266
1267 # Add a check and an abort for a null receiver if needed
1268 fun check_recv_notnull(recv: RuntimeVariable)
1269 do
1270 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1271
1272 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1273 if maybenull then
1274 self.add("if (unlikely({recv} == NULL)) \{")
1275 self.add_abort("Receiver is null")
1276 self.add("\}")
1277 end
1278 end
1279
1280 # Names handling
1281
1282 private var names = new HashSet[String]
1283 private var last: Int = 0
1284
1285 # Return a new name based on `s` and unique in the visitor
1286 fun get_name(s: String): String
1287 do
1288 if not self.names.has(s) then
1289 self.names.add(s)
1290 return s
1291 end
1292 var i = self.last + 1
1293 loop
1294 var s2 = s + i.to_s
1295 if not self.names.has(s2) then
1296 self.last = i
1297 self.names.add(s2)
1298 return s2
1299 end
1300 i = i + 1
1301 end
1302 end
1303
1304 # Return an unique and stable identifier associated with an escapemark
1305 fun escapemark_name(e: nullable EscapeMark): String
1306 do
1307 assert e != null
1308 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1309 var name = e.name
1310 if name == null then name = "label"
1311 name = get_name(name)
1312 frame.escapemark_names[e] = name
1313 return name
1314 end
1315
1316 # Insert a C label for associated with an escapemark
1317 fun add_escape_label(e: nullable EscapeMark)
1318 do
1319 if e == null then return
1320 if e.escapes.is_empty then return
1321 add("BREAK_{escapemark_name(e)}: (void)0;")
1322 end
1323
1324 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1325 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1326 fun class_name_string(value: RuntimeVariable): String is abstract
1327
1328 # Variables handling
1329
1330 protected var variables = new HashMap[Variable, RuntimeVariable]
1331
1332 # Return the local runtime_variable associated to a Nit local variable
1333 fun variable(variable: Variable): RuntimeVariable
1334 do
1335 if self.variables.has_key(variable) then
1336 return self.variables[variable]
1337 else
1338 var name = self.get_name("var_{variable.name}")
1339 var mtype = variable.declared_type.as(not null)
1340 mtype = self.anchor(mtype)
1341 var res = new RuntimeVariable(name, mtype, mtype)
1342 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1343 self.variables[variable] = res
1344 return res
1345 end
1346 end
1347
1348 # Return a new uninitialized local runtime_variable
1349 fun new_var(mtype: MType): RuntimeVariable
1350 do
1351 mtype = self.anchor(mtype)
1352 var name = self.get_name("var")
1353 var res = new RuntimeVariable(name, mtype, mtype)
1354 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1355 return res
1356 end
1357
1358 # The difference with `new_var` is the C static type of the local variable
1359 fun new_var_extern(mtype: MType): RuntimeVariable
1360 do
1361 mtype = self.anchor(mtype)
1362 var name = self.get_name("var")
1363 var res = new RuntimeVariable(name, mtype, mtype)
1364 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1365 return res
1366 end
1367
1368 # Return a new uninitialized named runtime_variable
1369 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1370 do
1371 mtype = self.anchor(mtype)
1372 var res = new RuntimeVariable(name, mtype, mtype)
1373 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1374 return res
1375 end
1376
1377 # Correctly assign a left and a right value
1378 # Boxing and unboxing is performed if required
1379 fun assign(left, right: RuntimeVariable)
1380 do
1381 right = self.autobox(right, left.mtype)
1382 self.add("{left} = {right};")
1383 end
1384
1385 # Generate instances
1386
1387 # Generate a alloc-instance + init-attributes
1388 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1389
1390 # Allocate and init attributes of an instance of a standard or extern class
1391 #
1392 # Does not support universals and the pseudo-internal `NativeArray` class.
1393 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1394 do
1395 var recv
1396 var ctype = mtype.ctype
1397 assert mtype.mclass.name != "NativeArray"
1398 if not mtype.is_c_primitive then
1399 recv = init_instance(mtype)
1400 else if ctype == "char*" then
1401 recv = new_expr("NULL/*special!*/", mtype)
1402 else
1403 recv = new_expr("({ctype})0/*special!*/", mtype)
1404 end
1405 return recv
1406 end
1407
1408 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1409 fun set_finalizer(recv: RuntimeVariable)
1410 do
1411 var mtype = recv.mtype
1412 var finalizable_type = compiler.mainmodule.finalizable_type
1413 if finalizable_type != null and not mtype.need_anchor and
1414 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1415 add "gc_register_finalizer({recv});"
1416 end
1417 end
1418
1419 # Generate an integer value
1420 fun int_instance(value: Int): RuntimeVariable
1421 do
1422 var res = self.new_var(self.get_class("Int").mclass_type)
1423 self.add("{res} = {value};")
1424 return res
1425 end
1426
1427 # Generate an integer value
1428 fun bool_instance(value: Bool): RuntimeVariable
1429 do
1430 var res = self.new_var(self.get_class("Bool").mclass_type)
1431 if value then
1432 self.add("{res} = 1;")
1433 else
1434 self.add("{res} = 0;")
1435 end
1436 return res
1437 end
1438
1439 # Generate a string value
1440 fun string_instance(string: String): RuntimeVariable
1441 do
1442 var mtype = self.get_class("String").mclass_type
1443 var name = self.get_name("varonce")
1444 self.add_decl("static {mtype.ctype} {name};")
1445 var res = self.new_var(mtype)
1446 self.add("if (likely({name}!=NULL)) \{")
1447 self.add("{res} = {name};")
1448 self.add("\} else \{")
1449 var native_mtype = self.get_class("NativeString").mclass_type
1450 var nat = self.new_var(native_mtype)
1451 self.add("{nat} = \"{string.escape_to_c}\";")
1452 var length = self.int_instance(string.length)
1453 self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
1454 self.add("{name} = {res};")
1455 self.add("\}")
1456 return res
1457 end
1458
1459 fun value_instance(object: Object): RuntimeVariable
1460 do
1461 if object isa Int then
1462 return int_instance(object)
1463 else if object isa Bool then
1464 return bool_instance(object)
1465 else if object isa String then
1466 return string_instance(object)
1467 else
1468 abort
1469 end
1470 end
1471
1472 # Generate an array value
1473 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1474
1475 # Get an instance of a array for a vararg
1476 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1477
1478 # Code generation
1479
1480 # Add a line in the main part of the generated C
1481 fun add(s: String) do self.writer.lines.add(s)
1482
1483 # Add a line in the
1484 # (used for local or global declaration)
1485 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1486
1487 # Request the presence of a global declaration
1488 fun require_declaration(key: String)
1489 do
1490 var reqs = self.writer.file.required_declarations
1491 if reqs.has(key) then return
1492 reqs.add(key)
1493 var node = current_node
1494 if node != null then compiler.requirers_of_declarations[key] = node
1495 end
1496
1497 # Add a declaration in the local-header
1498 # The declaration is ensured to be present once
1499 fun declare_once(s: String)
1500 do
1501 self.compiler.provide_declaration(s, s)
1502 self.require_declaration(s)
1503 end
1504
1505 # Look for a needed .h and .c file for a given module
1506 # This is used for the legacy FFI
1507 fun add_extern(mmodule: MModule)
1508 do
1509 var file = mmodule.location.file.filename
1510 file = file.strip_extension(".nit")
1511 var tryfile = file + ".nit.h"
1512 if tryfile.file_exists then
1513 self.declare_once("#include \"{tryfile.basename("")}\"")
1514 self.compiler.files_to_copy.add(tryfile)
1515 end
1516 tryfile = file + "_nit.h"
1517 if tryfile.file_exists then
1518 self.declare_once("#include \"{tryfile.basename("")}\"")
1519 self.compiler.files_to_copy.add(tryfile)
1520 end
1521
1522 if self.compiler.seen_extern.has(file) then return
1523 self.compiler.seen_extern.add(file)
1524 tryfile = file + ".nit.c"
1525 if not tryfile.file_exists then
1526 tryfile = file + "_nit.c"
1527 if not tryfile.file_exists then return
1528 end
1529 var f = new ExternCFile(tryfile.basename(""), "")
1530 self.compiler.extern_bodies.add(f)
1531 self.compiler.files_to_copy.add(tryfile)
1532 end
1533
1534 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1535 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1536 do
1537 var res = new_var(mtype)
1538 self.add("{res} = {cexpr};")
1539 return res
1540 end
1541
1542 # Generate generic abort
1543 # used by aborts, asserts, casts, etc.
1544 fun add_abort(message: String)
1545 do
1546 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1547 add_raw_abort
1548 end
1549
1550 fun add_raw_abort
1551 do
1552 if self.current_node != null and self.current_node.location.file != null and
1553 self.current_node.location.file.mmodule != null then
1554 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1555 self.require_declaration(f)
1556 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1557 else
1558 self.add("PRINT_ERROR(\"\\n\");")
1559 end
1560 self.add("show_backtrace(1);")
1561 end
1562
1563 # Add a dynamic cast
1564 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1565 do
1566 var res = self.type_test(value, mtype, tag)
1567 self.add("if (unlikely(!{res})) \{")
1568 var cn = self.class_name_string(value)
1569 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1570 self.add_raw_abort
1571 self.add("\}")
1572 end
1573
1574 # Generate a return with the value `s`
1575 fun ret(s: RuntimeVariable)
1576 do
1577 self.assign(self.frame.returnvar.as(not null), s)
1578 self.add("goto {self.frame.returnlabel.as(not null)};")
1579 end
1580
1581 # Compile a statement (if any)
1582 fun stmt(nexpr: nullable AExpr)
1583 do
1584 if nexpr == null then return
1585
1586 var narray = nexpr.comprehension
1587 if narray != null then
1588 var recv = frame.comprehension.as(not null)
1589 var val = expr(nexpr, narray.element_mtype)
1590 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1591 return
1592 end
1593
1594 var old = self.current_node
1595 self.current_node = nexpr
1596 nexpr.stmt(self)
1597 self.current_node = old
1598 end
1599
1600 # Compile an expression an return its result
1601 # `mtype` is the expected return type, pass null if no specific type is expected.
1602 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1603 do
1604 var old = self.current_node
1605 self.current_node = nexpr
1606 var res = nexpr.expr(self).as(not null)
1607 if mtype != null then
1608 mtype = self.anchor(mtype)
1609 res = self.autobox(res, mtype)
1610 end
1611 res = autoadapt(res, nexpr.mtype.as(not null))
1612 var implicit_cast_to = nexpr.implicit_cast_to
1613 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1614 add_cast(res, implicit_cast_to, "auto")
1615 res = autoadapt(res, implicit_cast_to)
1616 end
1617 self.current_node = old
1618 return res
1619 end
1620
1621 # Alias for `self.expr(nexpr, self.bool_type)`
1622 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1623
1624 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1625 fun debug(message: String)
1626 do
1627 var node = self.current_node
1628 if node == null then
1629 print "?: {message}"
1630 else
1631 node.debug(message)
1632 end
1633 self.add("/* DEBUG: {message} */")
1634 end
1635 end
1636
1637 # A C function associated to a Nit method
1638 # Because of customization, a given Nit method can be compiler more that once
1639 abstract class AbstractRuntimeFunction
1640
1641 type COMPILER: AbstractCompiler
1642 type VISITOR: AbstractCompilerVisitor
1643
1644 # The associated Nit method
1645 var mmethoddef: MMethodDef
1646
1647 # The mangled c name of the runtime_function
1648 # Subclasses should redefine `build_c_name` instead
1649 fun c_name: String
1650 do
1651 var res = self.c_name_cache
1652 if res != null then return res
1653 res = self.build_c_name
1654 self.c_name_cache = res
1655 return res
1656 end
1657
1658 # Non cached version of `c_name`
1659 protected fun build_c_name: String is abstract
1660
1661 protected var c_name_cache: nullable String = null is writable
1662
1663 # Implements a call of the runtime_function
1664 # May inline the body or generate a C function call
1665 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1666
1667 # Generate the code for the `AbstractRuntimeFunction`
1668 # Warning: compile more than once compilation makes CC unhappy
1669 fun compile_to_c(compiler: COMPILER) is abstract
1670 end
1671
1672 # A runtime variable hold a runtime value in C.
1673 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1674 #
1675 # 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.
1676 class RuntimeVariable
1677 # The name of the variable in the C code
1678 var name: String
1679
1680 # The static type of the variable (as declard in C)
1681 var mtype: MType
1682
1683 # The current casted type of the variable (as known in Nit)
1684 var mcasttype: MType is writable
1685
1686 # If the variable exaclty a mcasttype?
1687 # false (usual value) means that the variable is a mcasttype or a subtype.
1688 var is_exact: Bool = false is writable
1689
1690 init
1691 do
1692 assert not mtype.need_anchor
1693 assert not mcasttype.need_anchor
1694 end
1695
1696 redef fun to_s do return name
1697
1698 redef fun inspect
1699 do
1700 var exact_str
1701 if self.is_exact then
1702 exact_str = " exact"
1703 else
1704 exact_str = ""
1705 end
1706 var type_str
1707 if self.mtype == self.mcasttype then
1708 type_str = "{mtype}{exact_str}"
1709 else
1710 type_str = "{mtype}({mcasttype}{exact_str})"
1711 end
1712 return "<{name}:{type_str}>"
1713 end
1714 end
1715
1716 # The static context of a visited property in a `AbstractCompilerVisitor`
1717 class StaticFrame
1718
1719 type VISITOR: AbstractCompilerVisitor
1720
1721 # The associated visitor
1722 var visitor: VISITOR
1723
1724 # The executed property.
1725 # A Method in case of a call, an attribute in case of a default initialization.
1726 var mpropdef: MPropDef
1727
1728 # The static type of the receiver
1729 var receiver: MClassType
1730
1731 # Arguments of the method (the first is the receiver)
1732 var arguments: Array[RuntimeVariable]
1733
1734 # The runtime_variable associated to the return (in a function)
1735 var returnvar: nullable RuntimeVariable = null is writable
1736
1737 # The label at the end of the property
1738 var returnlabel: nullable String = null is writable
1739
1740 # Labels associated to a each escapemarks.
1741 # Because of inlinings, escape-marks must be associated to their context (the frame)
1742 private var escapemark_names = new HashMap[EscapeMark, String]
1743
1744 # The array comprehension currently filled, if any
1745 private var comprehension: nullable RuntimeVariable = null
1746 end
1747
1748 redef class MType
1749 # Return the C type associated to a given Nit static type
1750 fun ctype: String do return "val*"
1751
1752 # C type outside of the compiler code and in boxes
1753 fun ctype_extern: String do return "val*"
1754
1755 # Short name of the `ctype` to use in unions
1756 fun ctypename: String do return "val"
1757
1758 # Is the associated C type a primitive one?
1759 #
1760 # ENSURE `result == (ctype != "val*")`
1761 fun is_c_primitive: Bool do return false
1762 end
1763
1764 redef class MClassType
1765
1766 redef var ctype is lazy do
1767 if mclass.name == "Int" then
1768 return "long"
1769 else if mclass.name == "Bool" then
1770 return "short int"
1771 else if mclass.name == "Char" then
1772 return "char"
1773 else if mclass.name == "Float" then
1774 return "double"
1775 else if mclass.name == "NativeString" then
1776 return "char*"
1777 else if mclass.name == "NativeArray" then
1778 return "val*"
1779 else
1780 return "val*"
1781 end
1782 end
1783
1784 redef var is_c_primitive is lazy do return ctype != "val*"
1785
1786 redef fun ctype_extern: String
1787 do
1788 if mclass.kind == extern_kind then
1789 return "void*"
1790 else
1791 return ctype
1792 end
1793 end
1794
1795 redef fun ctypename: String
1796 do
1797 if mclass.name == "Int" then
1798 return "l"
1799 else if mclass.name == "Bool" then
1800 return "s"
1801 else if mclass.name == "Char" then
1802 return "c"
1803 else if mclass.name == "Float" then
1804 return "d"
1805 else if mclass.name == "NativeString" then
1806 return "str"
1807 else if mclass.name == "NativeArray" then
1808 #return "{self.arguments.first.ctype}*"
1809 return "val"
1810 else
1811 return "val"
1812 end
1813 end
1814 end
1815
1816 redef class MPropDef
1817 type VISITOR: AbstractCompilerVisitor
1818 end
1819
1820 redef class MMethodDef
1821 # Can the body be inlined?
1822 fun can_inline(v: VISITOR): Bool
1823 do
1824 if is_abstract then return true
1825 var modelbuilder = v.compiler.modelbuilder
1826 var node = modelbuilder.mpropdef2node(self)
1827 if node isa APropdef then
1828 return node.can_inline
1829 else if node isa AClassdef then
1830 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
1831 return true
1832 else
1833 abort
1834 end
1835 end
1836
1837 # Inline the body in another visitor
1838 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1839 do
1840 var modelbuilder = v.compiler.modelbuilder
1841 var val = constant_value
1842 var node = modelbuilder.mpropdef2node(self)
1843
1844 if is_abstract then
1845 var cn = v.class_name_string(arguments.first)
1846 v.current_node = node
1847 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
1848 v.add_raw_abort
1849 return null
1850 end
1851
1852 if node isa APropdef then
1853 var oldnode = v.current_node
1854 v.current_node = node
1855 self.compile_parameter_check(v, arguments)
1856 node.compile_to_c(v, self, arguments)
1857 v.current_node = oldnode
1858 else if node isa AClassdef then
1859 var oldnode = v.current_node
1860 v.current_node = node
1861 self.compile_parameter_check(v, arguments)
1862 node.compile_to_c(v, self, arguments)
1863 v.current_node = oldnode
1864 else if val != null then
1865 v.ret(v.value_instance(val))
1866 else
1867 abort
1868 end
1869 return null
1870 end
1871
1872 # Generate type checks in the C code to check covariant parameters
1873 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
1874 do
1875 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
1876
1877 for i in [0..msignature.arity[ do
1878 # skip test for vararg since the array is instantiated with the correct polymorphic type
1879 if msignature.vararg_rank == i then continue
1880
1881 # skip if the cast is not required
1882 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
1883 if not origmtype.need_anchor then continue
1884
1885 # get the parameter type
1886 var mtype = self.msignature.mparameters[i].mtype
1887
1888 # generate the cast
1889 # note that v decides if and how to implements the cast
1890 v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
1891 v.add_cast(arguments[i+1], mtype, "covariance")
1892 end
1893 end
1894 end
1895
1896 # Node visit
1897
1898 redef class APropdef
1899 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1900 do
1901 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
1902 debug("Not yet implemented")
1903 end
1904
1905 fun can_inline: Bool do return true
1906 end
1907
1908 redef class AMethPropdef
1909 redef fun compile_to_c(v, mpropdef, arguments)
1910 do
1911 # Call the implicit super-init
1912 var auto_super_inits = self.auto_super_inits
1913 if auto_super_inits != null then
1914 var args = [arguments.first]
1915 for auto_super_init in auto_super_inits do
1916 assert auto_super_init.mproperty != mpropdef.mproperty
1917 args.clear
1918 for i in [0..auto_super_init.msignature.arity+1[ do
1919 args.add(arguments[i])
1920 end
1921 assert auto_super_init.mproperty != mpropdef.mproperty
1922 v.compile_callsite(auto_super_init, args)
1923 end
1924 end
1925 if auto_super_call then
1926 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1927 end
1928
1929 # Try special compilation
1930 if mpropdef.is_intern then
1931 if compile_intern_to_c(v, mpropdef, arguments) then return
1932 else if mpropdef.is_extern then
1933 if mpropdef.mproperty.is_init then
1934 if compile_externinit_to_c(v, mpropdef, arguments) then return
1935 else
1936 if compile_externmeth_to_c(v, mpropdef, arguments) then return
1937 end
1938 end
1939
1940 # Compile block if any
1941 var n_block = n_block
1942 if n_block != null then
1943 for i in [0..mpropdef.msignature.arity[ do
1944 var variable = self.n_signature.n_params[i].variable.as(not null)
1945 v.assign(v.variable(variable), arguments[i+1])
1946 end
1947 v.stmt(n_block)
1948 return
1949 end
1950
1951 # We have a problem
1952 var cn = v.class_name_string(arguments.first)
1953 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
1954 v.add_raw_abort
1955 end
1956
1957 redef fun can_inline
1958 do
1959 if self.auto_super_inits != null then return false
1960 var nblock = self.n_block
1961 if nblock == null then return true
1962 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1963 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1964 return false
1965 end
1966
1967 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
1968 do
1969 var pname = mpropdef.mproperty.name
1970 var cname = mpropdef.mclassdef.mclass.name
1971 var ret = mpropdef.msignature.return_mtype
1972 if ret != null then
1973 ret = v.resolve_for(ret, arguments.first)
1974 end
1975 if pname != "==" and pname != "!=" then
1976 v.adapt_signature(mpropdef, arguments)
1977 v.unbox_signature_extern(mpropdef, arguments)
1978 end
1979 if cname == "Int" then
1980 if pname == "output" then
1981 v.add("printf(\"%ld\\n\", {arguments.first});")
1982 return true
1983 else if pname == "object_id" then
1984 v.ret(arguments.first)
1985 return true
1986 else if pname == "+" then
1987 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1988 return true
1989 else if pname == "-" then
1990 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1991 return true
1992 else if pname == "unary -" then
1993 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1994 return true
1995 else if pname == "*" then
1996 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1997 return true
1998 else if pname == "/" then
1999 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2000 return true
2001 else if pname == "%" then
2002 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2003 return true
2004 else if pname == "lshift" then
2005 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2006 return true
2007 else if pname == "rshift" then
2008 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2009 return true
2010 else if pname == "==" then
2011 v.ret(v.equal_test(arguments[0], arguments[1]))
2012 return true
2013 else if pname == "!=" then
2014 var res = v.equal_test(arguments[0], arguments[1])
2015 v.ret(v.new_expr("!{res}", ret.as(not null)))
2016 return true
2017 else if pname == "<" then
2018 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2019 return true
2020 else if pname == ">" then
2021 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2022 return true
2023 else if pname == "<=" then
2024 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2025 return true
2026 else if pname == ">=" then
2027 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2028 return true
2029 else if pname == "to_f" then
2030 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2031 return true
2032 else if pname == "ascii" then
2033 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
2034 return true
2035 end
2036 else if cname == "Char" then
2037 if pname == "output" then
2038 v.add("printf(\"%c\", {arguments.first});")
2039 return true
2040 else if pname == "object_id" then
2041 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2042 return true
2043 else if pname == "successor" then
2044 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2045 return true
2046 else if pname == "predecessor" then
2047 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2048 return true
2049 else if pname == "==" then
2050 v.ret(v.equal_test(arguments[0], arguments[1]))
2051 return true
2052 else if pname == "!=" then
2053 var res = v.equal_test(arguments[0], arguments[1])
2054 v.ret(v.new_expr("!{res}", ret.as(not null)))
2055 return true
2056 else if pname == "<" then
2057 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2058 return true
2059 else if pname == ">" then
2060 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2061 return true
2062 else if pname == "<=" then
2063 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2064 return true
2065 else if pname == ">=" then
2066 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2067 return true
2068 else if pname == "to_i" then
2069 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2070 return true
2071 else if pname == "ascii" then
2072 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2073 return true
2074 end
2075 else if cname == "Bool" then
2076 if pname == "output" then
2077 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2078 return true
2079 else if pname == "object_id" then
2080 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2081 return true
2082 else if pname == "==" then
2083 v.ret(v.equal_test(arguments[0], arguments[1]))
2084 return true
2085 else if pname == "!=" then
2086 var res = v.equal_test(arguments[0], arguments[1])
2087 v.ret(v.new_expr("!{res}", ret.as(not null)))
2088 return true
2089 end
2090 else if cname == "Float" then
2091 if pname == "output" then
2092 v.add("printf(\"%f\\n\", {arguments.first});")
2093 return true
2094 else if pname == "object_id" then
2095 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2096 return true
2097 else if pname == "+" then
2098 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2099 return true
2100 else if pname == "-" then
2101 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2102 return true
2103 else if pname == "unary -" then
2104 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2105 return true
2106 else if pname == "succ" then
2107 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2108 return true
2109 else if pname == "prec" then
2110 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2111 return true
2112 else if pname == "*" then
2113 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2114 return true
2115 else if pname == "/" then
2116 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2117 return true
2118 else if pname == "==" then
2119 v.ret(v.equal_test(arguments[0], arguments[1]))
2120 return true
2121 else if pname == "!=" then
2122 var res = v.equal_test(arguments[0], arguments[1])
2123 v.ret(v.new_expr("!{res}", ret.as(not null)))
2124 return true
2125 else if pname == "<" then
2126 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2127 return true
2128 else if pname == ">" then
2129 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2130 return true
2131 else if pname == "<=" then
2132 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2133 return true
2134 else if pname == ">=" then
2135 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2136 return true
2137 else if pname == "to_i" then
2138 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2139 return true
2140 end
2141 else if cname == "NativeString" then
2142 if pname == "[]" then
2143 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2144 return true
2145 else if pname == "[]=" then
2146 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2147 return true
2148 else if pname == "copy_to" then
2149 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2150 return true
2151 else if pname == "atoi" then
2152 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2153 return true
2154 else if pname == "new" then
2155 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2156 return true
2157 end
2158 else if cname == "NativeArray" then
2159 v.native_array_def(pname, ret, arguments)
2160 return true
2161 end
2162 if pname == "exit" then
2163 v.add("exit({arguments[1]});")
2164 return true
2165 else if pname == "sys" then
2166 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2167 return true
2168 else if pname == "calloc_string" then
2169 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2170 return true
2171 else if pname == "calloc_array" then
2172 v.calloc_array(ret.as(not null), arguments)
2173 return true
2174 else if pname == "object_id" then
2175 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2176 return true
2177 else if pname == "is_same_type" then
2178 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2179 return true
2180 else if pname == "is_same_instance" then
2181 v.ret(v.equal_test(arguments[0], arguments[1]))
2182 return true
2183 else if pname == "output_class_name" then
2184 var nat = v.class_name_string(arguments.first)
2185 v.add("printf(\"%s\\n\", {nat});")
2186 return true
2187 else if pname == "native_class_name" then
2188 var nat = v.class_name_string(arguments.first)
2189 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2190 return true
2191 else if pname == "force_garbage_collection" then
2192 v.add("nit_gcollect();")
2193 return true
2194 else if pname == "native_argc" then
2195 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2196 return true
2197 else if pname == "native_argv" then
2198 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2199 return true
2200 end
2201 return false
2202 end
2203
2204 # Compile an extern method
2205 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2206 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2207 do
2208 var externname
2209 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2210 if at != null and at.n_args.length == 1 then
2211 externname = at.arg_as_string(v.compiler.modelbuilder)
2212 if externname == null then return false
2213 else
2214 return false
2215 end
2216 v.add_extern(mpropdef.mclassdef.mmodule)
2217 var res: nullable RuntimeVariable = null
2218 var ret = mpropdef.msignature.return_mtype
2219 if ret != null then
2220 ret = v.resolve_for(ret, arguments.first)
2221 res = v.new_var_extern(ret)
2222 end
2223 v.adapt_signature(mpropdef, arguments)
2224 v.unbox_signature_extern(mpropdef, arguments)
2225
2226 if res == null then
2227 v.add("{externname}({arguments.join(", ")});")
2228 else
2229 v.add("{res} = {externname}({arguments.join(", ")});")
2230 res = v.box_extern(res, ret.as(not null))
2231 v.ret(res)
2232 end
2233 return true
2234 end
2235
2236 # Compile an extern factory
2237 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2238 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2239 do
2240 var externname
2241 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2242 if at != null then
2243 externname = at.arg_as_string(v.compiler.modelbuilder)
2244 if externname == null then return false
2245 else
2246 return false
2247 end
2248 v.add_extern(mpropdef.mclassdef.mmodule)
2249 v.adapt_signature(mpropdef, arguments)
2250 v.unbox_signature_extern(mpropdef, arguments)
2251 var ret = arguments.first.mtype
2252 var res = v.new_var_extern(ret)
2253
2254 arguments.shift
2255
2256 v.add("{res} = {externname}({arguments.join(", ")});")
2257 res = v.box_extern(res, ret)
2258 v.ret(res)
2259 return true
2260 end
2261 end
2262
2263 redef class AAttrPropdef
2264 redef fun can_inline: Bool do return not is_lazy
2265
2266 redef fun compile_to_c(v, mpropdef, arguments)
2267 do
2268 if mpropdef == mreadpropdef then
2269 assert arguments.length == 1
2270 var recv = arguments.first
2271 var res
2272 if is_lazy then
2273 var set
2274 var ret = self.mpropdef.static_mtype
2275 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2276 var guard = self.mlazypropdef.mproperty
2277 if useiset then
2278 set = v.isset_attribute(self.mpropdef.mproperty, recv)
2279 else
2280 set = v.read_attribute(guard, recv)
2281 end
2282 v.add("if(likely({set})) \{")
2283 res = v.read_attribute(self.mpropdef.mproperty, recv)
2284 v.add("\} else \{")
2285
2286 var value = evaluate_expr(v, recv)
2287
2288 v.assign(res, value)
2289 if not useiset then
2290 var true_v = v.new_expr("1", v.bool_type)
2291 v.write_attribute(guard, arguments.first, true_v)
2292 end
2293 v.add("\}")
2294 else
2295 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2296 end
2297 v.assign(v.frame.returnvar.as(not null), res)
2298 else if mpropdef == mwritepropdef then
2299 assert arguments.length == 2
2300 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2301 if is_lazy then
2302 var ret = self.mpropdef.static_mtype
2303 var useiset = not ret.is_c_primitive and not ret isa MNullableType
2304 if not useiset then
2305 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2306 end
2307 end
2308 else
2309 abort
2310 end
2311 end
2312
2313 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2314 do
2315 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
2316 end
2317
2318 # Evaluate, store and return the default value of the attribute
2319 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
2320 do
2321 var oldnode = v.current_node
2322 v.current_node = self
2323 var old_frame = v.frame
2324 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
2325 v.frame = frame
2326
2327 var value
2328 var mtype = self.mpropdef.static_mtype
2329 assert mtype != null
2330
2331 var nexpr = self.n_expr
2332 var nblock = self.n_block
2333 if nexpr != null then
2334 value = v.expr(nexpr, mtype)
2335 else if nblock != null then
2336 value = v.new_var(mtype)
2337 frame.returnvar = value
2338 frame.returnlabel = v.get_name("RET_LABEL")
2339 v.add("\{")
2340 v.stmt(nblock)
2341 v.add("{frame.returnlabel.as(not null)}:(void)0;")
2342 v.add("\}")
2343 else
2344 abort
2345 end
2346
2347 v.write_attribute(self.mpropdef.mproperty, recv, value)
2348
2349 v.frame = old_frame
2350 v.current_node = oldnode
2351
2352 return value
2353 end
2354
2355 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2356 do
2357 var nexpr = self.n_expr
2358 if nexpr != null then return
2359
2360 var oldnode = v.current_node
2361 v.current_node = self
2362 var old_frame = v.frame
2363 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2364 v.frame = frame
2365 # Force read to check the initialization
2366 v.read_attribute(self.mpropdef.mproperty, recv)
2367 v.frame = old_frame
2368 v.current_node = oldnode
2369 end
2370 end
2371
2372 redef class AClassdef
2373 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2374 do
2375 if mpropdef == self.mfree_init then
2376 assert mpropdef.mproperty.is_root_init
2377 assert arguments.length == 1
2378 if not mpropdef.is_intro then
2379 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2380 end
2381 return
2382 else
2383 abort
2384 end
2385 end
2386 end
2387
2388 redef class AExpr
2389 # Try to compile self as an expression
2390 # Do not call this method directly, use `v.expr` instead
2391 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2392 do
2393 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2394 var mtype = self.mtype
2395 if mtype == null then
2396 return null
2397 else
2398 var res = v.new_var(mtype)
2399 v.add("/* {res} = NOT YET {class_name} */")
2400 return res
2401 end
2402 end
2403
2404 # Try to compile self as a statement
2405 # Do not call this method directly, use `v.stmt` instead
2406 private fun stmt(v: AbstractCompilerVisitor)
2407 do
2408 expr(v)
2409 end
2410 end
2411
2412 redef class ABlockExpr
2413 redef fun stmt(v)
2414 do
2415 for e in self.n_expr do v.stmt(e)
2416 end
2417 redef fun expr(v)
2418 do
2419 var last = self.n_expr.last
2420 for e in self.n_expr do
2421 if e == last then break
2422 v.stmt(e)
2423 end
2424 return v.expr(last, null)
2425 end
2426 end
2427
2428 redef class AVardeclExpr
2429 redef fun stmt(v)
2430 do
2431 var variable = self.variable.as(not null)
2432 var ne = self.n_expr
2433 if ne != null then
2434 var i = v.expr(ne, variable.declared_type)
2435 v.assign(v.variable(variable), i)
2436 end
2437 end
2438 end
2439
2440 redef class AVarExpr
2441 redef fun expr(v)
2442 do
2443 var res = v.variable(self.variable.as(not null))
2444 var mtype = self.mtype.as(not null)
2445 return v.autoadapt(res, mtype)
2446 end
2447 end
2448
2449 redef class AVarAssignExpr
2450 redef fun expr(v)
2451 do
2452 var variable = self.variable.as(not null)
2453 var i = v.expr(self.n_value, variable.declared_type)
2454 v.assign(v.variable(variable), i)
2455 return i
2456 end
2457 end
2458
2459 redef class AVarReassignExpr
2460 redef fun stmt(v)
2461 do
2462 var variable = self.variable.as(not null)
2463 var vari = v.variable(variable)
2464 var value = v.expr(self.n_value, variable.declared_type)
2465 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2466 assert res != null
2467 v.assign(v.variable(variable), res)
2468 end
2469 end
2470
2471 redef class ASelfExpr
2472 redef fun expr(v) do return v.frame.arguments.first
2473 end
2474
2475 redef class AEscapeExpr
2476 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2477 end
2478
2479 redef class AReturnExpr
2480 redef fun stmt(v)
2481 do
2482 var nexpr = self.n_expr
2483 if nexpr != null then
2484 var returnvar = v.frame.returnvar.as(not null)
2485 var i = v.expr(nexpr, returnvar.mtype)
2486 v.assign(returnvar, i)
2487 end
2488 v.add("goto {v.frame.returnlabel.as(not null)};")
2489 end
2490 end
2491
2492 redef class AAbortExpr
2493 redef fun stmt(v) do v.add_abort("Aborted")
2494 end
2495
2496 redef class AIfExpr
2497 redef fun stmt(v)
2498 do
2499 var cond = v.expr_bool(self.n_expr)
2500 v.add("if ({cond})\{")
2501 v.stmt(self.n_then)
2502 v.add("\} else \{")
2503 v.stmt(self.n_else)
2504 v.add("\}")
2505 end
2506
2507 redef fun expr(v)
2508 do
2509 var res = v.new_var(self.mtype.as(not null))
2510 var cond = v.expr_bool(self.n_expr)
2511 v.add("if ({cond})\{")
2512 v.assign(res, v.expr(self.n_then.as(not null), null))
2513 v.add("\} else \{")
2514 v.assign(res, v.expr(self.n_else.as(not null), null))
2515 v.add("\}")
2516 return res
2517 end
2518 end
2519
2520 redef class AIfexprExpr
2521 redef fun expr(v)
2522 do
2523 var res = v.new_var(self.mtype.as(not null))
2524 var cond = v.expr_bool(self.n_expr)
2525 v.add("if ({cond})\{")
2526 v.assign(res, v.expr(self.n_then, null))
2527 v.add("\} else \{")
2528 v.assign(res, v.expr(self.n_else, null))
2529 v.add("\}")
2530 return res
2531 end
2532 end
2533
2534 redef class ADoExpr
2535 redef fun stmt(v)
2536 do
2537 v.stmt(self.n_block)
2538 v.add_escape_label(break_mark)
2539 end
2540 end
2541
2542 redef class AWhileExpr
2543 redef fun stmt(v)
2544 do
2545 v.add("for(;;) \{")
2546 var cond = v.expr_bool(self.n_expr)
2547 v.add("if (!{cond}) break;")
2548 v.stmt(self.n_block)
2549 v.add_escape_label(continue_mark)
2550 v.add("\}")
2551 v.add_escape_label(break_mark)
2552 end
2553 end
2554
2555 redef class ALoopExpr
2556 redef fun stmt(v)
2557 do
2558 v.add("for(;;) \{")
2559 v.stmt(self.n_block)
2560 v.add_escape_label(continue_mark)
2561 v.add("\}")
2562 v.add_escape_label(break_mark)
2563 end
2564 end
2565
2566 redef class AForExpr
2567 redef fun stmt(v)
2568 do
2569 var cl = v.expr(self.n_expr, null)
2570 var it_meth = self.method_iterator
2571 assert it_meth != null
2572 var it = v.compile_callsite(it_meth, [cl])
2573 assert it != null
2574 v.add("for(;;) \{")
2575 var isok_meth = self.method_is_ok
2576 assert isok_meth != null
2577 var ok = v.compile_callsite(isok_meth, [it])
2578 assert ok != null
2579 v.add("if(!{ok}) break;")
2580 if self.variables.length == 1 then
2581 var item_meth = self.method_item
2582 assert item_meth != null
2583 var i = v.compile_callsite(item_meth, [it])
2584 assert i != null
2585 v.assign(v.variable(variables.first), i)
2586 else if self.variables.length == 2 then
2587 var key_meth = self.method_key
2588 assert key_meth != null
2589 var i = v.compile_callsite(key_meth, [it])
2590 assert i != null
2591 v.assign(v.variable(variables[0]), i)
2592 var item_meth = self.method_item
2593 assert item_meth != null
2594 i = v.compile_callsite(item_meth, [it])
2595 assert i != null
2596 v.assign(v.variable(variables[1]), i)
2597 else
2598 abort
2599 end
2600 v.stmt(self.n_block)
2601 v.add_escape_label(continue_mark)
2602 var next_meth = self.method_next
2603 assert next_meth != null
2604 v.compile_callsite(next_meth, [it])
2605 v.add("\}")
2606 v.add_escape_label(break_mark)
2607
2608 var method_finish = self.method_finish
2609 if method_finish != null then
2610 # TODO: Find a way to call this also in long escape (e.g. return)
2611 v.compile_callsite(method_finish, [it])
2612 end
2613 end
2614 end
2615
2616 redef class AAssertExpr
2617 redef fun stmt(v)
2618 do
2619 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2620
2621 var cond = v.expr_bool(self.n_expr)
2622 v.add("if (unlikely(!{cond})) \{")
2623 v.stmt(self.n_else)
2624 var nid = self.n_id
2625 if nid != null then
2626 v.add_abort("Assert '{nid.text}' failed")
2627 else
2628 v.add_abort("Assert failed")
2629 end
2630 v.add("\}")
2631 end
2632 end
2633
2634 redef class AOrExpr
2635 redef fun expr(v)
2636 do
2637 var res = v.new_var(self.mtype.as(not null))
2638 var i1 = v.expr_bool(self.n_expr)
2639 v.add("if ({i1}) \{")
2640 v.add("{res} = 1;")
2641 v.add("\} else \{")
2642 var i2 = v.expr_bool(self.n_expr2)
2643 v.add("{res} = {i2};")
2644 v.add("\}")
2645 return res
2646 end
2647 end
2648
2649 redef class AImpliesExpr
2650 redef fun expr(v)
2651 do
2652 var res = v.new_var(self.mtype.as(not null))
2653 var i1 = v.expr_bool(self.n_expr)
2654 v.add("if (!{i1}) \{")
2655 v.add("{res} = 1;")
2656 v.add("\} else \{")
2657 var i2 = v.expr_bool(self.n_expr2)
2658 v.add("{res} = {i2};")
2659 v.add("\}")
2660 return res
2661 end
2662 end
2663
2664 redef class AAndExpr
2665 redef fun expr(v)
2666 do
2667 var res = v.new_var(self.mtype.as(not null))
2668 var i1 = v.expr_bool(self.n_expr)
2669 v.add("if (!{i1}) \{")
2670 v.add("{res} = 0;")
2671 v.add("\} else \{")
2672 var i2 = v.expr_bool(self.n_expr2)
2673 v.add("{res} = {i2};")
2674 v.add("\}")
2675 return res
2676 end
2677 end
2678
2679 redef class ANotExpr
2680 redef fun expr(v)
2681 do
2682 var cond = v.expr_bool(self.n_expr)
2683 return v.new_expr("!{cond}", self.mtype.as(not null))
2684 end
2685 end
2686
2687 redef class AOrElseExpr
2688 redef fun expr(v)
2689 do
2690 var res = v.new_var(self.mtype.as(not null))
2691 var i1 = v.expr(self.n_expr, null)
2692 v.add("if ({i1}!=NULL) \{")
2693 v.assign(res, i1)
2694 v.add("\} else \{")
2695 var i2 = v.expr(self.n_expr2, null)
2696 v.assign(res, i2)
2697 v.add("\}")
2698 return res
2699 end
2700 end
2701
2702 redef class AIntExpr
2703 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2704 end
2705
2706 redef class AFloatExpr
2707 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2708 end
2709
2710 redef class ACharExpr
2711 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2712 end
2713
2714 redef class AArrayExpr
2715 redef fun expr(v)
2716 do
2717 var mtype = self.element_mtype.as(not null)
2718 var array = new Array[RuntimeVariable]
2719 var res = v.array_instance(array, mtype)
2720
2721 var old_comprehension = v.frame.comprehension
2722 v.frame.comprehension = res
2723 for nexpr in self.n_exprs do
2724 v.stmt(nexpr)
2725 end
2726 v.frame.comprehension = old_comprehension
2727
2728 return res
2729 end
2730 end
2731
2732 redef class AStringFormExpr
2733 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2734 end
2735
2736 redef class ASuperstringExpr
2737 redef fun expr(v)
2738 do
2739 var array = new Array[RuntimeVariable]
2740 for ne in self.n_exprs do
2741 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2742 var i = v.expr(ne, null)
2743 array.add(i)
2744 end
2745 var a = v.array_instance(array, v.object_type)
2746 var res = v.send(v.get_property("to_s", a.mtype), [a])
2747 return res
2748 end
2749 end
2750
2751 redef class ACrangeExpr
2752 redef fun expr(v)
2753 do
2754 var i1 = v.expr(self.n_expr, null)
2755 var i2 = v.expr(self.n_expr2, null)
2756 var mtype = self.mtype.as(MClassType)
2757 var res = v.init_instance(mtype)
2758 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2759 return res
2760 end
2761 end
2762
2763 redef class AOrangeExpr
2764 redef fun expr(v)
2765 do
2766 var i1 = v.expr(self.n_expr, null)
2767 var i2 = v.expr(self.n_expr2, null)
2768 var mtype = self.mtype.as(MClassType)
2769 var res = v.init_instance(mtype)
2770 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2771 return res
2772 end
2773 end
2774
2775 redef class ATrueExpr
2776 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2777 end
2778
2779 redef class AFalseExpr
2780 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2781 end
2782
2783 redef class ANullExpr
2784 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2785 end
2786
2787 redef class AIsaExpr
2788 redef fun expr(v)
2789 do
2790 var i = v.expr(self.n_expr, null)
2791 return v.type_test(i, self.cast_type.as(not null), "isa")
2792 end
2793 end
2794
2795 redef class AAsCastExpr
2796 redef fun expr(v)
2797 do
2798 var i = v.expr(self.n_expr, null)
2799 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2800
2801 v.add_cast(i, self.mtype.as(not null), "as")
2802 return i
2803 end
2804 end
2805
2806 redef class AAsNotnullExpr
2807 redef fun expr(v)
2808 do
2809 var i = v.expr(self.n_expr, null)
2810 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2811
2812 if i.mtype.is_c_primitive then return i
2813
2814 v.add("if (unlikely({i} == NULL)) \{")
2815 v.add_abort("Cast failed")
2816 v.add("\}")
2817 return i
2818 end
2819 end
2820
2821 redef class AParExpr
2822 redef fun expr(v) do return v.expr(self.n_expr, null)
2823 end
2824
2825 redef class AOnceExpr
2826 redef fun expr(v)
2827 do
2828 var mtype = self.mtype.as(not null)
2829 var name = v.get_name("varonce")
2830 var guard = v.get_name(name + "_guard")
2831 v.add_decl("static {mtype.ctype} {name};")
2832 v.add_decl("static int {guard};")
2833 var res = v.new_var(mtype)
2834 v.add("if (likely({guard})) \{")
2835 v.add("{res} = {name};")
2836 v.add("\} else \{")
2837 var i = v.expr(self.n_expr, mtype)
2838 v.add("{res} = {i};")
2839 v.add("{name} = {res};")
2840 v.add("{guard} = 1;")
2841 v.add("\}")
2842 return res
2843 end
2844 end
2845
2846 redef class ASendExpr
2847 redef fun expr(v)
2848 do
2849 var recv = v.expr(self.n_expr, null)
2850 var callsite = self.callsite.as(not null)
2851 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2852 return v.compile_callsite(callsite, args)
2853 end
2854 end
2855
2856 redef class ASendReassignFormExpr
2857 redef fun stmt(v)
2858 do
2859 var recv = v.expr(self.n_expr, null)
2860 var callsite = self.callsite.as(not null)
2861 var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
2862
2863 var value = v.expr(self.n_value, null)
2864
2865 var left = v.compile_callsite(callsite, args)
2866 assert left != null
2867
2868 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2869 assert res != null
2870
2871 args.add(res)
2872 v.compile_callsite(self.write_callsite.as(not null), args)
2873 end
2874 end
2875
2876 redef class ASuperExpr
2877 redef fun expr(v)
2878 do
2879 var recv = v.frame.arguments.first
2880
2881 var callsite = self.callsite
2882 if callsite != null then
2883 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2884
2885 # Add additional arguments for the super init call
2886 if args.length == 1 then
2887 for i in [0..callsite.msignature.arity[ do
2888 args.add(v.frame.arguments[i+1])
2889 end
2890 end
2891 # Super init call
2892 var res = v.compile_callsite(callsite, args)
2893 return res
2894 end
2895
2896 var mpropdef = self.mpropdef.as(not null)
2897 var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
2898 if args.length == 1 then
2899 args = v.frame.arguments
2900 end
2901
2902 # stantard call-next-method
2903 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
2904 end
2905 end
2906
2907 redef class ANewExpr
2908 redef fun expr(v)
2909 do
2910 var mtype = self.recvtype
2911 assert mtype != null
2912
2913 if mtype.mclass.name == "NativeArray" then
2914 assert self.n_args.n_exprs.length == 1
2915 var l = v.expr(self.n_args.n_exprs.first, null)
2916 assert mtype isa MGenericType
2917 var elttype = mtype.arguments.first
2918 return v.native_array_instance(elttype, l)
2919 end
2920
2921 var recv = v.init_instance_or_extern(mtype)
2922
2923 var callsite = self.callsite.as(not null)
2924 var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
2925 var res2 = v.compile_callsite(callsite, args)
2926 if res2 != null then
2927 #self.debug("got {res2} from {mproperty}. drop {recv}")
2928 return res2
2929 end
2930 return recv
2931 end
2932 end
2933
2934 redef class AAttrExpr
2935 redef fun expr(v)
2936 do
2937 var recv = v.expr(self.n_expr, null)
2938 var mproperty = self.mproperty.as(not null)
2939 return v.read_attribute(mproperty, recv)
2940 end
2941 end
2942
2943 redef class AAttrAssignExpr
2944 redef fun expr(v)
2945 do
2946 var recv = v.expr(self.n_expr, null)
2947 var i = v.expr(self.n_value, null)
2948 var mproperty = self.mproperty.as(not null)
2949 v.write_attribute(mproperty, recv, i)
2950 return i
2951 end
2952 end
2953
2954 redef class AAttrReassignExpr
2955 redef fun stmt(v)
2956 do
2957 var recv = v.expr(self.n_expr, null)
2958 var value = v.expr(self.n_value, null)
2959 var mproperty = self.mproperty.as(not null)
2960 var attr = v.read_attribute(mproperty, recv)
2961 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2962 assert res != null
2963 v.write_attribute(mproperty, recv, res)
2964 end
2965 end
2966
2967 redef class AIssetAttrExpr
2968 redef fun expr(v)
2969 do
2970 var recv = v.expr(self.n_expr, null)
2971 var mproperty = self.mproperty.as(not null)
2972 return v.isset_attribute(mproperty, recv)
2973 end
2974 end
2975
2976 redef class ADebugTypeExpr
2977 redef fun stmt(v)
2978 do
2979 # do nothing
2980 end
2981 end
2982
2983 # Utils
2984
2985 redef class Array[E]
2986 # Return a new `Array` with the elements only contened in self and not in `o`
2987 fun -(o: Array[E]): Array[E] do
2988 var res = new Array[E]
2989 for e in self do if not o.has(e) then res.add(e)
2990 return res
2991 end
2992 end
2993
2994 redef class MModule
2995 # All `MProperty` associated to all `MClassDef` of `mclass`
2996 fun properties(mclass: MClass): Set[MProperty] do
2997 if not self.properties_cache.has_key(mclass) then
2998 var properties = new HashSet[MProperty]
2999 var parents = new Array[MClass]
3000 if self.flatten_mclass_hierarchy.has(mclass) then
3001 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3002 end
3003 for parent in parents do
3004 properties.add_all(self.properties(parent))
3005 end
3006 for mclassdef in mclass.mclassdefs do
3007 if not self.in_importation <= mclassdef.mmodule then continue
3008 for mprop in mclassdef.intro_mproperties do
3009 properties.add(mprop)
3010 end
3011 end
3012 self.properties_cache[mclass] = properties
3013 end
3014 return properties_cache[mclass]
3015 end
3016 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3017
3018 # Write FFI and nitni results to file
3019 fun finalize_ffi(c: AbstractCompiler) do end
3020
3021 # Give requided addinional system libraries (as given to LD_LIBS)
3022 # Note: can return null instead of an empty set
3023 fun collect_linker_libs: nullable Array[String] do return null
3024 end
3025
3026 # Create a tool context to handle options and paths
3027 var toolcontext = new ToolContext
3028
3029 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3030
3031 # We do not add other options, so process them now!
3032 toolcontext.process_options(args)
3033
3034 # We need a model to collect stufs
3035 var model = new Model
3036 # An a model builder to parse files
3037 var modelbuilder = new ModelBuilder(model, toolcontext)
3038
3039 var arguments = toolcontext.option_context.rest
3040 if arguments.length > 1 and toolcontext.opt_output.value != null then
3041 print "Error: --output needs a single source file. Do you prefer --dir?"
3042 exit 1
3043 end
3044
3045 # Here we load an process all modules passed on the command line
3046 var mmodules = modelbuilder.parse(arguments)
3047
3048 if mmodules.is_empty then return
3049 modelbuilder.run_phases
3050
3051 for mmodule in mmodules do
3052 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3053 var ms = [mmodule]
3054 toolcontext.run_global_phases(ms)
3055 end