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