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