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