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