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