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