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