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