src: add some asserts on `super` related things
[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 assert auto_super_init.mproperty != mpropdef.mproperty
1833 args.clear
1834 for i in [0..auto_super_init.msignature.arity+1[ do
1835 args.add(arguments[i])
1836 end
1837 v.compile_callsite(auto_super_init, args)
1838 end
1839 end
1840
1841 var n_block = n_block
1842 if n_block != null then
1843 for i in [0..mpropdef.msignature.arity[ do
1844 var variable = self.n_signature.n_params[i].variable.as(not null)
1845 v.assign(v.variable(variable), arguments[i+1])
1846 end
1847 v.stmt(n_block)
1848 else if mpropdef.is_intern then
1849 compile_intern_to_c(v, mpropdef, arguments)
1850 else if mpropdef.is_extern then
1851 if mpropdef.mproperty.is_init then
1852 compile_externinit_to_c(v, mpropdef, arguments)
1853 else
1854 compile_externmeth_to_c(v, mpropdef, arguments)
1855 end
1856 else
1857 abort
1858 end
1859 end
1860
1861 redef fun can_inline
1862 do
1863 if self.auto_super_inits != null then return false
1864 var nblock = self.n_block
1865 if nblock == null then return true
1866 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
1867 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
1868 return false
1869 end
1870
1871 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
1872 do
1873 var pname = mpropdef.mproperty.name
1874 var cname = mpropdef.mclassdef.mclass.name
1875 var ret = mpropdef.msignature.return_mtype
1876 if ret != null then
1877 ret = v.resolve_for(ret, arguments.first)
1878 else if mpropdef.mproperty.is_new then
1879 ret = arguments.first.mcasttype
1880 end
1881 if pname != "==" and pname != "!=" then
1882 v.adapt_signature(mpropdef, arguments)
1883 end
1884 if cname == "Int" then
1885 if pname == "output" then
1886 v.add("printf(\"%ld\\n\", {arguments.first});")
1887 return
1888 else if pname == "object_id" then
1889 v.ret(arguments.first)
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 == "-" then
1895 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1896 return
1897 else if pname == "unary -" then
1898 v.ret(v.new_expr("-{arguments[0]}", 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 == "%" then
1907 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1908 return
1909 else if pname == "lshift" then
1910 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1911 return
1912 else if pname == "rshift" then
1913 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1914 return
1915 else if pname == "==" then
1916 v.ret(v.equal_test(arguments[0], arguments[1]))
1917 return
1918 else if pname == "!=" then
1919 var res = v.equal_test(arguments[0], arguments[1])
1920 v.ret(v.new_expr("!{res}", 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 == ">=" then
1932 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1933 return
1934 else if pname == "to_f" then
1935 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1936 return
1937 else if pname == "ascii" then
1938 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1939 return
1940 end
1941 else if cname == "Char" then
1942 if pname == "output" then
1943 v.add("printf(\"%c\", {arguments.first});")
1944 return
1945 else if pname == "object_id" then
1946 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1947 return
1948 else if pname == "successor" then
1949 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1950 return
1951 else if pname == "predecessor" then
1952 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1953 return
1954 else if pname == "==" then
1955 v.ret(v.equal_test(arguments[0], arguments[1]))
1956 return
1957 else if pname == "!=" then
1958 var res = v.equal_test(arguments[0], arguments[1])
1959 v.ret(v.new_expr("!{res}", 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 == ">=" then
1971 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1972 return
1973 else if pname == "to_i" then
1974 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
1975 return
1976 else if pname == "ascii" then
1977 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
1978 return
1979 end
1980 else if cname == "Bool" then
1981 if pname == "output" then
1982 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
1983 return
1984 else if pname == "object_id" then
1985 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
1986 return
1987 else if pname == "==" then
1988 v.ret(v.equal_test(arguments[0], arguments[1]))
1989 return
1990 else if pname == "!=" then
1991 var res = v.equal_test(arguments[0], arguments[1])
1992 v.ret(v.new_expr("!{res}", ret.as(not null)))
1993 return
1994 end
1995 else if cname == "Float" then
1996 if pname == "output" then
1997 v.add("printf(\"%f\\n\", {arguments.first});")
1998 return
1999 else if pname == "object_id" then
2000 v.ret(v.new_expr("(double){arguments.first}", 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 == "-" then
2006 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2007 return
2008 else if pname == "unary -" then
2009 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2010 return
2011 else if pname == "succ" then
2012 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2013 return
2014 else if pname == "prec" then
2015 v.ret(v.new_expr("{arguments[0]}-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.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2022 return
2023 else if pname == "==" then
2024 v.ret(v.equal_test(arguments[0], arguments[1]))
2025 return
2026 else if pname == "!=" then
2027 var res = v.equal_test(arguments[0], arguments[1])
2028 v.ret(v.new_expr("!{res}", 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 == ">=" then
2040 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2041 return
2042 else if pname == "to_i" then
2043 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2044 return
2045 end
2046 else if cname == "NativeString" then
2047 if pname == "[]" then
2048 v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
2049 return
2050 else if pname == "[]=" then
2051 v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
2052 return
2053 else if pname == "copy_to" then
2054 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2055 return
2056 else if pname == "atoi" then
2057 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2058 return
2059 else if pname == "init" then
2060 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2061 return
2062 end
2063 else if cname == "NativeArray" then
2064 v.native_array_def(pname, ret, arguments)
2065 return
2066 end
2067 if pname == "exit" then
2068 v.add("exit({arguments[1]});")
2069 return
2070 else if pname == "sys" then
2071 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2072 return
2073 else if pname == "calloc_string" then
2074 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2075 return
2076 else if pname == "calloc_array" then
2077 v.calloc_array(ret.as(not null), arguments)
2078 return
2079 else if pname == "object_id" then
2080 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2081 return
2082 else if pname == "is_same_type" then
2083 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2084 return
2085 else if pname == "is_same_instance" then
2086 v.ret(v.equal_test(arguments[0], arguments[1]))
2087 return
2088 else if pname == "output_class_name" then
2089 var nat = v.class_name_string(arguments.first)
2090 v.add("printf(\"%s\\n\", {nat});")
2091 return
2092 else if pname == "native_class_name" then
2093 var nat = v.class_name_string(arguments.first)
2094 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2095 return
2096 else if pname == "force_garbage_collection" then
2097 v.add("nit_gcollect();")
2098 return
2099 else if pname == "native_argc" then
2100 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2101 return
2102 else if pname == "native_argv" then
2103 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2104 return
2105 end
2106 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
2107 debug("Not implemented {mpropdef}")
2108 end
2109
2110 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2111 do
2112 var externname
2113 var nextern = self.n_extern
2114 if nextern == null then
2115 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
2116 v.add("show_backtrace(1);")
2117 return
2118 end
2119 externname = nextern.text.substring(1, nextern.text.length-2)
2120 if location.file != null then
2121 var file = location.file.filename
2122 v.add_extern(file)
2123 end
2124 var res: nullable RuntimeVariable = null
2125 var ret = mpropdef.msignature.return_mtype
2126 if ret != null then
2127 ret = v.resolve_for(ret, arguments.first)
2128 res = v.new_var(ret)
2129 end
2130 v.adapt_signature(mpropdef, arguments)
2131
2132 if res == null then
2133 v.add("{externname}({arguments.join(", ")});")
2134 else
2135 v.add("{res} = {externname}({arguments.join(", ")});")
2136 v.ret(res)
2137 end
2138 end
2139
2140 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2141 do
2142 var externname
2143 var nextern = self.n_extern
2144 if nextern == null then
2145 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
2146 v.add("show_backtrace(1);")
2147 return
2148 end
2149 externname = nextern.text.substring(1, nextern.text.length-2)
2150 if location.file != null then
2151 var file = location.file.filename
2152 v.add_extern(file)
2153 end
2154 v.adapt_signature(mpropdef, arguments)
2155 var ret = arguments.first.mtype
2156 var res = v.new_var(ret)
2157
2158 arguments.shift
2159
2160 v.add("{res} = {externname}({arguments.join(", ")});")
2161 v.ret(res)
2162 end
2163 end
2164
2165 redef class AAttrPropdef
2166 redef fun compile_to_c(v, mpropdef, arguments)
2167 do
2168 if mpropdef == mreadpropdef then
2169 assert arguments.length == 1
2170 var res
2171 if is_lazy then
2172 var nexpr = n_expr
2173 assert nexpr != null
2174 var set
2175 var ret = self.mpropdef.static_mtype
2176 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2177 var guard = self.mlazypropdef.mproperty
2178 if useiset then
2179 set = v.isset_attribute(self.mpropdef.mproperty, arguments.first)
2180 else
2181 set = v.read_attribute(guard, arguments.first)
2182 end
2183 v.add("if(likely({set})) \{")
2184 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2185 v.add("\} else \{")
2186 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2187 v.write_attribute(self.mpropdef.mproperty, arguments.first, value)
2188 v.assign(res, value)
2189 if not useiset then
2190 var true_v = v.new_expr("1", v.bool_type)
2191 v.write_attribute(guard, arguments.first, true_v)
2192 end
2193 v.add("\}")
2194 else
2195 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2196 end
2197 v.assign(v.frame.returnvar.as(not null), res)
2198 else if mpropdef == mwritepropdef then
2199 assert arguments.length == 2
2200 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2201 if is_lazy then
2202 var ret = self.mpropdef.static_mtype
2203 var useiset = ret.ctype == "val*" and not ret isa MNullableType
2204 if not useiset then
2205 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
2206 end
2207 end
2208 else
2209 abort
2210 end
2211 end
2212
2213 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2214 do
2215 var nexpr = self.n_expr
2216 if nexpr != null and not is_lazy then
2217 var oldnode = v.current_node
2218 v.current_node = self
2219 var old_frame = v.frame
2220 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2221 v.frame = frame
2222 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2223 v.write_attribute(self.mpropdef.mproperty, recv, value)
2224 v.frame = old_frame
2225 v.current_node = oldnode
2226 end
2227 end
2228
2229 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2230 do
2231 var nexpr = self.n_expr
2232 if nexpr != null then return
2233
2234 var oldnode = v.current_node
2235 v.current_node = self
2236 var old_frame = v.frame
2237 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2238 v.frame = frame
2239 # Force read to check the initialization
2240 v.read_attribute(self.mpropdef.mproperty, recv)
2241 v.frame = old_frame
2242 v.current_node = oldnode
2243 end
2244 end
2245
2246 redef class AClassdef
2247 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2248 do
2249 if mpropdef == self.mfree_init then
2250 var super_inits = self.super_inits
2251 if super_inits != null then
2252 var args_of_super = arguments
2253 if arguments.length > 1 then args_of_super = [arguments.first]
2254 for su in super_inits do
2255 v.send(su, args_of_super)
2256 end
2257 end
2258 var recv = arguments.first
2259 var i = 1
2260 # Collect undefined attributes
2261 for npropdef in self.n_propdefs do
2262 if npropdef isa AAttrPropdef and npropdef.n_expr == null and not npropdef.noinit then
2263 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
2264 i += 1
2265 end
2266 end
2267 else
2268 abort
2269 end
2270 end
2271 end
2272
2273 redef class AExpr
2274 # Try to compile self as an expression
2275 # Do not call this method directly, use `v.expr` instead
2276 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2277 do
2278 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2279 var mtype = self.mtype
2280 if mtype == null then
2281 return null
2282 else
2283 var res = v.new_var(mtype)
2284 v.add("/* {res} = NOT YET {class_name} */")
2285 return res
2286 end
2287 end
2288
2289 # Try to compile self as a statement
2290 # Do not call this method directly, use `v.stmt` instead
2291 private fun stmt(v: AbstractCompilerVisitor)
2292 do
2293 var res = expr(v)
2294 if res != null then v.add("{res};")
2295 end
2296 end
2297
2298 redef class ABlockExpr
2299 redef fun stmt(v)
2300 do
2301 for e in self.n_expr do v.stmt(e)
2302 end
2303 redef fun expr(v)
2304 do
2305 var last = self.n_expr.last
2306 for e in self.n_expr do
2307 if e == last then break
2308 v.stmt(e)
2309 end
2310 return v.expr(last, null)
2311 end
2312 end
2313
2314 redef class AVardeclExpr
2315 redef fun stmt(v)
2316 do
2317 var variable = self.variable.as(not null)
2318 var ne = self.n_expr
2319 if ne != null then
2320 var i = v.expr(ne, variable.declared_type)
2321 v.assign(v.variable(variable), i)
2322 end
2323 end
2324 end
2325
2326 redef class AVarExpr
2327 redef fun expr(v)
2328 do
2329 var res = v.variable(self.variable.as(not null))
2330 var mtype = self.mtype.as(not null)
2331 return v.autoadapt(res, mtype)
2332 end
2333 end
2334
2335 redef class AVarAssignExpr
2336 redef fun stmt(v)
2337 do
2338 var variable = self.variable.as(not null)
2339 var i = v.expr(self.n_value, variable.declared_type)
2340 v.assign(v.variable(variable), i)
2341 end
2342 redef fun expr(v)
2343 do
2344 var variable = self.variable.as(not null)
2345 var i = v.expr(self.n_value, variable.declared_type)
2346 v.assign(v.variable(variable), i)
2347 return i
2348 end
2349 end
2350
2351 redef class AVarReassignExpr
2352 redef fun stmt(v)
2353 do
2354 var variable = self.variable.as(not null)
2355 var vari = v.variable(variable)
2356 var value = v.expr(self.n_value, variable.declared_type)
2357 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2358 assert res != null
2359 v.assign(v.variable(variable), res)
2360 end
2361 end
2362
2363 redef class ASelfExpr
2364 redef fun expr(v) do return v.frame.arguments.first
2365 end
2366
2367 redef class AContinueExpr
2368 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2369 end
2370
2371 redef class ABreakExpr
2372 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2373 end
2374
2375 redef class AReturnExpr
2376 redef fun stmt(v)
2377 do
2378 var nexpr = self.n_expr
2379 if nexpr != null then
2380 var returnvar = v.frame.returnvar.as(not null)
2381 var i = v.expr(nexpr, returnvar.mtype)
2382 v.assign(returnvar, i)
2383 end
2384 v.add("goto {v.frame.returnlabel.as(not null)};")
2385 end
2386 end
2387
2388 redef class AAbortExpr
2389 redef fun stmt(v) do v.add_abort("Aborted")
2390 end
2391
2392 redef class AIfExpr
2393 redef fun stmt(v)
2394 do
2395 var cond = v.expr_bool(self.n_expr)
2396 v.add("if ({cond})\{")
2397 v.stmt(self.n_then)
2398 v.add("\} else \{")
2399 v.stmt(self.n_else)
2400 v.add("\}")
2401 end
2402
2403 redef fun expr(v)
2404 do
2405 var res = v.new_var(self.mtype.as(not null))
2406 var cond = v.expr_bool(self.n_expr)
2407 v.add("if ({cond})\{")
2408 v.assign(res, v.expr(self.n_then.as(not null), null))
2409 v.add("\} else \{")
2410 v.assign(res, v.expr(self.n_else.as(not null), null))
2411 v.add("\}")
2412 return res
2413 end
2414 end
2415
2416 redef class AIfexprExpr
2417 redef fun expr(v)
2418 do
2419 var res = v.new_var(self.mtype.as(not null))
2420 var cond = v.expr_bool(self.n_expr)
2421 v.add("if ({cond})\{")
2422 v.assign(res, v.expr(self.n_then, null))
2423 v.add("\} else \{")
2424 v.assign(res, v.expr(self.n_else, null))
2425 v.add("\}")
2426 return res
2427 end
2428 end
2429
2430 redef class ADoExpr
2431 redef fun stmt(v)
2432 do
2433 v.stmt(self.n_block)
2434 var escapemark = self.escapemark
2435 if escapemark != null then
2436 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2437 end
2438 end
2439 end
2440
2441 redef class AWhileExpr
2442 redef fun stmt(v)
2443 do
2444 v.add("for(;;) \{")
2445 var cond = v.expr_bool(self.n_expr)
2446 v.add("if (!{cond}) break;")
2447 v.stmt(self.n_block)
2448 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2449 v.add("\}")
2450 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2451 end
2452 end
2453
2454 redef class ALoopExpr
2455 redef fun stmt(v)
2456 do
2457 v.add("for(;;) \{")
2458 v.stmt(self.n_block)
2459 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2460 v.add("\}")
2461 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2462 end
2463 end
2464
2465 redef class AForExpr
2466 redef fun stmt(v)
2467 do
2468 # Shortcut on explicit range
2469 # Avoid the instantiation of the range and the iterator
2470 var nexpr = self.n_expr
2471 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2472 var from = v.expr(nexpr.n_expr, null)
2473 var to = v.expr(nexpr.n_expr2, null)
2474 var variable = v.variable(variables.first)
2475 var one = v.new_expr("1", v.get_class("Int").mclass_type)
2476
2477 v.assign(variable, from)
2478 v.add("for(;;) \{ /* shortcut range */")
2479
2480 var ok
2481 if nexpr isa AOrangeExpr then
2482 ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2483 else
2484 ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
2485 end
2486 assert ok != null
2487 v.add("if(!{ok}) break;")
2488
2489 v.stmt(self.n_block)
2490
2491 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2492 var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
2493 assert succ != null
2494 v.assign(variable, succ)
2495 v.add("\}")
2496 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2497 return
2498 end
2499
2500 var cl = v.expr(self.n_expr, null)
2501 var it_meth = self.method_iterator
2502 assert it_meth != null
2503 var it = v.compile_callsite(it_meth, [cl])
2504 assert it != null
2505 v.add("for(;;) \{")
2506 var isok_meth = self.method_is_ok
2507 assert isok_meth != null
2508 var ok = v.compile_callsite(isok_meth, [it])
2509 assert ok != null
2510 v.add("if(!{ok}) break;")
2511 if self.variables.length == 1 then
2512 var item_meth = self.method_item
2513 assert item_meth != null
2514 var i = v.compile_callsite(item_meth, [it])
2515 assert i != null
2516 v.assign(v.variable(variables.first), i)
2517 else if self.variables.length == 2 then
2518 var key_meth = self.method_key
2519 assert key_meth != null
2520 var i = v.compile_callsite(key_meth, [it])
2521 assert i != null
2522 v.assign(v.variable(variables[0]), i)
2523 var item_meth = self.method_item
2524 assert item_meth != null
2525 i = v.compile_callsite(item_meth, [it])
2526 assert i != null
2527 v.assign(v.variable(variables[1]), i)
2528 else
2529 abort
2530 end
2531 v.stmt(self.n_block)
2532 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2533 var next_meth = self.method_next
2534 assert next_meth != null
2535 v.compile_callsite(next_meth, [it])
2536 v.add("\}")
2537 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2538 end
2539 end
2540
2541 redef class AAssertExpr
2542 redef fun stmt(v)
2543 do
2544 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2545
2546 var cond = v.expr_bool(self.n_expr)
2547 v.add("if (unlikely(!{cond})) \{")
2548 v.stmt(self.n_else)
2549 var nid = self.n_id
2550 if nid != null then
2551 v.add_abort("Assert '{nid.text}' failed")
2552 else
2553 v.add_abort("Assert failed")
2554 end
2555 v.add("\}")
2556 end
2557 end
2558
2559 redef class AOrExpr
2560 redef fun expr(v)
2561 do
2562 var res = v.new_var(self.mtype.as(not null))
2563 var i1 = v.expr_bool(self.n_expr)
2564 v.add("if ({i1}) \{")
2565 v.add("{res} = 1;")
2566 v.add("\} else \{")
2567 var i2 = v.expr_bool(self.n_expr2)
2568 v.add("{res} = {i2};")
2569 v.add("\}")
2570 return res
2571 end
2572 end
2573
2574 redef class AImpliesExpr
2575 redef fun expr(v)
2576 do
2577 var res = v.new_var(self.mtype.as(not null))
2578 var i1 = v.expr_bool(self.n_expr)
2579 v.add("if (!{i1}) \{")
2580 v.add("{res} = 1;")
2581 v.add("\} else \{")
2582 var i2 = v.expr_bool(self.n_expr2)
2583 v.add("{res} = {i2};")
2584 v.add("\}")
2585 return res
2586 end
2587 end
2588
2589 redef class AAndExpr
2590 redef fun expr(v)
2591 do
2592 var res = v.new_var(self.mtype.as(not null))
2593 var i1 = v.expr_bool(self.n_expr)
2594 v.add("if (!{i1}) \{")
2595 v.add("{res} = 0;")
2596 v.add("\} else \{")
2597 var i2 = v.expr_bool(self.n_expr2)
2598 v.add("{res} = {i2};")
2599 v.add("\}")
2600 return res
2601 end
2602 end
2603
2604 redef class ANotExpr
2605 redef fun expr(v)
2606 do
2607 var cond = v.expr_bool(self.n_expr)
2608 return v.new_expr("!{cond}", self.mtype.as(not null))
2609 end
2610 end
2611
2612 redef class AOrElseExpr
2613 redef fun expr(v)
2614 do
2615 var res = v.new_var(self.mtype.as(not null))
2616 var i1 = v.expr(self.n_expr, null)
2617 v.add("if ({i1}!=NULL) \{")
2618 v.assign(res, i1)
2619 v.add("\} else \{")
2620 var i2 = v.expr(self.n_expr2, null)
2621 v.assign(res, i2)
2622 v.add("\}")
2623 return res
2624 end
2625 end
2626
2627 redef class AIntExpr
2628 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2629 end
2630
2631 redef class AFloatExpr
2632 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2633 end
2634
2635 redef class ACharExpr
2636 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2637 end
2638
2639 redef class AArrayExpr
2640 redef fun expr(v)
2641 do
2642 var mtype = self.mtype.as(MClassType).arguments.first
2643 var array = new Array[RuntimeVariable]
2644 for nexpr in self.n_exprs.n_exprs do
2645 var i = v.expr(nexpr, mtype)
2646 array.add(i)
2647 end
2648 return v.array_instance(array, mtype)
2649 end
2650 end
2651
2652 redef class AStringFormExpr
2653 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2654 end
2655
2656 redef class ASuperstringExpr
2657 redef fun expr(v)
2658 do
2659 var array = new Array[RuntimeVariable]
2660 for ne in self.n_exprs do
2661 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2662 var i = v.expr(ne, null)
2663 array.add(i)
2664 end
2665 var a = v.array_instance(array, v.object_type)
2666 var res = v.send(v.get_property("to_s", a.mtype), [a])
2667 return res
2668 end
2669 end
2670
2671 redef class ACrangeExpr
2672 redef fun expr(v)
2673 do
2674 var i1 = v.expr(self.n_expr, null)
2675 var i2 = v.expr(self.n_expr2, null)
2676 var mtype = self.mtype.as(MClassType)
2677 var res = v.init_instance(mtype)
2678 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2679 return res
2680 end
2681 end
2682
2683 redef class AOrangeExpr
2684 redef fun expr(v)
2685 do
2686 var i1 = v.expr(self.n_expr, null)
2687 var i2 = v.expr(self.n_expr2, null)
2688 var mtype = self.mtype.as(MClassType)
2689 var res = v.init_instance(mtype)
2690 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2691 return res
2692 end
2693 end
2694
2695 redef class ATrueExpr
2696 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2697 end
2698
2699 redef class AFalseExpr
2700 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2701 end
2702
2703 redef class ANullExpr
2704 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2705 end
2706
2707 redef class AIsaExpr
2708 redef fun expr(v)
2709 do
2710 var i = v.expr(self.n_expr, null)
2711 return v.type_test(i, self.cast_type.as(not null), "isa")
2712 end
2713 end
2714
2715 redef class AAsCastExpr
2716 redef fun expr(v)
2717 do
2718 var i = v.expr(self.n_expr, null)
2719 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2720
2721 v.add_cast(i, self.mtype.as(not null), "as")
2722 return i
2723 end
2724 end
2725
2726 redef class AAsNotnullExpr
2727 redef fun expr(v)
2728 do
2729 var i = v.expr(self.n_expr, null)
2730 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2731
2732 if i.mtype.ctype != "val*" then return i
2733
2734 v.add("if (unlikely({i} == NULL)) \{")
2735 v.add_abort("Cast failed")
2736 v.add("\}")
2737 return i
2738 end
2739 end
2740
2741 redef class AParExpr
2742 redef fun expr(v) do return v.expr(self.n_expr, null)
2743 end
2744
2745 redef class AOnceExpr
2746 redef fun expr(v)
2747 do
2748 var mtype = self.mtype.as(not null)
2749 var name = v.get_name("varonce")
2750 var guard = v.get_name(name + "_guard")
2751 v.add_decl("static {mtype.ctype} {name};")
2752 v.add_decl("static int {guard};")
2753 var res = v.new_var(mtype)
2754 v.add("if ({guard}) \{")
2755 v.add("{res} = {name};")
2756 v.add("\} else \{")
2757 var i = v.expr(self.n_expr, mtype)
2758 v.add("{res} = {i};")
2759 v.add("{name} = {res};")
2760 v.add("{guard} = 1;")
2761 v.add("\}")
2762 return res
2763 end
2764 end
2765
2766 redef class ASendExpr
2767 redef fun expr(v)
2768 do
2769 var recv = v.expr(self.n_expr, null)
2770 var args = [recv]
2771 for a in self.raw_arguments do
2772 args.add(v.expr(a, null))
2773 end
2774 return v.compile_callsite(self.callsite.as(not null), args)
2775 end
2776 end
2777
2778 redef class ASendReassignFormExpr
2779 redef fun stmt(v)
2780 do
2781 var recv = v.expr(self.n_expr, null)
2782 var args = [recv]
2783 for a in self.raw_arguments do
2784 args.add(v.expr(a, null))
2785 end
2786 var value = v.expr(self.n_value, null)
2787
2788 var left = v.compile_callsite(self.callsite.as(not null), args)
2789 assert left != null
2790
2791 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2792 assert res != null
2793
2794 args.add(res)
2795 v.compile_callsite(self.write_callsite.as(not null), args)
2796 end
2797 end
2798
2799 redef class ASuperExpr
2800 redef fun expr(v)
2801 do
2802 var recv = v.frame.arguments.first
2803 var args = [recv]
2804 for a in self.n_args.n_exprs do
2805 args.add(v.expr(a, null))
2806 end
2807
2808 var callsite = self.callsite
2809 if callsite != null then
2810 # Add additionnals arguments for the super init call
2811 if args.length == 1 then
2812 for i in [0..callsite.msignature.arity[ do
2813 args.add(v.frame.arguments[i+1])
2814 end
2815 end
2816 # Super init call
2817 var res = v.compile_callsite(callsite, args)
2818 return res
2819 end
2820
2821 if args.length == 1 then
2822 args = v.frame.arguments
2823 end
2824
2825 # stantard call-next-method
2826 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2827 end
2828 end
2829
2830 redef class ANewExpr
2831 redef fun expr(v)
2832 do
2833 var mtype = self.mtype.as(MClassType)
2834 var recv
2835 var ctype = mtype.ctype
2836 if mtype.mclass.name == "NativeArray" then
2837 assert self.n_args.n_exprs.length == 1
2838 var l = v.expr(self.n_args.n_exprs.first, null)
2839 assert mtype isa MGenericType
2840 var elttype = mtype.arguments.first
2841 return v.native_array_instance(elttype, l)
2842 else if ctype == "val*" then
2843 recv = v.init_instance(mtype)
2844 else if ctype == "void*" then
2845 recv = v.new_expr("NULL/*special!*/", mtype)
2846 else
2847 recv = v.new_expr("({ctype})0/*special!*/", mtype)
2848 end
2849 var args = [recv]
2850 for a in self.n_args.n_exprs do
2851 args.add(v.expr(a, null))
2852 end
2853 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2854 if res2 != null then
2855 #self.debug("got {res2} from {mproperty}. drop {recv}")
2856 return res2
2857 end
2858 return recv
2859 end
2860 end
2861
2862 redef class AAttrExpr
2863 redef fun expr(v)
2864 do
2865 var recv = v.expr(self.n_expr, null)
2866 var mproperty = self.mproperty.as(not null)
2867 return v.read_attribute(mproperty, recv)
2868 end
2869 end
2870
2871 redef class AAttrAssignExpr
2872 redef fun stmt(v)
2873 do
2874 var recv = v.expr(self.n_expr, null)
2875 var i = v.expr(self.n_value, null)
2876 var mproperty = self.mproperty.as(not null)
2877 v.write_attribute(mproperty, recv, i)
2878 end
2879 end
2880
2881 redef class AAttrReassignExpr
2882 redef fun stmt(v)
2883 do
2884 var recv = v.expr(self.n_expr, null)
2885 var value = v.expr(self.n_value, null)
2886 var mproperty = self.mproperty.as(not null)
2887 var attr = v.read_attribute(mproperty, recv)
2888 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2889 assert res != null
2890 v.write_attribute(mproperty, recv, res)
2891 end
2892 end
2893
2894 redef class AIssetAttrExpr
2895 redef fun expr(v)
2896 do
2897 var recv = v.expr(self.n_expr, null)
2898 var mproperty = self.mproperty.as(not null)
2899 return v.isset_attribute(mproperty, recv)
2900 end
2901 end
2902
2903 redef class ADebugTypeExpr
2904 redef fun stmt(v)
2905 do
2906 # do nothing
2907 end
2908 end
2909
2910 # Utils
2911
2912 redef class Array[E]
2913 # Return a new `Array` with the elements only contened in self and not in `o`
2914 fun -(o: Array[E]): Array[E] do
2915 var res = new Array[E]
2916 for e in self do if not o.has(e) then res.add(e)
2917 return res
2918 end
2919 end
2920
2921 redef class MModule
2922 # All `MProperty` associated to all `MClassDef` of `mclass`
2923 fun properties(mclass: MClass): Set[MProperty] do
2924 if not self.properties_cache.has_key(mclass) then
2925 var properties = new HashSet[MProperty]
2926 var parents = new Array[MClass]
2927 if self.flatten_mclass_hierarchy.has(mclass) then
2928 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2929 end
2930 for parent in parents do
2931 properties.add_all(self.properties(parent))
2932 end
2933 for mclassdef in mclass.mclassdefs do
2934 if not self.in_importation <= mclassdef.mmodule then continue
2935 for mprop in mclassdef.intro_mproperties do
2936 properties.add(mprop)
2937 end
2938 end
2939 self.properties_cache[mclass] = properties
2940 end
2941 return properties_cache[mclass]
2942 end
2943 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2944
2945 # Write FFI and nitni results to file
2946 fun finalize_ffi(c: AbstractCompiler) do end
2947
2948 # Give requided addinional system libraries (as given to LD_LIBS)
2949 # Note: can return null instead of an empty set
2950 fun collect_linker_libs: nullable Set[String] do return null
2951 end
2952
2953 # Create a tool context to handle options and paths
2954 var toolcontext = new ToolContext
2955
2956 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
2957 toolcontext.option_context.add_option(opt_mixins)
2958
2959 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
2960
2961 # We do not add other options, so process them now!
2962 toolcontext.process_options(args)
2963
2964 # We need a model to collect stufs
2965 var model = new Model
2966 # An a model builder to parse files
2967 var modelbuilder = new ModelBuilder(model, toolcontext)
2968
2969 var arguments = toolcontext.option_context.rest
2970 if arguments.length > 1 and toolcontext.opt_output.value != null then
2971 print "Error: --output needs a single source file. Do you prefer --dir?"
2972 exit 1
2973 end
2974
2975 # Here we load an process all modules passed on the command line
2976 var mmodules = modelbuilder.parse(arguments)
2977 var mixins = modelbuilder.parse(opt_mixins.value)
2978
2979 if mmodules.is_empty then return
2980 modelbuilder.run_phases
2981
2982 for mmodule in mmodules do
2983 toolcontext.info("*** PROCESS {mmodule} ***", 1)
2984 var ms = [mmodule]
2985 if not mixins.is_empty then
2986 ms.add_all mixins
2987 end
2988 toolcontext.run_global_phases(ms)
2989 end
2990