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