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