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