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