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