8015a5a14a888a371a4fa689c233f2e8a88d9137
[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 arguments.length == 1 then
2125 var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
2126 v.assign(v.frame.returnvar.as(not null), res)
2127 else
2128 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
2129 end
2130 end
2131
2132 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2133 do
2134 var nexpr = self.n_expr
2135 if nexpr != null then
2136 var oldnode = v.current_node
2137 v.current_node = self
2138 var old_frame = v.frame
2139 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2140 v.frame = frame
2141 var value = v.expr(nexpr, self.mpropdef.static_mtype)
2142 v.write_attribute(self.mpropdef.mproperty, recv, value)
2143 v.frame = old_frame
2144 v.current_node = oldnode
2145 end
2146 end
2147
2148 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
2149 do
2150 var nexpr = self.n_expr
2151 if nexpr != null then return
2152
2153 var oldnode = v.current_node
2154 v.current_node = self
2155 var old_frame = v.frame
2156 var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
2157 v.frame = frame
2158 # Force read to check the initialization
2159 v.read_attribute(self.mpropdef.mproperty, recv)
2160 v.frame = old_frame
2161 v.current_node = oldnode
2162 end
2163 end
2164
2165 redef class AClassdef
2166 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2167 do
2168 if mpropdef == self.mfree_init then
2169 var super_inits = self.super_inits
2170 if super_inits != null then
2171 var args_of_super = arguments
2172 if arguments.length > 1 then args_of_super = [arguments.first]
2173 for su in super_inits do
2174 v.send(su, args_of_super)
2175 end
2176 end
2177 var recv = arguments.first
2178 var i = 1
2179 # Collect undefined attributes
2180 for npropdef in self.n_propdefs do
2181 if npropdef isa AAttrPropdef and npropdef.n_expr == null and not npropdef.noinit then
2182 v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
2183 i += 1
2184 end
2185 end
2186 else
2187 abort
2188 end
2189 end
2190 end
2191
2192 redef class AExpr
2193 # Try to compile self as an expression
2194 # Do not call this method directly, use `v.expr` instead
2195 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
2196 do
2197 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
2198 var mtype = self.mtype
2199 if mtype == null then
2200 return null
2201 else
2202 var res = v.new_var(mtype)
2203 v.add("/* {res} = NOT YET {class_name} */")
2204 return res
2205 end
2206 end
2207
2208 # Try to compile self as a statement
2209 # Do not call this method directly, use `v.stmt` instead
2210 private fun stmt(v: AbstractCompilerVisitor)
2211 do
2212 var res = expr(v)
2213 if res != null then v.add("{res};")
2214 end
2215 end
2216
2217 redef class ABlockExpr
2218 redef fun stmt(v)
2219 do
2220 for e in self.n_expr do v.stmt(e)
2221 end
2222 redef fun expr(v)
2223 do
2224 var last = self.n_expr.last
2225 for e in self.n_expr do
2226 if e == last then break
2227 v.stmt(e)
2228 end
2229 return v.expr(last, null)
2230 end
2231 end
2232
2233 redef class AVardeclExpr
2234 redef fun stmt(v)
2235 do
2236 var variable = self.variable.as(not null)
2237 var ne = self.n_expr
2238 if ne != null then
2239 var i = v.expr(ne, variable.declared_type)
2240 v.assign(v.variable(variable), i)
2241 end
2242 end
2243 end
2244
2245 redef class AVarExpr
2246 redef fun expr(v)
2247 do
2248 var res = v.variable(self.variable.as(not null))
2249 var mtype = self.mtype.as(not null)
2250 return v.autoadapt(res, mtype)
2251 end
2252 end
2253
2254 redef class AVarAssignExpr
2255 redef fun stmt(v)
2256 do
2257 var variable = self.variable.as(not null)
2258 var i = v.expr(self.n_value, variable.declared_type)
2259 v.assign(v.variable(variable), i)
2260 end
2261 redef fun expr(v)
2262 do
2263 var variable = self.variable.as(not null)
2264 var i = v.expr(self.n_value, variable.declared_type)
2265 v.assign(v.variable(variable), i)
2266 return i
2267 end
2268 end
2269
2270 redef class AVarReassignExpr
2271 redef fun stmt(v)
2272 do
2273 var variable = self.variable.as(not null)
2274 var vari = v.variable(variable)
2275 var value = v.expr(self.n_value, variable.declared_type)
2276 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
2277 assert res != null
2278 v.assign(v.variable(variable), res)
2279 end
2280 end
2281
2282 redef class ASelfExpr
2283 redef fun expr(v) do return v.frame.arguments.first
2284 end
2285
2286 redef class AContinueExpr
2287 redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
2288 end
2289
2290 redef class ABreakExpr
2291 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
2292 end
2293
2294 redef class AReturnExpr
2295 redef fun stmt(v)
2296 do
2297 var nexpr = self.n_expr
2298 if nexpr != null then
2299 var returnvar = v.frame.returnvar.as(not null)
2300 var i = v.expr(nexpr, returnvar.mtype)
2301 v.assign(returnvar, i)
2302 end
2303 v.add("goto {v.frame.returnlabel.as(not null)};")
2304 end
2305 end
2306
2307 redef class AAbortExpr
2308 redef fun stmt(v) do v.add_abort("Aborted")
2309 end
2310
2311 redef class AIfExpr
2312 redef fun stmt(v)
2313 do
2314 var cond = v.expr_bool(self.n_expr)
2315 v.add("if ({cond})\{")
2316 v.stmt(self.n_then)
2317 v.add("\} else \{")
2318 v.stmt(self.n_else)
2319 v.add("\}")
2320 end
2321
2322 redef fun expr(v)
2323 do
2324 var res = v.new_var(self.mtype.as(not null))
2325 var cond = v.expr_bool(self.n_expr)
2326 v.add("if ({cond})\{")
2327 v.assign(res, v.expr(self.n_then.as(not null), null))
2328 v.add("\} else \{")
2329 v.assign(res, v.expr(self.n_else.as(not null), null))
2330 v.add("\}")
2331 return res
2332 end
2333 end
2334
2335 redef class AIfexprExpr
2336 redef fun expr(v)
2337 do
2338 var res = v.new_var(self.mtype.as(not null))
2339 var cond = v.expr_bool(self.n_expr)
2340 v.add("if ({cond})\{")
2341 v.assign(res, v.expr(self.n_then, null))
2342 v.add("\} else \{")
2343 v.assign(res, v.expr(self.n_else, null))
2344 v.add("\}")
2345 return res
2346 end
2347 end
2348
2349 redef class ADoExpr
2350 redef fun stmt(v)
2351 do
2352 v.stmt(self.n_block)
2353 var escapemark = self.escapemark
2354 if escapemark != null then
2355 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2356 end
2357 end
2358 end
2359
2360 redef class AWhileExpr
2361 redef fun stmt(v)
2362 do
2363 v.add("for(;;) \{")
2364 var cond = v.expr_bool(self.n_expr)
2365 v.add("if (!{cond}) break;")
2366 v.stmt(self.n_block)
2367 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2368 v.add("\}")
2369 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2370 end
2371 end
2372
2373 redef class ALoopExpr
2374 redef fun stmt(v)
2375 do
2376 v.add("for(;;) \{")
2377 v.stmt(self.n_block)
2378 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2379 v.add("\}")
2380 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2381 end
2382 end
2383
2384 redef class AForExpr
2385 redef fun stmt(v)
2386 do
2387 # Shortcut on explicit range
2388 # Avoid the instantiation of the range and the iterator
2389 var nexpr = self.n_expr
2390 if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
2391 var from = v.expr(nexpr.n_expr, null)
2392 var to = v.expr(nexpr.n_expr2, null)
2393 var variable = v.variable(variables.first)
2394
2395 v.assign(variable, from)
2396 v.add("for(;;) \{ /* shortcut range */")
2397
2398 var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
2399 assert ok != null
2400 v.add("if(!{ok}) break;")
2401
2402 v.stmt(self.n_block)
2403
2404 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2405 var succ = v.send(v.get_property("succ", variable.mtype), [variable])
2406 assert succ != null
2407 v.assign(variable, succ)
2408 v.add("\}")
2409 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2410 return
2411 end
2412
2413 var cl = v.expr(self.n_expr, null)
2414 var it_meth = self.method_iterator
2415 assert it_meth != null
2416 var it = v.compile_callsite(it_meth, [cl])
2417 assert it != null
2418 v.add("for(;;) \{")
2419 var isok_meth = self.method_is_ok
2420 assert isok_meth != null
2421 var ok = v.compile_callsite(isok_meth, [it])
2422 assert ok != null
2423 v.add("if(!{ok}) break;")
2424 if self.variables.length == 1 then
2425 var item_meth = self.method_item
2426 assert item_meth != null
2427 var i = v.compile_callsite(item_meth, [it])
2428 assert i != null
2429 v.assign(v.variable(variables.first), i)
2430 else if self.variables.length == 2 then
2431 var key_meth = self.method_key
2432 assert key_meth != null
2433 var i = v.compile_callsite(key_meth, [it])
2434 assert i != null
2435 v.assign(v.variable(variables[0]), i)
2436 var item_meth = self.method_item
2437 assert item_meth != null
2438 i = v.compile_callsite(item_meth, [it])
2439 assert i != null
2440 v.assign(v.variable(variables[1]), i)
2441 else
2442 abort
2443 end
2444 v.stmt(self.n_block)
2445 v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
2446 var next_meth = self.method_next
2447 assert next_meth != null
2448 v.compile_callsite(next_meth, [it])
2449 v.add("\}")
2450 v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
2451 end
2452 end
2453
2454 redef class AAssertExpr
2455 redef fun stmt(v)
2456 do
2457 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
2458
2459 var cond = v.expr_bool(self.n_expr)
2460 v.add("if (unlikely(!{cond})) \{")
2461 v.stmt(self.n_else)
2462 var nid = self.n_id
2463 if nid != null then
2464 v.add_abort("Assert '{nid.text}' failed")
2465 else
2466 v.add_abort("Assert failed")
2467 end
2468 v.add("\}")
2469 end
2470 end
2471
2472 redef class AOrExpr
2473 redef fun expr(v)
2474 do
2475 var res = v.new_var(self.mtype.as(not null))
2476 var i1 = v.expr_bool(self.n_expr)
2477 v.add("if ({i1}) \{")
2478 v.add("{res} = 1;")
2479 v.add("\} else \{")
2480 var i2 = v.expr_bool(self.n_expr2)
2481 v.add("{res} = {i2};")
2482 v.add("\}")
2483 return res
2484 end
2485 end
2486
2487 redef class AImpliesExpr
2488 redef fun expr(v)
2489 do
2490 var res = v.new_var(self.mtype.as(not null))
2491 var i1 = v.expr_bool(self.n_expr)
2492 v.add("if (!{i1}) \{")
2493 v.add("{res} = 1;")
2494 v.add("\} else \{")
2495 var i2 = v.expr_bool(self.n_expr2)
2496 v.add("{res} = {i2};")
2497 v.add("\}")
2498 return res
2499 end
2500 end
2501
2502 redef class AAndExpr
2503 redef fun expr(v)
2504 do
2505 var res = v.new_var(self.mtype.as(not null))
2506 var i1 = v.expr_bool(self.n_expr)
2507 v.add("if (!{i1}) \{")
2508 v.add("{res} = 0;")
2509 v.add("\} else \{")
2510 var i2 = v.expr_bool(self.n_expr2)
2511 v.add("{res} = {i2};")
2512 v.add("\}")
2513 return res
2514 end
2515 end
2516
2517 redef class ANotExpr
2518 redef fun expr(v)
2519 do
2520 var cond = v.expr_bool(self.n_expr)
2521 return v.new_expr("!{cond}", self.mtype.as(not null))
2522 end
2523 end
2524
2525 redef class AOrElseExpr
2526 redef fun expr(v)
2527 do
2528 var res = v.new_var(self.mtype.as(not null))
2529 var i1 = v.expr(self.n_expr, null)
2530 v.add("if ({i1}!=NULL) \{")
2531 v.assign(res, i1)
2532 v.add("\} else \{")
2533 var i2 = v.expr(self.n_expr2, null)
2534 v.assign(res, i2)
2535 v.add("\}")
2536 return res
2537 end
2538 end
2539
2540 redef class AIntExpr
2541 redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
2542 end
2543
2544 redef class AFloatExpr
2545 redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
2546 end
2547
2548 redef class ACharExpr
2549 redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
2550 end
2551
2552 redef class AArrayExpr
2553 redef fun expr(v)
2554 do
2555 var mtype = self.mtype.as(MClassType).arguments.first
2556 var array = new Array[RuntimeVariable]
2557 for nexpr in self.n_exprs.n_exprs do
2558 var i = v.expr(nexpr, mtype)
2559 array.add(i)
2560 end
2561 return v.array_instance(array, mtype)
2562 end
2563 end
2564
2565 redef class AStringFormExpr
2566 redef fun expr(v) do return v.string_instance(self.value.as(not null))
2567 end
2568
2569 redef class ASuperstringExpr
2570 redef fun expr(v)
2571 do
2572 var array = new Array[RuntimeVariable]
2573 for ne in self.n_exprs do
2574 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
2575 var i = v.expr(ne, null)
2576 array.add(i)
2577 end
2578 var a = v.array_instance(array, v.object_type)
2579 var res = v.send(v.get_property("to_s", a.mtype), [a])
2580 return res
2581 end
2582 end
2583
2584 redef class ACrangeExpr
2585 redef fun expr(v)
2586 do
2587 var i1 = v.expr(self.n_expr, null)
2588 var i2 = v.expr(self.n_expr2, null)
2589 var mtype = self.mtype.as(MClassType)
2590 var res = v.init_instance(mtype)
2591 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2592 return res
2593 end
2594 end
2595
2596 redef class AOrangeExpr
2597 redef fun expr(v)
2598 do
2599 var i1 = v.expr(self.n_expr, null)
2600 var i2 = v.expr(self.n_expr2, null)
2601 var mtype = self.mtype.as(MClassType)
2602 var res = v.init_instance(mtype)
2603 var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
2604 return res
2605 end
2606 end
2607
2608 redef class ATrueExpr
2609 redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
2610 end
2611
2612 redef class AFalseExpr
2613 redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
2614 end
2615
2616 redef class ANullExpr
2617 redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
2618 end
2619
2620 redef class AIsaExpr
2621 redef fun expr(v)
2622 do
2623 var i = v.expr(self.n_expr, null)
2624 return v.type_test(i, self.cast_type.as(not null), "isa")
2625 end
2626 end
2627
2628 redef class AAsCastExpr
2629 redef fun expr(v)
2630 do
2631 var i = v.expr(self.n_expr, null)
2632 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2633
2634 v.add_cast(i, self.mtype.as(not null), "as")
2635 return i
2636 end
2637 end
2638
2639 redef class AAsNotnullExpr
2640 redef fun expr(v)
2641 do
2642 var i = v.expr(self.n_expr, null)
2643 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
2644
2645 if i.mtype.ctype != "val*" then return i
2646
2647 v.add("if (unlikely({i} == NULL)) \{")
2648 v.add_abort("Cast failed")
2649 v.add("\}")
2650 return i
2651 end
2652 end
2653
2654 redef class AParExpr
2655 redef fun expr(v) do return v.expr(self.n_expr, null)
2656 end
2657
2658 redef class AOnceExpr
2659 redef fun expr(v)
2660 do
2661 var mtype = self.mtype.as(not null)
2662 var name = v.get_name("varonce")
2663 var guard = v.get_name(name + "_guard")
2664 v.add_decl("static {mtype.ctype} {name};")
2665 v.add_decl("static int {guard};")
2666 var res = v.new_var(mtype)
2667 v.add("if ({guard}) \{")
2668 v.add("{res} = {name};")
2669 v.add("\} else \{")
2670 var i = v.expr(self.n_expr, mtype)
2671 v.add("{res} = {i};")
2672 v.add("{name} = {res};")
2673 v.add("{guard} = 1;")
2674 v.add("\}")
2675 return res
2676 end
2677 end
2678
2679 redef class ASendExpr
2680 redef fun expr(v)
2681 do
2682 var recv = v.expr(self.n_expr, null)
2683 var args = [recv]
2684 for a in self.raw_arguments do
2685 args.add(v.expr(a, null))
2686 end
2687 return v.compile_callsite(self.callsite.as(not null), args)
2688 end
2689 end
2690
2691 redef class ASendReassignFormExpr
2692 redef fun stmt(v)
2693 do
2694 var recv = v.expr(self.n_expr, null)
2695 var args = [recv]
2696 for a in self.raw_arguments do
2697 args.add(v.expr(a, null))
2698 end
2699 var value = v.expr(self.n_value, null)
2700
2701 var left = v.compile_callsite(self.callsite.as(not null), args)
2702 assert left != null
2703
2704 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
2705 assert res != null
2706
2707 args.add(res)
2708 v.compile_callsite(self.write_callsite.as(not null), args)
2709 end
2710 end
2711
2712 redef class ASuperExpr
2713 redef fun expr(v)
2714 do
2715 var recv = v.frame.arguments.first
2716 var args = [recv]
2717 for a in self.n_args.n_exprs do
2718 args.add(v.expr(a, null))
2719 end
2720
2721 var callsite = self.callsite
2722 if callsite != null then
2723 # Add additionnals arguments for the super init call
2724 if args.length == 1 then
2725 for i in [0..callsite.msignature.arity[ do
2726 args.add(v.frame.arguments[i+1])
2727 end
2728 end
2729 # Super init call
2730 var res = v.compile_callsite(callsite, args)
2731 return res
2732 end
2733
2734 if args.length == 1 then
2735 args = v.frame.arguments
2736 end
2737
2738 # stantard call-next-method
2739 return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
2740 end
2741 end
2742
2743 redef class ANewExpr
2744 redef fun expr(v)
2745 do
2746 var mtype = self.mtype.as(MClassType)
2747 var recv
2748 var ctype = mtype.ctype
2749 if mtype.mclass.name == "NativeArray" then
2750 assert self.n_args.n_exprs.length == 1
2751 var l = v.expr(self.n_args.n_exprs.first, null)
2752 assert mtype isa MGenericType
2753 var elttype = mtype.arguments.first
2754 return v.native_array_instance(elttype, l)
2755 else if ctype == "val*" then
2756 recv = v.init_instance(mtype)
2757 else if ctype == "void*" then
2758 recv = v.new_expr("NULL/*special!*/", mtype)
2759 else
2760 recv = v.new_expr("({ctype})0/*special!*/", mtype)
2761 end
2762 var args = [recv]
2763 for a in self.n_args.n_exprs do
2764 args.add(v.expr(a, null))
2765 end
2766 var res2 = v.compile_callsite(self.callsite.as(not null), args)
2767 if res2 != null then
2768 #self.debug("got {res2} from {mproperty}. drop {recv}")
2769 return res2
2770 end
2771 return recv
2772 end
2773 end
2774
2775 redef class AAttrExpr
2776 redef fun expr(v)
2777 do
2778 var recv = v.expr(self.n_expr, null)
2779 var mproperty = self.mproperty.as(not null)
2780 return v.read_attribute(mproperty, recv)
2781 end
2782 end
2783
2784 redef class AAttrAssignExpr
2785 redef fun stmt(v)
2786 do
2787 var recv = v.expr(self.n_expr, null)
2788 var i = v.expr(self.n_value, null)
2789 var mproperty = self.mproperty.as(not null)
2790 v.write_attribute(mproperty, recv, i)
2791 end
2792 end
2793
2794 redef class AAttrReassignExpr
2795 redef fun stmt(v)
2796 do
2797 var recv = v.expr(self.n_expr, null)
2798 var value = v.expr(self.n_value, null)
2799 var mproperty = self.mproperty.as(not null)
2800 var attr = v.read_attribute(mproperty, recv)
2801 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
2802 assert res != null
2803 v.write_attribute(mproperty, recv, res)
2804 end
2805 end
2806
2807 redef class AIssetAttrExpr
2808 redef fun expr(v)
2809 do
2810 var recv = v.expr(self.n_expr, null)
2811 var mproperty = self.mproperty.as(not null)
2812 return v.isset_attribute(mproperty, recv)
2813 end
2814 end
2815
2816 redef class ADebugTypeExpr
2817 redef fun stmt(v)
2818 do
2819 # do nothing
2820 end
2821 end
2822
2823 # Utils
2824
2825 redef class Array[E]
2826 # Return a new `Array` with the elements only contened in self and not in `o`
2827 fun -(o: Array[E]): Array[E] do
2828 var res = new Array[E]
2829 for e in self do if not o.has(e) then res.add(e)
2830 return res
2831 end
2832 end
2833
2834 redef class MModule
2835 # All `MProperty` associated to all `MClassDef` of `mclass`
2836 fun properties(mclass: MClass): Set[MProperty] do
2837 if not self.properties_cache.has_key(mclass) then
2838 var properties = new HashSet[MProperty]
2839 var parents = new Array[MClass]
2840 if self.flatten_mclass_hierarchy.has(mclass) then
2841 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
2842 end
2843 for parent in parents do
2844 properties.add_all(self.properties(parent))
2845 end
2846 for mclassdef in mclass.mclassdefs do
2847 if not self.in_importation <= mclassdef.mmodule then continue
2848 for mprop in mclassdef.intro_mproperties do
2849 properties.add(mprop)
2850 end
2851 end
2852 self.properties_cache[mclass] = properties
2853 end
2854 return properties_cache[mclass]
2855 end
2856 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
2857
2858 # Write FFI and nitni results to file
2859 fun finalize_ffi(c: AbstractCompiler) do end
2860
2861 # Give requided addinional system libraries (as given to LD_LIBS)
2862 # Note: can return null instead of an empty set
2863 fun collect_linker_libs: nullable Set[String] do return null
2864 end
2865
2866 # Create a tool context to handle options and paths
2867 var toolcontext = new ToolContext
2868
2869 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
2870 toolcontext.option_context.add_option(opt_mixins)
2871
2872 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
2873
2874 # We do not add other options, so process them now!
2875 toolcontext.process_options(args)
2876
2877 # We need a model to collect stufs
2878 var model = new Model
2879 # An a model builder to parse files
2880 var modelbuilder = new ModelBuilder(model, toolcontext)
2881
2882 var arguments = toolcontext.option_context.rest
2883 if arguments.length > 1 and toolcontext.opt_output.value != null then
2884 print "Error: --output needs a single source file. Do you prefer --dir?"
2885 exit 1
2886 end
2887
2888 # Here we load an process all modules passed on the command line
2889 var mmodules = modelbuilder.parse(arguments)
2890 var mixins = modelbuilder.parse(opt_mixins.value)
2891
2892 if mmodules.is_empty then return
2893 modelbuilder.run_phases
2894
2895 for mmodule in mmodules do
2896 toolcontext.info("*** PROCESS {mmodule} ***", 1)
2897 var ms = [mmodule]
2898 if not mixins.is_empty then
2899 ms.add_all mixins
2900 end
2901 toolcontext.run_global_phases(ms)
2902 end
2903