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