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