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