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