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