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