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