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