lib/core: Re-implemented some frequently used binops for primitive types as intern...
[nit.git] / src / compiler / abstract_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Abstract compiler
18 module abstract_compiler
19
20 import literal
21 import semantize
22 import platform
23 import c_tools
24 private import annotation
25 import mixin
26 import counter
27
28 # Add compiling options
29 redef class ToolContext
30 # --output
31 var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
32 # --dir
33 var opt_dir = new OptionString("Output directory", "--dir")
34 # --no-cc
35 var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
36 # --no-main
37 var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
38 # --make-flags
39 var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
40 # --max-c-lines
41 var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
42 # --group-c-files
43 var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
44 # --compile-dir
45 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
46 # --hardening
47 var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
48 # --no-check-covariance
49 var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
50 # --no-check-attr-isset
51 var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
52 # --no-check-assert
53 var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit `assert` and `as` (dangerous)", "--no-check-assert")
54 # --no-check-autocast
55 var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
56 # --no-check-null
57 var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
58 # --no-check-all
59 var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
60 # --typing-test-metrics
61 var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
62 # --invocation-metrics
63 var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
64 # --isset-checks-metrics
65 var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
66 # --no-stacktrace
67 var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
68 # --no-gcc-directives
69 var opt_no_gcc_directive = new OptionArray("Disable advanced gcc directives for optimization", "--no-gcc-directive")
70 # --release
71 var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
72 # -g
73 var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
74
75 redef init
76 do
77 super
78 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)
79 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)
80 self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
81 self.option_context.add_option(self.opt_no_stacktrace)
82 self.option_context.add_option(self.opt_no_gcc_directive)
83 self.option_context.add_option(self.opt_release)
84 self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
85 self.option_context.add_option(self.opt_debug)
86
87 opt_no_main.hidden = true
88 end
89
90 redef fun process_options(args)
91 do
92 super
93
94 if opt_output.value != null and opt_dir.value != null then
95 print "Option Error: cannot use both --dir and --output"
96 exit(1)
97 end
98
99 if opt_no_check_all.value then
100 opt_no_check_covariance.value = true
101 opt_no_check_attr_isset.value = true
102 opt_no_check_assert.value = true
103 opt_no_check_autocast.value = true
104 opt_no_check_null.value = true
105 end
106 end
107 end
108
109 redef class ModelBuilder
110 # Simple indirection to `Toolchain::write_and_make`
111 protected fun write_and_make(compiler: AbstractCompiler)
112 do
113 var platform = compiler.target_platform
114 var toolchain = platform.toolchain(toolcontext, compiler)
115 compiler.toolchain = toolchain
116 toolchain.write_and_make
117 end
118 end
119
120 redef class Platform
121 # The specific tool-chain associated to the platform
122 fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
123 do
124 return new MakefileToolchain(toolcontext, compiler)
125 end
126 end
127
128 # Build toolchain for a specific target program, varies per `Platform`
129 class Toolchain
130
131 # Toolcontext
132 var toolcontext: ToolContext
133
134 # Compiler of the target program
135 var compiler: AbstractCompiler
136
137 # Directory where to generate all files
138 #
139 # The option `--compile_dir` change this directory.
140 fun root_compile_dir: String
141 do
142 var compile_dir = toolcontext.opt_compile_dir.value
143 if compile_dir == null then compile_dir = "nit_compile"
144 return compile_dir
145 end
146
147 # Directory where to generate all C files
148 #
149 # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
150 fun compile_dir: String do return root_compile_dir
151
152 # Write all C files and compile them
153 fun write_and_make is abstract
154 end
155
156 # Default toolchain using a Makefile
157 class MakefileToolchain
158 super Toolchain
159
160 redef fun write_and_make
161 do
162 var debug = toolcontext.opt_debug.value
163 var compile_dir = compile_dir
164
165 # Remove the compilation directory unless explicitly set
166 var auto_remove = toolcontext.opt_compile_dir.value == null
167 # If debug flag is set, do not remove sources
168 if debug then auto_remove = false
169
170 # Generate the .h and .c files
171 # A single C file regroups many compiled rumtime functions
172 # 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
173 var time0 = get_time
174 self.toolcontext.info("*** WRITING C ***", 1)
175
176 root_compile_dir.mkdir
177 compile_dir.mkdir
178
179 var cfiles = new Array[String]
180 write_files(compile_dir, cfiles)
181
182 # Generate the Makefile
183
184 write_makefile(compile_dir, cfiles)
185
186 var time1 = get_time
187 self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
188
189 # Execute the Makefile
190
191 if self.toolcontext.opt_no_cc.value then return
192
193 time0 = time1
194 self.toolcontext.info("*** COMPILING C ***", 1)
195
196 compile_c_code(compile_dir)
197
198 if auto_remove then
199 sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
200 end
201
202 time1 = get_time
203 self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
204 end
205
206 # Write all source files to the `compile_dir`
207 fun write_files(compile_dir: String, cfiles: Array[String])
208 do
209 var platform = compiler.target_platform
210 if platform.supports_libunwind then compiler.build_c_to_nit_bindings
211 var cc_opt_with_libgc = "-DWITH_LIBGC"
212 if not platform.supports_libgc then cc_opt_with_libgc = ""
213
214 # Add gc_choser.h to aditionnal bodies
215 var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
216 if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
217 compiler.extern_bodies.add(gc_chooser)
218 var clib = toolcontext.nit_dir / "clib"
219 compiler.files_to_copy.add "{clib}/gc_chooser.c"
220 compiler.files_to_copy.add "{clib}/gc_chooser.h"
221
222 # FFI
223 for m in compiler.mainmodule.in_importation.greaters do
224 compiler.finalize_ffi_for_module(m)
225 end
226
227 # Copy original .[ch] files to compile_dir
228 for src in compiler.files_to_copy do
229 var basename = src.basename
230 var dst = "{compile_dir}/{basename}"
231 src.file_copy_to dst
232 end
233
234 var hfilename = compiler.header.file.name + ".h"
235 var hfilepath = "{compile_dir}/{hfilename}"
236 var h = new FileWriter.open(hfilepath)
237 for l in compiler.header.decl_lines do
238 h.write l
239 h.write "\n"
240 end
241 for l in compiler.header.lines do
242 h.write l
243 h.write "\n"
244 end
245 h.close
246
247 var max_c_lines = toolcontext.opt_max_c_lines.value
248 for f in compiler.files do
249 var i = 0
250 var count = 0
251 var file: nullable FileWriter = null
252 for vis in f.writers do
253 if vis == compiler.header then continue
254 var total_lines = vis.lines.length + vis.decl_lines.length
255 if total_lines == 0 then continue
256 count += total_lines
257 if file == null or (count > max_c_lines and max_c_lines > 0) then
258 i += 1
259 if file != null then file.close
260 var cfilename = "{f.name}.{i}.c"
261 var cfilepath = "{compile_dir}/{cfilename}"
262 self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
263 cfiles.add(cfilename)
264 file = new FileWriter.open(cfilepath)
265 file.write "#include \"{f.name}.0.h\"\n"
266 count = total_lines
267 end
268 for l in vis.decl_lines do
269 file.write l
270 file.write "\n"
271 end
272 for l in vis.lines do
273 file.write l
274 file.write "\n"
275 end
276 end
277 if file == null then continue
278 file.close
279
280 var cfilename = "{f.name}.0.h"
281 var cfilepath = "{compile_dir}/{cfilename}"
282 var hfile: nullable FileWriter = null
283 hfile = new FileWriter.open(cfilepath)
284 hfile.write "#include \"{hfilename}\"\n"
285 for key in f.required_declarations do
286 if not compiler.provided_declarations.has_key(key) then
287 var node = compiler.requirers_of_declarations.get_or_null(key)
288 if node != null then
289 node.debug "No provided declaration for {key}"
290 else
291 print "No provided declaration for {key}"
292 end
293 abort
294 end
295 hfile.write compiler.provided_declarations[key]
296 hfile.write "\n"
297 end
298 hfile.close
299 end
300
301 self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
302 end
303
304 # Get the name of the Makefile to use
305 fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
306
307 # Get the default name of the executable to produce
308 fun default_outname: String
309 do
310 var mainmodule = compiler.mainmodule.first_real_mmodule
311 return mainmodule.name
312 end
313
314 # Combine options and platform informations to get the final path of the outfile
315 fun outfile(mainmodule: MModule): String
316 do
317 var res = self.toolcontext.opt_output.value
318 if res != null then return res
319 res = default_outname
320 var dir = self.toolcontext.opt_dir.value
321 if dir != null then return dir.join_path(res)
322 return res
323 end
324
325 # Write the Makefile
326 fun write_makefile(compile_dir: String, cfiles: Array[String])
327 do
328 var mainmodule = compiler.mainmodule
329 var platform = compiler.target_platform
330
331 var outname = outfile(mainmodule)
332
333 var real_outpath = compile_dir.relpath(outname)
334 var outpath = real_outpath.escape_to_mk
335 if outpath != real_outpath then
336 # If the name is crazy and need escaping, we will do an indirection
337 # 1. generate the binary in the nit_compile dir under an escaped name
338 # 2. copy the binary at the right place in the `all` goal.
339 outpath = mainmodule.c_name
340 end
341 var makename = makefile_name
342 var makepath = "{compile_dir}/{makename}"
343 var makefile = new FileWriter.open(makepath)
344
345 var linker_options = new HashSet[String]
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 var debug = toolcontext.opt_debug.value
351
352 makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
353
354 makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
355 if platform.supports_libunwind then
356 if toolcontext.opt_no_stacktrace.value then
357 makefile.write "NO_STACKTRACE=True"
358 else
359 makefile.write "NO_STACKTRACE= # Set to `True` to enable"
360 end
361 end
362
363 # Dynamic adaptations
364 # While `platform` enable complex toolchains, they are statically applied
365 # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
366 makefile.write "\n\n"
367
368 # Check and adapt the targeted system
369 makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
370
371 # Check and adapt for the compiler used
372 # clang need an additionnal `-Qunused-arguments`
373 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")
374
375 if platform.supports_libunwind then
376 makefile.write """
377 ifneq ($(NO_STACKTRACE), True)
378 # Check and include lib-unwind in a portable way
379 ifneq ($(uname_S),Darwin)
380 # already included on macosx, but need to get the correct flags in other supported platforms.
381 ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
382 LDLIBS += `pkg-config --libs libunwind`
383 CFLAGS += `pkg-config --cflags libunwind`
384 else
385 $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
386 CFLAGS += -D NO_STACKTRACE
387 endif
388 endif
389 else
390 # Stacktraces disabled
391 CFLAGS += -D NO_STACKTRACE
392 endif
393
394 """
395 else
396 makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
397 end
398
399 makefile.write """
400 # Special configuration for Darwin
401 ifeq ($(uname_S),Darwin)
402 # Remove POSIX flag -lrt
403 LDLIBS := $(filter-out -lrt,$(LDLIBS))
404 endif
405
406 """
407
408 makefile.write("all: {outpath}\n")
409 if outpath != real_outpath then
410 makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
411 end
412 makefile.write("\n")
413
414 var ofiles = new Array[String]
415 var dep_rules = new Array[String]
416 # Compile each generated file
417 for f in cfiles do
418 var o = f.strip_extension(".c") + ".o"
419 makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
420 ofiles.add(o)
421 dep_rules.add(o)
422 end
423
424 # Generate linker script, if any
425 if not compiler.linker_script.is_empty then
426 var linker_script_path = "{compile_dir}/linker_script"
427 ofiles.add "linker_script"
428 var f = new FileWriter.open(linker_script_path)
429 for l in compiler.linker_script do
430 f.write l
431 f.write "\n"
432 end
433 f.close
434 end
435
436 var java_files = new Array[ExternFile]
437
438 var pkgconfigs = new Array[String]
439 for f in compiler.extern_bodies do
440 pkgconfigs.add_all f.pkgconfigs
441 end
442 # Protect pkg-config
443 if not pkgconfigs.is_empty then
444 makefile.write """
445 # does pkg-config exists?
446 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
447 $(error "Command `pkg-config` not found. Please install it")
448 endif
449 """
450 for p in pkgconfigs do
451 makefile.write """
452 # Check for library {{{p}}}
453 ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
454 $(error "pkg-config: package {{{p}}} is not found.")
455 endif
456 """
457 end
458 end
459
460 # Compile each required extern body into a specific .o
461 for f in compiler.extern_bodies do
462 var o = f.makefile_rule_name
463 var ff = f.filename.basename
464 makefile.write("{o}: {ff}\n")
465 makefile.write("\t{f.makefile_rule_content}\n\n")
466 dep_rules.add(f.makefile_rule_name)
467
468 if f.compiles_to_o_file then ofiles.add(o)
469 if f.add_to_jar then java_files.add(f)
470 end
471
472 if not java_files.is_empty then
473 var jar_file = "{outpath}.jar"
474
475 var class_files_array = new Array[String]
476 for f in java_files do class_files_array.add(f.makefile_rule_name)
477 var class_files = class_files_array.join(" ")
478
479 makefile.write("{jar_file}: {class_files}\n")
480 makefile.write("\tjar cf {jar_file} {class_files}\n\n")
481 dep_rules.add jar_file
482 end
483
484 # Link edition
485 var pkg = ""
486 if not pkgconfigs.is_empty then
487 pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
488 end
489 makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
490 # Clean
491 makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
492 if outpath != real_outpath then
493 makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
494 end
495 makefile.close
496 self.toolcontext.info("Generated makefile: {makepath}", 2)
497
498 makepath.file_copy_to "{compile_dir}/Makefile"
499 end
500
501 # The C code is generated, compile it to an executable
502 fun compile_c_code(compile_dir: String)
503 do
504 var makename = makefile_name
505
506 var makeflags = self.toolcontext.opt_make_flags.value
507 if makeflags == null then makeflags = ""
508
509 var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
510 self.toolcontext.info(command, 2)
511
512 var res
513 if self.toolcontext.verbose_level >= 3 then
514 res = sys.system("{command} 2>&1")
515 else
516 res = sys.system("{command} 2>&1 >/dev/null")
517 end
518 if res != 0 then
519 toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
520 end
521 end
522 end
523
524 # Singleton that store the knowledge about the compilation process
525 abstract class AbstractCompiler
526 type VISITOR: AbstractCompilerVisitor
527
528 # Table corresponding c_names to nit names (methods)
529 var names = new HashMap[String, String]
530
531 # The main module of the program currently compiled
532 # Is assigned during the separate compilation
533 var mainmodule: MModule is writable
534
535 # The real main module of the program
536 var realmainmodule: MModule is noinit
537
538 # The modelbuilder used to know the model and the AST
539 var modelbuilder: ModelBuilder is protected writable
540
541 # The associated toolchain
542 #
543 # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
544 var toolchain: Toolchain is noinit
545
546 # Is hardening asked? (see --hardening)
547 fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
548
549 # The targeted specific platform
550 var target_platform: Platform is noinit
551
552 init
553 do
554 self.realmainmodule = mainmodule
555 target_platform = mainmodule.target_platform or else new Platform
556 end
557
558 # Do the full code generation of the program `mainmodule`
559 # It is the main method usually called after the instantiation
560 fun do_compilation is abstract
561
562 # Force the creation of a new file
563 # The point is to avoid contamination between must-be-compiled-separately files
564 fun new_file(name: String): CodeFile
565 do
566 if modelbuilder.toolcontext.opt_group_c_files.value then
567 if self.files.is_empty then
568 var f = new CodeFile(mainmodule.c_name)
569 self.files.add(f)
570 end
571 return self.files.first
572 end
573 var f = new CodeFile(name)
574 self.files.add(f)
575 return f
576 end
577
578 # The list of all associated files
579 # Used to generate .c files
580 var files = new List[CodeFile]
581
582 # Initialize a visitor specific for a compiler engine
583 fun new_visitor: VISITOR is abstract
584
585 # Where global declaration are stored (the main .h)
586 var header: CodeWriter is writable, noinit
587
588 # Additionnal linker script for `ld`.
589 # Mainly used to do specific link-time symbol resolution
590 var linker_script = new Array[String]
591
592 # Provide a declaration that can be requested (before or latter) by a visitor
593 fun provide_declaration(key: String, s: String)
594 do
595 if self.provided_declarations.has_key(key) then
596 assert self.provided_declarations[key] == s
597 end
598 self.provided_declarations[key] = s
599 end
600
601 private var provided_declarations = new HashMap[String, String]
602
603 private var requirers_of_declarations = new HashMap[String, ANode]
604
605 # Builds the .c and .h files to be used when generating a Stack Trace
606 # Binds the generated C function names to Nit function names
607 fun build_c_to_nit_bindings
608 do
609 var compile_dir = toolchain.compile_dir
610
611 var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
612 stream.write("#include <string.h>\n")
613 stream.write("#include <stdlib.h>\n")
614 stream.write("#include \"c_functions_hash.h\"\n")
615 stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
616 stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
617 stream.write("char* procname = malloc(len+1);")
618 stream.write("memcpy(procname, procproc, len);")
619 stream.write("procname[len] = '\\0';")
620 stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
621 for i in names.keys do
622 stream.write("\{\"")
623 stream.write(i.escape_to_c)
624 stream.write("\",\"")
625 stream.write(names[i].escape_to_c)
626 stream.write("\"\},\n")
627 end
628 stream.write("\};\n")
629 stream.write("int i;")
630 stream.write("for(i = 0; i < {names.length}; i++)\{")
631 stream.write("if(strcmp(procname,map[i].name) == 0)\{")
632 stream.write("free(procname);")
633 stream.write("return map[i].nit_name;")
634 stream.write("\}")
635 stream.write("\}")
636 stream.write("free(procname);")
637 stream.write("return NULL;")
638 stream.write("\}\n")
639 stream.close
640
641 stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
642 stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
643 stream.close
644
645 extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
646 end
647
648 # Compile C headers
649 # This method call compile_header_strucs method that has to be refined
650 fun compile_header do
651 self.header.add_decl("#include <stdlib.h>")
652 self.header.add_decl("#include <stdio.h>")
653 self.header.add_decl("#include <string.h>")
654 self.header.add_decl("#include <sys/types.h>\n")
655 self.header.add_decl("#include <unistd.h>\n")
656 self.header.add_decl("#include <stdint.h>\n")
657 self.header.add_decl("#include <inttypes.h>\n")
658 self.header.add_decl("#include \"gc_chooser.h\"")
659 self.header.add_decl("#ifdef ANDROID")
660 self.header.add_decl(" #include <android/log.h>")
661 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
662 self.header.add_decl("#else")
663 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
664 self.header.add_decl("#endif")
665
666 compile_header_structs
667 compile_nitni_structs
668
669 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
670 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
671 # Signal handler function prototype
672 self.header.add_decl("void fatal_exit(int);")
673 else
674 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
675 end
676
677 if gccd_disable.has("likely") or gccd_disable.has("all") then
678 self.header.add_decl("#define likely(x) (x)")
679 self.header.add_decl("#define unlikely(x) (x)")
680 else if gccd_disable.has("correct-likely") then
681 # invert the `likely` definition
682 # Used by masochists to bench the worst case
683 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
684 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
685 else
686 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
687 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
688 end
689
690 # Global variable used by intern methods
691 self.header.add_decl("extern int glob_argc;")
692 self.header.add_decl("extern char **glob_argv;")
693 self.header.add_decl("extern val *glob_sys;")
694 end
695
696 # Declaration of structures for live Nit types
697 protected fun compile_header_structs is abstract
698
699 # Declaration of structures for nitni undelying the FFI
700 protected fun compile_nitni_structs
701 do
702 self.header.add_decl """
703 /* Native reference to Nit objects */
704 /* This structure is used to represent every Nit type in extern methods and custom C code. */
705 struct nitni_ref {
706 struct nitni_ref *next,
707 *prev; /* adjacent global references in global list */
708 int count; /* number of time this global reference has been marked */
709 };
710
711 /* List of global references from C code to Nit objects */
712 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
713 struct nitni_global_ref_list_t {
714 struct nitni_ref *head, *tail;
715 };
716 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
717
718 /* Initializer of global reference list */
719 extern void nitni_global_ref_list_init();
720
721 /* Intern function to add a global reference to the list */
722 extern void nitni_global_ref_add( struct nitni_ref *ref );
723
724 /* Intern function to remove a global reference from the list */
725 extern void nitni_global_ref_remove( struct nitni_ref *ref );
726
727 /* Increase count on an existing global reference */
728 extern void nitni_global_ref_incr( struct nitni_ref *ref );
729
730 /* Decrease count on an existing global reference */
731 extern void nitni_global_ref_decr( struct nitni_ref *ref );
732 """
733 end
734
735 fun compile_finalizer_function
736 do
737 var finalizable_type = mainmodule.finalizable_type
738 if finalizable_type == null then return
739
740 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
741
742 if finalize_meth == null then
743 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
744 return
745 end
746
747 var v = self.new_visitor
748 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
749 var recv = v.new_expr("obj", finalizable_type)
750 v.send(finalize_meth, [recv])
751 v.add "\}"
752 end
753
754 # Generate the main C function.
755 #
756 # This function:
757 #
758 # * allocate the Sys object if it exists
759 # * call init if is exists
760 # * call main if it exists
761 fun compile_main_function
762 do
763 var v = self.new_visitor
764 v.add_decl("#include <signal.h>")
765 var platform = target_platform
766
767 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
768
769 if platform.supports_libunwind then
770 v.add_decl("#ifndef NO_STACKTRACE")
771 v.add_decl("#define UNW_LOCAL_ONLY")
772 v.add_decl("#include <libunwind.h>")
773 v.add_decl("#include \"c_functions_hash.h\"")
774 v.add_decl("#endif")
775 end
776 v.add_decl("int glob_argc;")
777 v.add_decl("char **glob_argv;")
778 v.add_decl("val *glob_sys;")
779
780 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
781 for tag in count_type_test_tags do
782 v.add_decl("long count_type_test_resolved_{tag};")
783 v.add_decl("long count_type_test_unresolved_{tag};")
784 v.add_decl("long count_type_test_skipped_{tag};")
785 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
786 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
787 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
788 end
789 end
790
791 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
792 v.add_decl("long count_invoke_by_tables;")
793 v.add_decl("long count_invoke_by_direct;")
794 v.add_decl("long count_invoke_by_inline;")
795 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
796 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
797 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
798 end
799
800 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
801 v.add_decl("long count_attr_reads = 0;")
802 v.add_decl("long count_isset_checks = 0;")
803 v.compiler.header.add_decl("extern long count_attr_reads;")
804 v.compiler.header.add_decl("extern long count_isset_checks;")
805 end
806
807 v.add_decl("static void show_backtrace(void) \{")
808 if platform.supports_libunwind then
809 v.add_decl("#ifndef NO_STACKTRACE")
810 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
811 v.add_decl("unw_cursor_t cursor;")
812 v.add_decl("if(opt==NULL)\{")
813 v.add_decl("unw_context_t uc;")
814 v.add_decl("unw_word_t ip;")
815 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
816 v.add_decl("unw_getcontext(&uc);")
817 v.add_decl("unw_init_local(&cursor, &uc);")
818 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
819 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
820 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
821 v.add_decl("while (unw_step(&cursor) > 0) \{")
822 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
823 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
824 v.add_decl(" if (recv != NULL)\{")
825 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
826 v.add_decl(" \}else\{")
827 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
828 v.add_decl(" \}")
829 v.add_decl("\}")
830 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
831 v.add_decl("free(procname);")
832 v.add_decl("\}")
833 v.add_decl("#endif /* NO_STACKTRACE */")
834 end
835 v.add_decl("\}")
836
837 v.add_decl("void sig_handler(int signo)\{")
838 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
839 v.add_decl("show_backtrace();")
840 # rethrows
841 v.add_decl("signal(signo, SIG_DFL);")
842 v.add_decl("kill(getpid(), signo);")
843 v.add_decl("\}")
844
845 v.add_decl("void fatal_exit(int status) \{")
846 v.add_decl("show_backtrace();")
847 v.add_decl("exit(status);")
848 v.add_decl("\}")
849
850 if no_main then
851 v.add_decl("int nit_main(int argc, char** argv) \{")
852 else
853 v.add_decl("int main(int argc, char** argv) \{")
854 end
855
856 v.add "#ifndef ANDROID"
857 v.add("signal(SIGABRT, sig_handler);")
858 v.add("signal(SIGFPE, sig_handler);")
859 v.add("signal(SIGILL, sig_handler);")
860 v.add("signal(SIGINT, sig_handler);")
861 v.add("signal(SIGTERM, sig_handler);")
862 v.add("signal(SIGSEGV, sig_handler);")
863 v.add "#endif"
864 v.add("signal(SIGPIPE, SIG_IGN);")
865
866 v.add("glob_argc = argc; glob_argv = argv;")
867 v.add("initialize_gc_option();")
868
869 v.add "initialize_nitni_global_refs();"
870
871 var main_type = mainmodule.sys_type
872 if main_type != null then
873 var mainmodule = v.compiler.mainmodule
874 var glob_sys = v.init_instance(main_type)
875 v.add("glob_sys = {glob_sys};")
876 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
877 if main_init != null then
878 v.send(main_init, [glob_sys])
879 end
880 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
881 mainmodule.try_get_primitive_method("main", main_type.mclass)
882 if main_method != null then
883 v.send(main_method, [glob_sys])
884 end
885 end
886
887 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
888 v.add_decl("long count_type_test_resolved_total = 0;")
889 v.add_decl("long count_type_test_unresolved_total = 0;")
890 v.add_decl("long count_type_test_skipped_total = 0;")
891 v.add_decl("long count_type_test_total_total = 0;")
892 for tag in count_type_test_tags do
893 v.add_decl("long count_type_test_total_{tag};")
894 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
895 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
896 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
897 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
898 v.add("count_type_test_total_total += count_type_test_total_{tag};")
899 end
900 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
901 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
902 var tags = count_type_test_tags.to_a
903 tags.add("total")
904 for tag in tags do
905 v.add("printf(\"{tag}\");")
906 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
907 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
908 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
909 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
910 end
911 end
912
913 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
914 v.add_decl("long count_invoke_total;")
915 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
916 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
917 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
918 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
919 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
920 end
921
922 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
923 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
924 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
925 end
926
927 v.add("return 0;")
928 v.add("\}")
929
930 for m in mainmodule.in_importation.greaters do
931 var f = "FILE_"+m.c_name
932 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
933 provide_declaration(f, "extern const char {f}[];")
934 end
935 end
936
937 # Copile all C functions related to the [incr|decr]_ref features of the FFI
938 fun compile_nitni_global_ref_functions
939 do
940 var v = self.new_visitor
941 v.add """
942 struct nitni_global_ref_list_t *nitni_global_ref_list;
943 void initialize_nitni_global_refs() {
944 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
945 nitni_global_ref_list->head = NULL;
946 nitni_global_ref_list->tail = NULL;
947 }
948
949 void nitni_global_ref_add( struct nitni_ref *ref ) {
950 if ( nitni_global_ref_list->head == NULL ) {
951 nitni_global_ref_list->head = ref;
952 ref->prev = NULL;
953 } else {
954 nitni_global_ref_list->tail->next = ref;
955 ref->prev = nitni_global_ref_list->tail;
956 }
957 nitni_global_ref_list->tail = ref;
958
959 ref->next = NULL;
960 }
961
962 void nitni_global_ref_remove( struct nitni_ref *ref ) {
963 if ( ref->prev == NULL ) {
964 nitni_global_ref_list->head = ref->next;
965 } else {
966 ref->prev->next = ref->next;
967 }
968
969 if ( ref->next == NULL ) {
970 nitni_global_ref_list->tail = ref->prev;
971 } else {
972 ref->next->prev = ref->prev;
973 }
974 }
975
976 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
977 if ( ref->count == 0 ) /* not registered */
978 {
979 /* add to list */
980 nitni_global_ref_add( ref );
981 }
982
983 ref->count ++;
984 }
985
986 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
987 if ( ref->count == 1 ) /* was last reference */
988 {
989 /* remove from list */
990 nitni_global_ref_remove( ref );
991 }
992
993 ref->count --;
994 }
995 """
996 end
997
998 # List of additional files required to compile (FFI)
999 var extern_bodies = new Array[ExternFile]
1000
1001 # List of source files to copy over to the compile dir
1002 var files_to_copy = new Array[String]
1003
1004 # This is used to avoid adding an extern file more than once
1005 private var seen_extern = new ArraySet[String]
1006
1007 # Generate code that initialize the attributes on a new instance
1008 fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1009 do
1010 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1011 self.mainmodule.linearize_mclassdefs(cds)
1012 for cd in cds do
1013 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1014 npropdef.init_expr(v, recv)
1015 end
1016 end
1017 end
1018
1019 # Generate code that check if an attribute is correctly initialized
1020 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1021 do
1022 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1023 self.mainmodule.linearize_mclassdefs(cds)
1024 for cd in cds do
1025 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1026 npropdef.check_expr(v, recv)
1027 end
1028 end
1029 end
1030
1031 # stats
1032
1033 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1034 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1035 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1036 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1037
1038 protected fun init_count_type_test_tags: HashMap[String, Int]
1039 do
1040 var res = new HashMap[String, Int]
1041 for tag in count_type_test_tags do
1042 res[tag] = 0
1043 end
1044 return res
1045 end
1046
1047 # Display stats about compilation process
1048 #
1049 # Metrics used:
1050 #
1051 # * type tests against resolved types (`x isa Collection[Animal]`)
1052 # * type tests against unresolved types (`x isa Collection[E]`)
1053 # * type tests skipped
1054 # * type tests total
1055 fun display_stats
1056 do
1057 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1058 print "# static count_type_test"
1059 print "\tresolved:\tunresolved\tskipped\ttotal"
1060 var count_type_test_total = init_count_type_test_tags
1061 count_type_test_resolved["total"] = 0
1062 count_type_test_unresolved["total"] = 0
1063 count_type_test_skipped["total"] = 0
1064 count_type_test_total["total"] = 0
1065 for tag in count_type_test_tags do
1066 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1067 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1068 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1069 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1070 count_type_test_total["total"] += count_type_test_total[tag]
1071 end
1072 var count_type_test = count_type_test_total["total"]
1073 var tags = count_type_test_tags.to_a
1074 tags.add("total")
1075 for tag in tags do
1076 printn tag
1077 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1078 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1079 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1080 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1081 print ""
1082 end
1083 end
1084 end
1085
1086 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1087 end
1088
1089 # A file unit (may be more than one file if
1090 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1091 class CodeFile
1092 var name: String
1093 var writers = new Array[CodeWriter]
1094 var required_declarations = new HashSet[String]
1095 end
1096
1097 # Where to store generated lines
1098 class CodeWriter
1099 var file: CodeFile
1100 var lines: List[String] = new List[String]
1101 var decl_lines: List[String] = new List[String]
1102
1103 # Add a line in the main part of the generated C
1104 fun add(s: String) do self.lines.add(s)
1105
1106 # Add a line in the
1107 # (used for local or global declaration)
1108 fun add_decl(s: String) do self.decl_lines.add(s)
1109
1110 init
1111 do
1112 file.writers.add(self)
1113 end
1114 end
1115
1116 # A visitor on the AST of property definition that generate the C code.
1117 abstract class AbstractCompilerVisitor
1118
1119 type COMPILER: AbstractCompiler
1120
1121 # The associated compiler
1122 var compiler: COMPILER
1123
1124 # The current visited AST node
1125 var current_node: nullable ANode = null is writable
1126
1127 # The current `StaticFrame`
1128 var frame: nullable StaticFrame = null is writable
1129
1130 # Alias for self.compiler.mainmodule.object_type
1131 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1132
1133 # Alias for self.compiler.mainmodule.bool_type
1134 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1135
1136 var writer: CodeWriter is noinit
1137
1138 init
1139 do
1140 self.writer = new CodeWriter(compiler.files.last)
1141 end
1142
1143 # Force to get the primitive property named `name` in the instance `recv` or abort
1144 fun get_property(name: String, recv: MType): MMethod
1145 do
1146 assert recv isa MClassType
1147 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1148 end
1149
1150 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1151 do
1152 if callsite.is_broken then return null
1153 var initializers = callsite.mpropdef.initializers
1154 if not initializers.is_empty then
1155 var recv = arguments.first
1156
1157 var i = 1
1158 for p in initializers do
1159 if p isa MMethod then
1160 var args = [recv]
1161 for x in p.intro.msignature.mparameters do
1162 args.add arguments[i]
1163 i += 1
1164 end
1165 self.send(p, args)
1166 else if p isa MAttribute then
1167 self.write_attribute(p, recv, arguments[i])
1168 i += 1
1169 else abort
1170 end
1171 assert i == arguments.length
1172
1173 return self.send(callsite.mproperty, [recv])
1174 end
1175
1176 return self.send(callsite.mproperty, arguments)
1177 end
1178
1179 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1180
1181 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1182
1183 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
1184
1185 # Return an element of a native array.
1186 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1187 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1188
1189 # Store an element in a native array.
1190 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1191 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1192
1193 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1194 # This method is used to manage varargs in signatures and returns the real array
1195 # of runtime variables to use in the call.
1196 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1197 do
1198 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
1199 var res = new Array[RuntimeVariable]
1200 res.add(recv)
1201
1202 if msignature.arity == 0 then return res
1203
1204 if map == null then
1205 assert args.length == msignature.arity
1206 for ne in args do
1207 res.add self.expr(ne, null)
1208 end
1209 return res
1210 end
1211
1212 # Eval in order of arguments, not parameters
1213 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1214 for ne in args do
1215 exprs.add self.expr(ne, null)
1216 end
1217
1218 # Fill `res` with the result of the evaluation according to the mapping
1219 for i in [0..msignature.arity[ do
1220 var param = msignature.mparameters[i]
1221 var j = map.map.get_or_null(i)
1222 if j == null then
1223 # default value
1224 res.add(null_instance)
1225 continue
1226 end
1227 if param.is_vararg and args[i].vararg_decl > 0 then
1228 var vararg = exprs.sub(j, args[i].vararg_decl)
1229 var elttype = param.mtype
1230 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1231 res.add(arg)
1232 continue
1233 end
1234 res.add exprs[j]
1235 end
1236 return res
1237 end
1238
1239 # Type handling
1240
1241 # Anchor a type to the main module and the current receiver
1242 fun anchor(mtype: MType): MType
1243 do
1244 if not mtype.need_anchor then return mtype
1245 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1246 end
1247
1248 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1249 do
1250 if not mtype.need_anchor then return mtype
1251 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1252 end
1253
1254 # Unsafely cast a value to a new type
1255 # ie the result share the same C variable but my have a different mcasttype
1256 # NOTE: if the adaptation is useless then `value` is returned as it.
1257 # ENSURE: `result.name == value.name`
1258 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1259 do
1260 mtype = self.anchor(mtype)
1261 var valmtype = value.mcasttype
1262 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1263 return value
1264 end
1265
1266 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1267 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1268 return res
1269 else
1270 var res = new RuntimeVariable(value.name, valmtype, mtype)
1271 return res
1272 end
1273 end
1274
1275 # Generate a super call from a method definition
1276 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1277
1278 # Adapt the arguments of a method according to targetted `MMethodDef`
1279 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1280
1281 # Unbox all the arguments of a method when implemented `extern` or `intern`
1282 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1283
1284 # Box or unbox a value to another type iff a C type conversion is needed
1285 # ENSURE: `result.mtype.ctype == mtype.ctype`
1286 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1287
1288 # Box extern classes to be used in the generated code
1289 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1290
1291 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1292 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1293
1294 # Generate a polymorphic subtype test
1295 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1296
1297 # Generate the code required to dynamically check if 2 objects share the same runtime type
1298 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1299
1300 # Generate a Nit "is" for two runtime_variables
1301 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1302
1303 # Sends
1304
1305 # Generate a static call on a method definition
1306 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1307
1308 # Generate a polymorphic send for the method `m` and the arguments `args`
1309 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1310
1311 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1312 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1313 do
1314 assert t isa MClassType
1315 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1316 return self.call(propdef, t, args)
1317 end
1318
1319 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1320 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1321 do
1322 assert t isa MClassType
1323 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1324 return self.call(m, t, args)
1325 end
1326
1327 # Attributes handling
1328
1329 # Generate a polymorphic attribute is_set test
1330 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1331
1332 # Generate a polymorphic attribute read
1333 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1334
1335 # Generate a polymorphic attribute write
1336 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1337
1338 # Checks
1339
1340 # Add a check and an abort for a null receiver if needed
1341 fun check_recv_notnull(recv: RuntimeVariable)
1342 do
1343 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1344
1345 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1346 if maybenull then
1347 self.add("if (unlikely({recv} == NULL)) \{")
1348 self.add_abort("Receiver is null")
1349 self.add("\}")
1350 end
1351 end
1352
1353 # Names handling
1354
1355 private var names = new HashSet[String]
1356 private var last: Int = 0
1357
1358 # Return a new name based on `s` and unique in the visitor
1359 fun get_name(s: String): String
1360 do
1361 if not self.names.has(s) then
1362 self.names.add(s)
1363 return s
1364 end
1365 var i = self.last + 1
1366 loop
1367 var s2 = s + i.to_s
1368 if not self.names.has(s2) then
1369 self.last = i
1370 self.names.add(s2)
1371 return s2
1372 end
1373 i = i + 1
1374 end
1375 end
1376
1377 # Return an unique and stable identifier associated with an escapemark
1378 fun escapemark_name(e: nullable EscapeMark): String
1379 do
1380 assert e != null
1381 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1382 var name = e.name
1383 if name == null then name = "label"
1384 name = get_name(name)
1385 frame.escapemark_names[e] = name
1386 return name
1387 end
1388
1389 # Insert a C label for associated with an escapemark
1390 fun add_escape_label(e: nullable EscapeMark)
1391 do
1392 if e == null then return
1393 if e.escapes.is_empty then return
1394 add("BREAK_{escapemark_name(e)}: (void)0;")
1395 end
1396
1397 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1398 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1399 fun class_name_string(value: RuntimeVariable): String is abstract
1400
1401 # Variables handling
1402
1403 protected var variables = new HashMap[Variable, RuntimeVariable]
1404
1405 # Return the local runtime_variable associated to a Nit local variable
1406 fun variable(variable: Variable): RuntimeVariable
1407 do
1408 if self.variables.has_key(variable) then
1409 return self.variables[variable]
1410 else
1411 var name = self.get_name("var_{variable.name}")
1412 var mtype = variable.declared_type.as(not null)
1413 mtype = self.anchor(mtype)
1414 var res = new RuntimeVariable(name, mtype, mtype)
1415 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1416 self.variables[variable] = res
1417 return res
1418 end
1419 end
1420
1421 # Return a new uninitialized local runtime_variable
1422 fun new_var(mtype: MType): RuntimeVariable
1423 do
1424 mtype = self.anchor(mtype)
1425 var name = self.get_name("var")
1426 var res = new RuntimeVariable(name, mtype, mtype)
1427 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1428 return res
1429 end
1430
1431 # The difference with `new_var` is the C static type of the local variable
1432 fun new_var_extern(mtype: MType): RuntimeVariable
1433 do
1434 mtype = self.anchor(mtype)
1435 var name = self.get_name("var")
1436 var res = new RuntimeVariable(name, mtype, mtype)
1437 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1438 return res
1439 end
1440
1441 # Return a new uninitialized named runtime_variable
1442 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1443 do
1444 mtype = self.anchor(mtype)
1445 var res = new RuntimeVariable(name, mtype, mtype)
1446 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1447 return res
1448 end
1449
1450 # Correctly assign a left and a right value
1451 # Boxing and unboxing is performed if required
1452 fun assign(left, right: RuntimeVariable)
1453 do
1454 right = self.autobox(right, left.mtype)
1455 self.add("{left} = {right};")
1456 end
1457
1458 # Generate instances
1459
1460 # Generate a alloc-instance + init-attributes
1461 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1462
1463 # Allocate and init attributes of an instance of a standard or extern class
1464 #
1465 # Does not support universals and the pseudo-internal `NativeArray` class.
1466 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1467 do
1468 var recv
1469 var ctype = mtype.ctype
1470 assert mtype.mclass.name != "NativeArray"
1471 if not mtype.is_c_primitive then
1472 recv = init_instance(mtype)
1473 else if ctype == "char*" then
1474 recv = new_expr("NULL/*special!*/", mtype)
1475 else
1476 recv = new_expr("({ctype})0/*special!*/", mtype)
1477 end
1478 return recv
1479 end
1480
1481 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1482 fun set_finalizer(recv: RuntimeVariable)
1483 do
1484 var mtype = recv.mtype
1485 var finalizable_type = compiler.mainmodule.finalizable_type
1486 if finalizable_type != null and not mtype.need_anchor and
1487 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1488 add "gc_register_finalizer({recv});"
1489 end
1490 end
1491
1492 # The currently processed module
1493 #
1494 # alias for `compiler.mainmodule`
1495 fun mmodule: MModule do return compiler.mainmodule
1496
1497 # Generate an integer value
1498 fun int_instance(value: Int): RuntimeVariable
1499 do
1500 var t = mmodule.int_type
1501 var res = new RuntimeVariable("{value.to_s}l", t, t)
1502 return res
1503 end
1504
1505 # Generate a byte value
1506 fun byte_instance(value: Byte): RuntimeVariable
1507 do
1508 var t = mmodule.byte_type
1509 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1510 return res
1511 end
1512
1513 # Generate an int8 value
1514 fun int8_instance(value: Int8): RuntimeVariable
1515 do
1516 var t = mmodule.int8_type
1517 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1518 return res
1519 end
1520
1521 # Generate an int16 value
1522 fun int16_instance(value: Int16): RuntimeVariable
1523 do
1524 var t = mmodule.int16_type
1525 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1526 return res
1527 end
1528
1529 # Generate a uint16 value
1530 fun uint16_instance(value: UInt16): RuntimeVariable
1531 do
1532 var t = mmodule.uint16_type
1533 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1534 return res
1535 end
1536
1537 # Generate an int32 value
1538 fun int32_instance(value: Int32): RuntimeVariable
1539 do
1540 var t = mmodule.int32_type
1541 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1542 return res
1543 end
1544
1545 # Generate a uint32 value
1546 fun uint32_instance(value: UInt32): RuntimeVariable
1547 do
1548 var t = mmodule.uint32_type
1549 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1550 return res
1551 end
1552
1553 # Generate a char value
1554 fun char_instance(value: Char): RuntimeVariable
1555 do
1556 var t = mmodule.char_type
1557
1558 if value.code_point < 128 then
1559 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1560 else
1561 return new RuntimeVariable("{value.code_point}", t, t)
1562 end
1563 end
1564
1565 # Generate a float value
1566 #
1567 # FIXME pass a Float, not a string
1568 fun float_instance(value: String): RuntimeVariable
1569 do
1570 var t = mmodule.float_type
1571 var res = new RuntimeVariable("{value}", t, t)
1572 return res
1573 end
1574
1575 # Generate an integer value
1576 fun bool_instance(value: Bool): RuntimeVariable
1577 do
1578 var s = if value then "1" else "0"
1579 var res = new RuntimeVariable(s, bool_type, bool_type)
1580 return res
1581 end
1582
1583 # Generate the `null` value
1584 fun null_instance: RuntimeVariable
1585 do
1586 var t = compiler.mainmodule.model.null_type
1587 var res = new RuntimeVariable("((val*)NULL)", t, t)
1588 return res
1589 end
1590
1591 # Generate a string value
1592 fun string_instance(string: String): RuntimeVariable
1593 do
1594 var mtype = mmodule.string_type
1595 var name = self.get_name("varonce")
1596 self.add_decl("static {mtype.ctype} {name};")
1597 var res = self.new_var(mtype)
1598 self.add("if (likely({name}!=NULL)) \{")
1599 self.add("{res} = {name};")
1600 self.add("\} else \{")
1601 var native_mtype = mmodule.native_string_type
1602 var nat = self.new_var(native_mtype)
1603 self.add("{nat} = \"{string.escape_to_c}\";")
1604 var bytelen = self.int_instance(string.bytelen)
1605 var unilen = self.int_instance(string.length)
1606 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1607 self.add("{name} = {res};")
1608 self.add("\}")
1609 return res
1610 end
1611
1612 fun value_instance(object: Object): RuntimeVariable
1613 do
1614 if object isa Int then
1615 return int_instance(object)
1616 else if object isa Bool then
1617 return bool_instance(object)
1618 else if object isa String then
1619 return string_instance(object)
1620 else
1621 abort
1622 end
1623 end
1624
1625 # Generate an array value
1626 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1627
1628 # Get an instance of a array for a vararg
1629 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1630
1631 # Code generation
1632
1633 # Add a line in the main part of the generated C
1634 fun add(s: String) do self.writer.lines.add(s)
1635
1636 # Add a line in the
1637 # (used for local or global declaration)
1638 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1639
1640 # Request the presence of a global declaration
1641 fun require_declaration(key: String)
1642 do
1643 var reqs = self.writer.file.required_declarations
1644 if reqs.has(key) then return
1645 reqs.add(key)
1646 var node = current_node
1647 if node != null then compiler.requirers_of_declarations[key] = node
1648 end
1649
1650 # Add a declaration in the local-header
1651 # The declaration is ensured to be present once
1652 fun declare_once(s: String)
1653 do
1654 self.compiler.provide_declaration(s, s)
1655 self.require_declaration(s)
1656 end
1657
1658 # Look for a needed .h and .c file for a given module
1659 # This is used for the legacy FFI
1660 fun add_extern(mmodule: MModule)
1661 do
1662 var file = mmodule.location.file.filename
1663 file = file.strip_extension(".nit")
1664 var tryfile = file + ".nit.h"
1665 if tryfile.file_exists then
1666 self.declare_once("#include \"{tryfile.basename}\"")
1667 self.compiler.files_to_copy.add(tryfile)
1668 end
1669 tryfile = file + "_nit.h"
1670 if tryfile.file_exists then
1671 self.declare_once("#include \"{tryfile.basename}\"")
1672 self.compiler.files_to_copy.add(tryfile)
1673 end
1674
1675 if self.compiler.seen_extern.has(file) then return
1676 self.compiler.seen_extern.add(file)
1677 tryfile = file + ".nit.c"
1678 if not tryfile.file_exists then
1679 tryfile = file + "_nit.c"
1680 if not tryfile.file_exists then return
1681 end
1682 var f = new ExternCFile(tryfile.basename, "")
1683 self.compiler.extern_bodies.add(f)
1684 self.compiler.files_to_copy.add(tryfile)
1685 end
1686
1687 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1688 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1689 do
1690 var res = new_var(mtype)
1691 self.add("{res} = {cexpr};")
1692 return res
1693 end
1694
1695 # Generate generic abort
1696 # used by aborts, asserts, casts, etc.
1697 fun add_abort(message: String)
1698 do
1699 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1700 add_raw_abort
1701 end
1702
1703 fun add_raw_abort
1704 do
1705 if self.current_node != null and self.current_node.location.file != null and
1706 self.current_node.location.file.mmodule != null then
1707 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1708 self.require_declaration(f)
1709 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1710 else
1711 self.add("PRINT_ERROR(\"\\n\");")
1712 end
1713 self.add("fatal_exit(1);")
1714 end
1715
1716 # Add a dynamic cast
1717 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1718 do
1719 var res = self.type_test(value, mtype, tag)
1720 self.add("if (unlikely(!{res})) \{")
1721 var cn = self.class_name_string(value)
1722 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1723 self.add_raw_abort
1724 self.add("\}")
1725 end
1726
1727 # Generate a return with the value `s`
1728 fun ret(s: RuntimeVariable)
1729 do
1730 self.assign(self.frame.returnvar.as(not null), s)
1731 self.add("goto {self.frame.returnlabel.as(not null)};")
1732 end
1733
1734 # Compile a statement (if any)
1735 fun stmt(nexpr: nullable AExpr)
1736 do
1737 if nexpr == null then return
1738 if nexpr.is_broken then
1739 # Untyped expression.
1740 # Might mean dead code or invalid code
1741 # so aborts
1742 add_abort("FATAL: bad statement executed.")
1743 return
1744 end
1745
1746 var narray = nexpr.comprehension
1747 if narray != null then
1748 var recv = frame.comprehension.as(not null)
1749 var val = expr(nexpr, narray.element_mtype)
1750 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1751 return
1752 end
1753
1754 var old = self.current_node
1755 self.current_node = nexpr
1756 nexpr.stmt(self)
1757 self.current_node = old
1758 end
1759
1760 # Compile an expression an return its result
1761 # `mtype` is the expected return type, pass null if no specific type is expected.
1762 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1763 do
1764 var old = self.current_node
1765 self.current_node = nexpr
1766
1767 var res = null
1768 if nexpr.mtype != null then
1769 res = nexpr.expr(self)
1770 end
1771
1772 if res == null then
1773 # Untyped expression.
1774 # Might mean dead code or invalid code.
1775 # so aborts
1776 add_abort("FATAL: bad expression executed.")
1777 # and return a placebo result to please the C compiler
1778 if mtype == null then mtype = compiler.mainmodule.object_type
1779 res = new_var(mtype)
1780
1781 self.current_node = old
1782 return res
1783 end
1784
1785 if mtype != null then
1786 mtype = self.anchor(mtype)
1787 res = self.autobox(res, mtype)
1788 end
1789 res = autoadapt(res, nexpr.mtype.as(not null))
1790 var implicit_cast_to = nexpr.implicit_cast_to
1791 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1792 add_cast(res, implicit_cast_to, "auto")
1793 res = autoadapt(res, implicit_cast_to)
1794 end
1795 self.current_node = old
1796 return res
1797 end
1798
1799 # Alias for `self.expr(nexpr, self.bool_type)`
1800 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1801
1802 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1803 fun debug(message: String)
1804 do
1805 var node = self.current_node
1806 if node == null then
1807 print "?: {message}"
1808 else
1809 node.debug(message)
1810 end
1811 self.add("/* DEBUG: {message} */")
1812 end
1813 end
1814
1815 # A C function associated to a Nit method
1816 # Because of customization, a given Nit method can be compiler more that once
1817 abstract class AbstractRuntimeFunction
1818
1819 type COMPILER: AbstractCompiler
1820 type VISITOR: AbstractCompilerVisitor
1821
1822 # The associated Nit method
1823 var mmethoddef: MMethodDef
1824
1825 # The mangled c name of the runtime_function
1826 # Subclasses should redefine `build_c_name` instead
1827 fun c_name: String
1828 do
1829 var res = self.c_name_cache
1830 if res != null then return res
1831 res = self.build_c_name
1832 self.c_name_cache = res
1833 return res
1834 end
1835
1836 # Non cached version of `c_name`
1837 protected fun build_c_name: String is abstract
1838
1839 protected var c_name_cache: nullable String = null is writable
1840
1841 # Implements a call of the runtime_function
1842 # May inline the body or generate a C function call
1843 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1844
1845 # Generate the code for the `AbstractRuntimeFunction`
1846 # Warning: compile more than once compilation makes CC unhappy
1847 fun compile_to_c(compiler: COMPILER) is abstract
1848 end
1849
1850 # A runtime variable hold a runtime value in C.
1851 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1852 #
1853 # 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.
1854 class RuntimeVariable
1855 # The name of the variable in the C code
1856 var name: String
1857
1858 # The static type of the variable (as declard in C)
1859 var mtype: MType
1860
1861 # The current casted type of the variable (as known in Nit)
1862 var mcasttype: MType is writable
1863
1864 # If the variable exaclty a mcasttype?
1865 # false (usual value) means that the variable is a mcasttype or a subtype.
1866 var is_exact: Bool = false is writable
1867
1868 init
1869 do
1870 assert not mtype.need_anchor
1871 assert not mcasttype.need_anchor
1872 end
1873
1874 redef fun to_s do return name
1875
1876 redef fun inspect
1877 do
1878 var exact_str
1879 if self.is_exact then
1880 exact_str = " exact"
1881 else
1882 exact_str = ""
1883 end
1884 var type_str
1885 if self.mtype == self.mcasttype then
1886 type_str = "{mtype}{exact_str}"
1887 else
1888 type_str = "{mtype}({mcasttype}{exact_str})"
1889 end
1890 return "<{name}:{type_str}>"
1891 end
1892 end
1893
1894 # The static context of a visited property in a `AbstractCompilerVisitor`
1895 class StaticFrame
1896
1897 type VISITOR: AbstractCompilerVisitor
1898
1899 # The associated visitor
1900 var visitor: VISITOR
1901
1902 # The executed property.
1903 # A Method in case of a call, an attribute in case of a default initialization.
1904 var mpropdef: MPropDef
1905
1906 # The static type of the receiver
1907 var receiver: MClassType
1908
1909 # Arguments of the method (the first is the receiver)
1910 var arguments: Array[RuntimeVariable]
1911
1912 # The runtime_variable associated to the return (in a function)
1913 var returnvar: nullable RuntimeVariable = null is writable
1914
1915 # The label at the end of the property
1916 var returnlabel: nullable String = null is writable
1917
1918 # Labels associated to a each escapemarks.
1919 # Because of inlinings, escape-marks must be associated to their context (the frame)
1920 private var escapemark_names = new HashMap[EscapeMark, String]
1921
1922 # The array comprehension currently filled, if any
1923 private var comprehension: nullable RuntimeVariable = null
1924 end
1925
1926 redef class MType
1927 # Return the C type associated to a given Nit static type
1928 fun ctype: String do return "val*"
1929
1930 # C type outside of the compiler code and in boxes
1931 fun ctype_extern: String do return "val*"
1932
1933 # Short name of the `ctype` to use in unions
1934 fun ctypename: String do return "val"
1935
1936 # Is the associated C type a primitive one?
1937 #
1938 # ENSURE `result == (ctype != "val*")`
1939 fun is_c_primitive: Bool do return false
1940 end
1941
1942 redef class MClassType
1943
1944 redef var ctype is lazy do
1945 if mclass.name == "Int" then
1946 return "long"
1947 else if mclass.name == "Bool" then
1948 return "short int"
1949 else if mclass.name == "Char" then
1950 return "uint32_t"
1951 else if mclass.name == "Float" then
1952 return "double"
1953 else if mclass.name == "Int8" then
1954 return "int8_t"
1955 else if mclass.name == "Byte" then
1956 return "unsigned char"
1957 else if mclass.name == "Int16" then
1958 return "int16_t"
1959 else if mclass.name == "UInt16" then
1960 return "uint16_t"
1961 else if mclass.name == "Int32" then
1962 return "int32_t"
1963 else if mclass.name == "UInt32" then
1964 return "uint32_t"
1965 else if mclass.name == "NativeString" then
1966 return "char*"
1967 else if mclass.name == "NativeArray" then
1968 return "val*"
1969 else
1970 return "val*"
1971 end
1972 end
1973
1974 redef var is_c_primitive is lazy do return ctype != "val*"
1975
1976 redef fun ctype_extern: String
1977 do
1978 if mclass.kind == extern_kind then
1979 return "void*"
1980 else
1981 return ctype
1982 end
1983 end
1984
1985 redef fun ctypename: String
1986 do
1987 if mclass.name == "Int" then
1988 return "l"
1989 else if mclass.name == "Bool" then
1990 return "s"
1991 else if mclass.name == "Char" then
1992 return "c"
1993 else if mclass.name == "Float" then
1994 return "d"
1995 else if mclass.name == "Int8" then
1996 return "i8"
1997 else if mclass.name == "Byte" then
1998 return "b"
1999 else if mclass.name == "Int16" then
2000 return "i16"
2001 else if mclass.name == "UInt16" then
2002 return "u16"
2003 else if mclass.name == "Int32" then
2004 return "i32"
2005 else if mclass.name == "UInt32" then
2006 return "u32"
2007 else if mclass.name == "NativeString" then
2008 return "str"
2009 else if mclass.name == "NativeArray" then
2010 #return "{self.arguments.first.ctype}*"
2011 return "val"
2012 else
2013 return "val"
2014 end
2015 end
2016 end
2017
2018 redef class MPropDef
2019 type VISITOR: AbstractCompilerVisitor
2020 end
2021
2022 redef class MMethodDef
2023 # Can the body be inlined?
2024 fun can_inline(v: VISITOR): Bool
2025 do
2026 if is_abstract then return true
2027 if constant_value != null then return true
2028 var modelbuilder = v.compiler.modelbuilder
2029 var node = modelbuilder.mpropdef2node(self)
2030 if node isa APropdef then
2031 return node.can_inline
2032 else if node isa AClassdef then
2033 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2034 return true
2035 else
2036 abort
2037 end
2038 end
2039
2040 # Inline the body in another visitor
2041 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2042 do
2043 var modelbuilder = v.compiler.modelbuilder
2044 var val = constant_value
2045 var node = modelbuilder.mpropdef2node(self)
2046
2047 if is_abstract then
2048 var cn = v.class_name_string(arguments.first)
2049 v.current_node = node
2050 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2051 v.add_raw_abort
2052 return null
2053 end
2054
2055 if node isa APropdef then
2056 var oldnode = v.current_node
2057 v.current_node = node
2058 self.compile_parameter_check(v, arguments)
2059 node.compile_to_c(v, self, arguments)
2060 v.current_node = oldnode
2061 else if node isa AClassdef then
2062 var oldnode = v.current_node
2063 v.current_node = node
2064 self.compile_parameter_check(v, arguments)
2065 node.compile_to_c(v, self, arguments)
2066 v.current_node = oldnode
2067 else if val != null then
2068 v.ret(v.value_instance(val))
2069 else
2070 abort
2071 end
2072 return null
2073 end
2074
2075 # Generate type checks in the C code to check covariant parameters
2076 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2077 do
2078 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2079
2080 var msignature = self.msignature.as(not null)
2081
2082 for i in [0..msignature.arity[ do
2083 # skip test for vararg since the array is instantiated with the correct polymorphic type
2084 if msignature.vararg_rank == i then continue
2085
2086 # skip if the cast is not required
2087 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2088 if not origmtype.need_anchor then continue
2089
2090 # get the parameter type
2091 var mtype = msignature.mparameters[i].mtype
2092
2093 # generate the cast
2094 # note that v decides if and how to implements the cast
2095 v.add("/* Covariant cast for argument {i} ({msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
2096 v.add_cast(arguments[i+1], mtype, "covariance")
2097 end
2098 end
2099 end
2100
2101 # Node visit
2102
2103 redef class APropdef
2104 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2105 do
2106 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2107 debug("Not yet implemented")
2108 end
2109
2110 fun can_inline: Bool do return true
2111 end
2112
2113 redef class AMethPropdef
2114 redef fun compile_to_c(v, mpropdef, arguments)
2115 do
2116 # Call the implicit super-init
2117 var auto_super_inits = self.auto_super_inits
2118 if auto_super_inits != null then
2119 var args = [arguments.first]
2120 for auto_super_init in auto_super_inits do
2121 assert auto_super_init.mproperty != mpropdef.mproperty
2122 args.clear
2123 for i in [0..auto_super_init.msignature.arity+1[ do
2124 args.add(arguments[i])
2125 end
2126 assert auto_super_init.mproperty != mpropdef.mproperty
2127 v.compile_callsite(auto_super_init, args)
2128 end
2129 end
2130 if auto_super_call then
2131 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2132 end
2133
2134 # Try special compilation
2135 if mpropdef.is_intern then
2136 if compile_intern_to_c(v, mpropdef, arguments) then return
2137 else if mpropdef.is_extern then
2138 if mpropdef.mproperty.is_init then
2139 if compile_externinit_to_c(v, mpropdef, arguments) then return
2140 else
2141 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2142 end
2143 end
2144
2145 # Compile block if any
2146 var n_block = n_block
2147 if n_block != null then
2148 for i in [0..mpropdef.msignature.arity[ do
2149 var variable = self.n_signature.n_params[i].variable.as(not null)
2150 v.assign(v.variable(variable), arguments[i+1])
2151 end
2152 v.stmt(n_block)
2153 return
2154 end
2155
2156 # We have a problem
2157 var cn = v.class_name_string(arguments.first)
2158 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2159 v.add_raw_abort
2160 end
2161
2162 redef fun can_inline
2163 do
2164 if self.auto_super_inits != null then return false
2165 var nblock = self.n_block
2166 if nblock == null then return true
2167 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2168 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2169 return false
2170 end
2171
2172 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2173 do
2174 var pname = mpropdef.mproperty.name
2175 var cname = mpropdef.mclassdef.mclass.name
2176 var ret = mpropdef.msignature.return_mtype
2177 if ret != null then
2178 ret = v.resolve_for(ret, arguments.first)
2179 end
2180 if pname != "==" and pname != "!=" then
2181 v.adapt_signature(mpropdef, arguments)
2182 v.unbox_signature_extern(mpropdef, arguments)
2183 end
2184 if cname == "Int" then
2185 if pname == "output" then
2186 v.add("printf(\"%ld\\n\", {arguments.first});")
2187 return true
2188 else if pname == "object_id" then
2189 v.ret(arguments.first)
2190 return true
2191 else if pname == "+" then
2192 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2193 return true
2194 else if pname == "-" then
2195 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2196 return true
2197 else if pname == "unary -" then
2198 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2199 return true
2200 else if pname == "unary +" then
2201 v.ret(arguments[0])
2202 return true
2203 else if pname == "*" then
2204 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2205 return true
2206 else if pname == "/" then
2207 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2208 return true
2209 else if pname == "%" then
2210 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2211 return true
2212 else if pname == "==" then
2213 v.ret(v.equal_test(arguments[0], arguments[1]))
2214 return true
2215 else if pname == "!=" then
2216 var res = v.equal_test(arguments[0], arguments[1])
2217 v.ret(v.new_expr("!{res}", ret.as(not null)))
2218 return true
2219 else if pname == "<" then
2220 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2221 return true
2222 else if pname == ">" then
2223 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2224 return true
2225 else if pname == "<=" then
2226 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2227 return true
2228 else if pname == ">=" then
2229 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2230 return true
2231 else if pname == "to_i8" then
2232 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2233 return true
2234 else if pname == "to_i16" then
2235 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2236 return true
2237 else if pname == "to_u16" then
2238 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2239 return true
2240 else if pname == "to_i32" then
2241 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2242 return true
2243 else if pname == "to_u32" then
2244 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2245 return true
2246 else if pname == "to_f" then
2247 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2248 return true
2249 else if pname == "to_b" then
2250 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2251 return true
2252 else if pname == "code_point" then
2253 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2254 return true
2255 else if pname == "&" then
2256 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2257 return true
2258 else if pname == "|" then
2259 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2260 return true
2261 else if pname == ">>" then
2262 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2263 return true
2264 else if pname == "<<" then
2265 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2266 return true
2267 end
2268 else if cname == "Char" then
2269 if pname == "object_id" then
2270 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2271 return true
2272 else if pname == "successor" then
2273 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2274 return true
2275 else if pname == "predecessor" then
2276 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2277 return true
2278 else if pname == "==" then
2279 v.ret(v.equal_test(arguments[0], arguments[1]))
2280 return true
2281 else if pname == "!=" then
2282 var res = v.equal_test(arguments[0], arguments[1])
2283 v.ret(v.new_expr("!{res}", ret.as(not null)))
2284 return true
2285 else if pname == "<" then
2286 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2287 return true
2288 else if pname == ">" then
2289 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2290 return true
2291 else if pname == "<=" then
2292 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2293 return true
2294 else if pname == ">=" then
2295 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2296 return true
2297 else if pname == "to_i" then
2298 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2299 return true
2300 else if pname == "code_point" then
2301 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2302 return true
2303 end
2304 else if cname == "Byte" then
2305 if pname == "output" then
2306 v.add("printf(\"%x\\n\", {arguments.first});")
2307 return true
2308 else if pname == "object_id" then
2309 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2310 return true
2311 else if pname == "+" then
2312 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2313 return true
2314 else if pname == "-" then
2315 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2316 return true
2317 else if pname == "unary -" then
2318 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2319 return true
2320 else if pname == "unary +" then
2321 v.ret(arguments[0])
2322 return true
2323 else if pname == "*" then
2324 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2325 return true
2326 else if pname == "/" then
2327 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2328 return true
2329 else if pname == "%" then
2330 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2331 return true
2332 else if pname == "==" then
2333 v.ret(v.equal_test(arguments[0], arguments[1]))
2334 return true
2335 else if pname == "!=" then
2336 var res = v.equal_test(arguments[0], arguments[1])
2337 v.ret(v.new_expr("!{res}", ret.as(not null)))
2338 return true
2339 else if pname == "<" then
2340 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2341 return true
2342 else if pname == ">" then
2343 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2344 return true
2345 else if pname == "<=" then
2346 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2347 return true
2348 else if pname == ">=" then
2349 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2350 return true
2351 else if pname == ">>" then
2352 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2353 return true
2354 else if pname == "<<" then
2355 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2356 return true
2357 else if pname == "&" then
2358 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2359 return true
2360 else if pname == "to_i" then
2361 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2362 return true
2363 else if pname == "to_f" then
2364 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2365 return true
2366 else if pname == "to_i8" then
2367 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2368 return true
2369 else if pname == "to_i16" then
2370 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2371 return true
2372 else if pname == "to_u16" then
2373 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2374 return true
2375 else if pname == "to_i32" then
2376 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2377 return true
2378 else if pname == "to_u32" then
2379 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2380 return true
2381 else if pname == "ascii" then
2382 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2383 return true
2384 end
2385 else if cname == "Bool" then
2386 if pname == "output" then
2387 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2388 return true
2389 else if pname == "object_id" then
2390 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2391 return true
2392 else if pname == "==" then
2393 v.ret(v.equal_test(arguments[0], arguments[1]))
2394 return true
2395 else if pname == "!=" then
2396 var res = v.equal_test(arguments[0], arguments[1])
2397 v.ret(v.new_expr("!{res}", ret.as(not null)))
2398 return true
2399 end
2400 else if cname == "Float" then
2401 if pname == "output" then
2402 v.add("printf(\"%f\\n\", {arguments.first});")
2403 return true
2404 else if pname == "object_id" then
2405 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2406 return true
2407 else if pname == "+" then
2408 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2409 return true
2410 else if pname == "-" then
2411 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2412 return true
2413 else if pname == "unary -" then
2414 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2415 return true
2416 else if pname == "unary +" then
2417 v.ret(arguments[0])
2418 return true
2419 else if pname == "succ" then
2420 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2421 return true
2422 else if pname == "prec" then
2423 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2424 return true
2425 else if pname == "*" then
2426 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2427 return true
2428 else if pname == "/" then
2429 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2430 return true
2431 else if pname == "==" then
2432 v.ret(v.equal_test(arguments[0], arguments[1]))
2433 return true
2434 else if pname == "!=" then
2435 var res = v.equal_test(arguments[0], arguments[1])
2436 v.ret(v.new_expr("!{res}", ret.as(not null)))
2437 return true
2438 else if pname == "<" then
2439 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2440 return true
2441 else if pname == ">" then
2442 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2443 return true
2444 else if pname == "<=" then
2445 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2446 return true
2447 else if pname == ">=" then
2448 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2449 return true
2450 else if pname == "to_i" then
2451 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2452 return true
2453 else if pname == "to_b" then
2454 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2455 return true
2456 else if pname == "to_i8" then
2457 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2458 return true
2459 else if pname == "to_i16" then
2460 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2461 return true
2462 else if pname == "to_u16" then
2463 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2464 return true
2465 else if pname == "to_i32" then
2466 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2467 return true
2468 else if pname == "to_u32" then
2469 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2470 return true
2471 end
2472 else if cname == "NativeString" then
2473 if pname == "[]" then
2474 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2475 return true
2476 else if pname == "[]=" then
2477 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2478 return true
2479 else if pname == "copy_to" then
2480 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2481 return true
2482 else if pname == "atoi" then
2483 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2484 return true
2485 else if pname == "fast_cstring" then
2486 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2487 return true
2488 else if pname == "new" then
2489 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2490 return true
2491 end
2492 else if cname == "NativeArray" then
2493 v.native_array_def(pname, ret, arguments)
2494 return true
2495 else if cname == "Int8" then
2496 if pname == "output" then
2497 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2498 return true
2499 else if pname == "object_id" then
2500 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2501 return true
2502 else if pname == "+" then
2503 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2504 return true
2505 else if pname == "-" then
2506 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2507 return true
2508 else if pname == "unary -" then
2509 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2510 return true
2511 else if pname == "unary +" then
2512 v.ret(arguments[0])
2513 return true
2514 else if pname == "*" then
2515 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2516 return true
2517 else if pname == "/" then
2518 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2519 return true
2520 else if pname == "%" then
2521 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2522 return true
2523 else if pname == "<<" then
2524 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2525 return true
2526 else if pname == ">>" then
2527 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2528 return true
2529 else if pname == "==" then
2530 v.ret(v.equal_test(arguments[0], arguments[1]))
2531 return true
2532 else if pname == "!=" then
2533 var res = v.equal_test(arguments[0], arguments[1])
2534 v.ret(v.new_expr("!{res}", ret.as(not null)))
2535 return true
2536 else if pname == "<" then
2537 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2538 return true
2539 else if pname == ">" then
2540 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2541 return true
2542 else if pname == "<=" then
2543 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2544 return true
2545 else if pname == ">=" then
2546 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2547 return true
2548 else if pname == "to_i" then
2549 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2550 return true
2551 else if pname == "to_b" then
2552 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2553 return true
2554 else if pname == "to_i16" then
2555 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2556 return true
2557 else if pname == "to_u16" then
2558 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2559 return true
2560 else if pname == "to_i32" then
2561 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2562 return true
2563 else if pname == "to_u32" then
2564 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2565 return true
2566 else if pname == "to_f" then
2567 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2568 return true
2569 else if pname == "&" then
2570 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2571 return true
2572 else if pname == "|" then
2573 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2574 return true
2575 else if pname == "^" then
2576 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2577 return true
2578 else if pname == "unary ~" then
2579 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2580 return true
2581 end
2582 else if cname == "Int16" then
2583 if pname == "output" then
2584 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2585 return true
2586 else if pname == "object_id" then
2587 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2588 return true
2589 else if pname == "+" then
2590 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2591 return true
2592 else if pname == "-" then
2593 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2594 return true
2595 else if pname == "unary -" then
2596 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2597 return true
2598 else if pname == "unary +" then
2599 v.ret(arguments[0])
2600 return true
2601 else if pname == "*" then
2602 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2603 return true
2604 else if pname == "/" then
2605 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2606 return true
2607 else if pname == "%" then
2608 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2609 return true
2610 else if pname == "<<" then
2611 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2612 return true
2613 else if pname == ">>" then
2614 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2615 return true
2616 else if pname == "==" then
2617 v.ret(v.equal_test(arguments[0], arguments[1]))
2618 return true
2619 else if pname == "!=" then
2620 var res = v.equal_test(arguments[0], arguments[1])
2621 v.ret(v.new_expr("!{res}", ret.as(not null)))
2622 return true
2623 else if pname == "<" then
2624 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2625 return true
2626 else if pname == ">" then
2627 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2628 return true
2629 else if pname == "<=" then
2630 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2631 return true
2632 else if pname == ">=" then
2633 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2634 return true
2635 else if pname == "to_i" then
2636 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2637 return true
2638 else if pname == "to_b" then
2639 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2640 return true
2641 else if pname == "to_i8" then
2642 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2643 return true
2644 else if pname == "to_u16" then
2645 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2646 return true
2647 else if pname == "to_i32" then
2648 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2649 return true
2650 else if pname == "to_u32" then
2651 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2652 return true
2653 else if pname == "to_f" then
2654 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2655 return true
2656 else if pname == "&" then
2657 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2658 return true
2659 else if pname == "|" then
2660 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2661 return true
2662 else if pname == "^" then
2663 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2664 return true
2665 else if pname == "unary ~" then
2666 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2667 return true
2668 end
2669 else if cname == "UInt16" then
2670 if pname == "output" then
2671 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2672 return true
2673 else if pname == "object_id" then
2674 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2675 return true
2676 else if pname == "+" then
2677 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2678 return true
2679 else if pname == "-" then
2680 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2681 return true
2682 else if pname == "unary -" then
2683 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2684 return true
2685 else if pname == "unary +" then
2686 v.ret(arguments[0])
2687 return true
2688 else if pname == "*" then
2689 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2690 return true
2691 else if pname == "/" then
2692 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2693 return true
2694 else if pname == "%" then
2695 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2696 return true
2697 else if pname == "<<" then
2698 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2699 return true
2700 else if pname == ">>" then
2701 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2702 return true
2703 else if pname == "==" then
2704 v.ret(v.equal_test(arguments[0], arguments[1]))
2705 return true
2706 else if pname == "!=" then
2707 var res = v.equal_test(arguments[0], arguments[1])
2708 v.ret(v.new_expr("!{res}", ret.as(not null)))
2709 return true
2710 else if pname == "<" then
2711 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2712 return true
2713 else if pname == ">" then
2714 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2715 return true
2716 else if pname == "<=" then
2717 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2718 return true
2719 else if pname == ">=" then
2720 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2721 return true
2722 else if pname == "to_i" then
2723 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2724 return true
2725 else if pname == "to_b" then
2726 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2727 return true
2728 else if pname == "to_i8" then
2729 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2730 return true
2731 else if pname == "to_i16" then
2732 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2733 return true
2734 else if pname == "to_i32" then
2735 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2736 return true
2737 else if pname == "to_u32" then
2738 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2739 return true
2740 else if pname == "to_f" then
2741 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2742 return true
2743 else if pname == "&" then
2744 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2745 return true
2746 else if pname == "|" then
2747 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2748 return true
2749 else if pname == "^" then
2750 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2751 return true
2752 else if pname == "unary ~" then
2753 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2754 return true
2755 end
2756 else if cname == "Int32" then
2757 if pname == "output" then
2758 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2759 return true
2760 else if pname == "object_id" then
2761 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2762 return true
2763 else if pname == "+" then
2764 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2765 return true
2766 else if pname == "-" then
2767 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2768 return true
2769 else if pname == "unary -" then
2770 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2771 return true
2772 else if pname == "unary +" then
2773 v.ret(arguments[0])
2774 return true
2775 else if pname == "*" then
2776 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2777 return true
2778 else if pname == "/" then
2779 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2780 return true
2781 else if pname == "%" then
2782 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2783 return true
2784 else if pname == "<<" then
2785 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2786 return true
2787 else if pname == ">>" then
2788 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2789 return true
2790 else if pname == "==" then
2791 v.ret(v.equal_test(arguments[0], arguments[1]))
2792 return true
2793 else if pname == "!=" then
2794 var res = v.equal_test(arguments[0], arguments[1])
2795 v.ret(v.new_expr("!{res}", ret.as(not null)))
2796 return true
2797 else if pname == "<" then
2798 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2799 return true
2800 else if pname == ">" then
2801 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2802 return true
2803 else if pname == "<=" then
2804 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2805 return true
2806 else if pname == ">=" then
2807 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2808 return true
2809 else if pname == "to_i" then
2810 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2811 return true
2812 else if pname == "to_b" then
2813 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2814 return true
2815 else if pname == "to_i8" then
2816 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2817 return true
2818 else if pname == "to_i16" then
2819 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2820 return true
2821 else if pname == "to_u16" then
2822 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2823 return true
2824 else if pname == "to_u32" then
2825 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2826 return true
2827 else if pname == "to_f" then
2828 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2829 return true
2830 else if pname == "&" then
2831 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2832 return true
2833 else if pname == "|" then
2834 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2835 return true
2836 else if pname == "^" then
2837 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2838 return true
2839 else if pname == "unary ~" then
2840 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2841 return true
2842 end
2843 else if cname == "UInt32" then
2844 if pname == "output" then
2845 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2846 return true
2847 else if pname == "object_id" then
2848 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2849 return true
2850 else if pname == "+" then
2851 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2852 return true
2853 else if pname == "-" then
2854 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2855 return true
2856 else if pname == "unary -" then
2857 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2858 return true
2859 else if pname == "unary +" then
2860 v.ret(arguments[0])
2861 return true
2862 else if pname == "*" then
2863 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2864 return true
2865 else if pname == "/" then
2866 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2867 return true
2868 else if pname == "%" then
2869 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2870 return true
2871 else if pname == "<<" then
2872 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
2873 return true
2874 else if pname == ">>" then
2875 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2876 return true
2877 else if pname == "==" then
2878 v.ret(v.equal_test(arguments[0], arguments[1]))
2879 return true
2880 else if pname == "!=" then
2881 var res = v.equal_test(arguments[0], arguments[1])
2882 v.ret(v.new_expr("!{res}", ret.as(not null)))
2883 return true
2884 else if pname == "<" then
2885 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2886 return true
2887 else if pname == ">" then
2888 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2889 return true
2890 else if pname == "<=" then
2891 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
2892 return true
2893 else if pname == ">=" then
2894 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2895 return true
2896 else if pname == "to_i" then
2897 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2898 return true
2899 else if pname == "to_b" then
2900 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2901 return true
2902 else if pname == "to_i8" then
2903 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2904 return true
2905 else if pname == "to_i16" then
2906 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2907 return true
2908 else if pname == "to_u16" then
2909 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2910 return true
2911 else if pname == "to_i32" then
2912 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2913 return true
2914 else if pname == "to_f" then
2915 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2916 return true
2917 else if pname == "&" then
2918 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2919 return true
2920 else if pname == "|" then
2921 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2922 return true
2923 else if pname == "^" then
2924 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2925 return true
2926 else if pname == "unary ~" then
2927 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2928 return true
2929 end
2930 end
2931 if pname == "exit" then
2932 v.add("exit({arguments[1]});")
2933 return true
2934 else if pname == "sys" then
2935 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2936 return true
2937 else if pname == "calloc_string" then
2938 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2939 return true
2940 else if pname == "calloc_array" then
2941 v.calloc_array(ret.as(not null), arguments)
2942 return true
2943 else if pname == "object_id" then
2944 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2945 return true
2946 else if pname == "is_same_type" then
2947 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2948 return true
2949 else if pname == "is_same_instance" then
2950 v.ret(v.equal_test(arguments[0], arguments[1]))
2951 return true
2952 else if pname == "output_class_name" then
2953 var nat = v.class_name_string(arguments.first)
2954 v.add("printf(\"%s\\n\", {nat});")
2955 return true
2956 else if pname == "native_class_name" then
2957 var nat = v.class_name_string(arguments.first)
2958 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2959 return true
2960 else if pname == "force_garbage_collection" then
2961 v.add("nit_gcollect();")
2962 return true
2963 else if pname == "native_argc" then
2964 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2965 return true
2966 else if pname == "native_argv" then
2967 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2968 return true
2969 end
2970 return false
2971 end
2972
2973 # Compile an extern method
2974 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2975 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2976 do
2977 var externname
2978 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2979 if at != null and at.n_args.length == 1 then
2980 externname = at.arg_as_string(v.compiler.modelbuilder)
2981 if externname == null then return false
2982 else
2983 return false
2984 end
2985 v.add_extern(mpropdef.mclassdef.mmodule)
2986 var res: nullable RuntimeVariable = null
2987 var ret = mpropdef.msignature.return_mtype
2988 if ret != null then
2989 ret = v.resolve_for(ret, arguments.first)
2990 res = v.new_var_extern(ret)
2991 end
2992 v.adapt_signature(mpropdef, arguments)
2993 v.unbox_signature_extern(mpropdef, arguments)
2994
2995 if res == null then
2996 v.add("{externname}({arguments.join(", ")});")
2997 else
2998 v.add("{res} = {externname}({arguments.join(", ")});")
2999 res = v.box_extern(res, ret.as(not null))
3000 v.ret(res)
3001 end
3002 return true
3003 end
3004
3005 # Compile an extern factory
3006 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3007 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3008 do
3009 var externname
3010 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3011 if at != null then
3012 externname = at.arg_as_string(v.compiler.modelbuilder)
3013 if externname == null then return false
3014 else
3015 return false
3016 end
3017 v.add_extern(mpropdef.mclassdef.mmodule)
3018 v.adapt_signature(mpropdef, arguments)
3019 v.unbox_signature_extern(mpropdef, arguments)
3020 var ret = arguments.first.mtype
3021 var res = v.new_var_extern(ret)
3022
3023 arguments.shift
3024
3025 v.add("{res} = {externname}({arguments.join(", ")});")
3026 res = v.box_extern(res, ret)
3027 v.ret(res)
3028 return true
3029 end
3030 end
3031
3032 redef class AAttrPropdef
3033 redef fun can_inline: Bool do return not is_lazy
3034
3035 redef fun compile_to_c(v, mpropdef, arguments)
3036 do
3037 if mpropdef == mreadpropdef then
3038 assert arguments.length == 1
3039 var recv = arguments.first
3040 var res
3041 if is_lazy then
3042 var set
3043 var ret = self.mtype
3044 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3045 var guard = self.mlazypropdef.mproperty
3046 if useiset then
3047 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3048 else
3049 set = v.read_attribute(guard, recv)
3050 end
3051 v.add("if(likely({set})) \{")
3052 res = v.read_attribute(self.mpropdef.mproperty, recv)
3053 v.add("\} else \{")
3054
3055 var value = evaluate_expr(v, recv)
3056
3057 v.assign(res, value)
3058 if not useiset then
3059 var true_v = v.bool_instance(true)
3060 v.write_attribute(guard, arguments.first, true_v)
3061 end
3062 v.add("\}")
3063 else
3064 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3065 end
3066 v.assign(v.frame.returnvar.as(not null), res)
3067 else if mpropdef == mwritepropdef then
3068 assert arguments.length == 2
3069 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
3070 if is_lazy then
3071 var ret = self.mtype
3072 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3073 if not useiset then
3074 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3075 end
3076 end
3077 else
3078 abort
3079 end
3080 end
3081
3082 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3083 do
3084 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3085 end
3086
3087 # Evaluate, store and return the default value of the attribute
3088 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3089 do
3090 var oldnode = v.current_node
3091 v.current_node = self
3092 var old_frame = v.frame
3093 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3094 v.frame = frame
3095
3096 var value
3097 var mtype = self.mtype
3098 assert mtype != null
3099
3100 var nexpr = self.n_expr
3101 var nblock = self.n_block
3102 if nexpr != null then
3103 value = v.expr(nexpr, mtype)
3104 else if nblock != null then
3105 value = v.new_var(mtype)
3106 frame.returnvar = value
3107 frame.returnlabel = v.get_name("RET_LABEL")
3108 v.add("\{")
3109 v.stmt(nblock)
3110 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3111 v.add("\}")
3112 else
3113 abort
3114 end
3115
3116 v.write_attribute(self.mpropdef.mproperty, recv, value)
3117
3118 v.frame = old_frame
3119 v.current_node = oldnode
3120
3121 return value
3122 end
3123
3124 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3125 do
3126 var nexpr = self.n_expr
3127 if nexpr != null then return
3128
3129 var oldnode = v.current_node
3130 v.current_node = self
3131 var old_frame = v.frame
3132 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3133 v.frame = frame
3134 # Force read to check the initialization
3135 v.read_attribute(self.mpropdef.mproperty, recv)
3136 v.frame = old_frame
3137 v.current_node = oldnode
3138 end
3139 end
3140
3141 redef class AClassdef
3142 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3143 do
3144 if mpropdef == self.mfree_init then
3145 assert mpropdef.mproperty.is_root_init
3146 assert arguments.length == 1
3147 if not mpropdef.is_intro then
3148 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3149 end
3150 return
3151 else
3152 abort
3153 end
3154 end
3155 end
3156
3157 redef class AExpr
3158 # Try to compile self as an expression
3159 # Do not call this method directly, use `v.expr` instead
3160 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3161 do
3162 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3163 var mtype = self.mtype
3164 if mtype == null then
3165 return null
3166 else
3167 var res = v.new_var(mtype)
3168 v.add("/* {res} = NOT YET {class_name} */")
3169 return res
3170 end
3171 end
3172
3173 # Try to compile self as a statement
3174 # Do not call this method directly, use `v.stmt` instead
3175 private fun stmt(v: AbstractCompilerVisitor)
3176 do
3177 expr(v)
3178 end
3179 end
3180
3181 redef class ABlockExpr
3182 redef fun stmt(v)
3183 do
3184 for e in self.n_expr do v.stmt(e)
3185 end
3186 redef fun expr(v)
3187 do
3188 var last = self.n_expr.last
3189 for e in self.n_expr do
3190 if e == last then break
3191 v.stmt(e)
3192 end
3193 return v.expr(last, null)
3194 end
3195 end
3196
3197 redef class AVardeclExpr
3198 redef fun stmt(v)
3199 do
3200 var variable = self.variable.as(not null)
3201 var ne = self.n_expr
3202 if ne != null then
3203 var i = v.expr(ne, variable.declared_type)
3204 v.assign(v.variable(variable), i)
3205 end
3206 end
3207 end
3208
3209 redef class AVarExpr
3210 redef fun expr(v)
3211 do
3212 var res = v.variable(self.variable.as(not null))
3213 var mtype = self.mtype.as(not null)
3214 return v.autoadapt(res, mtype)
3215 end
3216 end
3217
3218 redef class AVarAssignExpr
3219 redef fun expr(v)
3220 do
3221 var variable = self.variable.as(not null)
3222 var i = v.expr(self.n_value, variable.declared_type)
3223 v.assign(v.variable(variable), i)
3224 return i
3225 end
3226 end
3227
3228 redef class AVarReassignExpr
3229 redef fun stmt(v)
3230 do
3231 var variable = self.variable.as(not null)
3232 var vari = v.variable(variable)
3233 var value = v.expr(self.n_value, variable.declared_type)
3234 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3235 assert res != null
3236 v.assign(v.variable(variable), res)
3237 end
3238 end
3239
3240 redef class ASelfExpr
3241 redef fun expr(v) do return v.frame.arguments.first
3242 end
3243
3244 redef class AImplicitSelfExpr
3245 redef fun expr(v) do
3246 if not is_sys then return super
3247 return v.new_expr("glob_sys", mtype.as(not null))
3248 end
3249 end
3250
3251 redef class AEscapeExpr
3252 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3253 end
3254
3255 redef class AReturnExpr
3256 redef fun stmt(v)
3257 do
3258 var nexpr = self.n_expr
3259 if nexpr != null then
3260 var returnvar = v.frame.returnvar.as(not null)
3261 var i = v.expr(nexpr, returnvar.mtype)
3262 v.assign(returnvar, i)
3263 end
3264 v.add("goto {v.frame.returnlabel.as(not null)};")
3265 end
3266 end
3267
3268 redef class AAbortExpr
3269 redef fun stmt(v) do v.add_abort("Aborted")
3270 end
3271
3272 redef class AIfExpr
3273 redef fun stmt(v)
3274 do
3275 var cond = v.expr_bool(self.n_expr)
3276 v.add("if ({cond})\{")
3277 v.stmt(self.n_then)
3278 v.add("\} else \{")
3279 v.stmt(self.n_else)
3280 v.add("\}")
3281 end
3282
3283 redef fun expr(v)
3284 do
3285 var res = v.new_var(self.mtype.as(not null))
3286 var cond = v.expr_bool(self.n_expr)
3287 v.add("if ({cond})\{")
3288 v.assign(res, v.expr(self.n_then.as(not null), null))
3289 v.add("\} else \{")
3290 v.assign(res, v.expr(self.n_else.as(not null), null))
3291 v.add("\}")
3292 return res
3293 end
3294 end
3295
3296 redef class AIfexprExpr
3297 redef fun expr(v)
3298 do
3299 var res = v.new_var(self.mtype.as(not null))
3300 var cond = v.expr_bool(self.n_expr)
3301 v.add("if ({cond})\{")
3302 v.assign(res, v.expr(self.n_then, null))
3303 v.add("\} else \{")
3304 v.assign(res, v.expr(self.n_else, null))
3305 v.add("\}")
3306 return res
3307 end
3308 end
3309
3310 redef class ADoExpr
3311 redef fun stmt(v)
3312 do
3313 v.stmt(self.n_block)
3314 v.add_escape_label(break_mark)
3315 end
3316 end
3317
3318 redef class AWhileExpr
3319 redef fun stmt(v)
3320 do
3321 v.add("for(;;) \{")
3322 var cond = v.expr_bool(self.n_expr)
3323 v.add("if (!{cond}) break;")
3324 v.stmt(self.n_block)
3325 v.add_escape_label(continue_mark)
3326 v.add("\}")
3327 v.add_escape_label(break_mark)
3328 end
3329 end
3330
3331 redef class ALoopExpr
3332 redef fun stmt(v)
3333 do
3334 v.add("for(;;) \{")
3335 v.stmt(self.n_block)
3336 v.add_escape_label(continue_mark)
3337 v.add("\}")
3338 v.add_escape_label(break_mark)
3339 end
3340 end
3341
3342 redef class AForExpr
3343 redef fun stmt(v)
3344 do
3345 for g in n_groups do
3346 var cl = v.expr(g.n_expr, null)
3347 var it_meth = g.method_iterator
3348 assert it_meth != null
3349 var it = v.compile_callsite(it_meth, [cl])
3350 assert it != null
3351 g.it = it
3352 end
3353 v.add("for(;;) \{")
3354 for g in n_groups do
3355 var it = g.it
3356 var isok_meth = g.method_is_ok
3357 assert isok_meth != null
3358 var ok = v.compile_callsite(isok_meth, [it])
3359 assert ok != null
3360 v.add("if(!{ok}) break;")
3361 if g.variables.length == 1 then
3362 var item_meth = g.method_item
3363 assert item_meth != null
3364 var i = v.compile_callsite(item_meth, [it])
3365 assert i != null
3366 v.assign(v.variable(g.variables.first), i)
3367 else if g.variables.length == 2 then
3368 var key_meth = g.method_key
3369 assert key_meth != null
3370 var i = v.compile_callsite(key_meth, [it])
3371 assert i != null
3372 v.assign(v.variable(g.variables[0]), i)
3373 var item_meth = g.method_item
3374 assert item_meth != null
3375 i = v.compile_callsite(item_meth, [it])
3376 assert i != null
3377 v.assign(v.variable(g.variables[1]), i)
3378 else
3379 abort
3380 end
3381 end
3382 v.stmt(self.n_block)
3383 v.add_escape_label(continue_mark)
3384 for g in n_groups do
3385 var next_meth = g.method_next
3386 assert next_meth != null
3387 v.compile_callsite(next_meth, [g.it])
3388 end
3389 v.add("\}")
3390 v.add_escape_label(break_mark)
3391
3392 for g in n_groups do
3393 var method_finish = g.method_finish
3394 if method_finish != null then
3395 # TODO: Find a way to call this also in long escape (e.g. return)
3396 v.compile_callsite(method_finish, [g.it])
3397 end
3398 end
3399 end
3400 end
3401
3402 redef class AForGroup
3403 # C variable representing the iterator
3404 private var it: RuntimeVariable is noinit
3405 end
3406
3407 redef class AAssertExpr
3408 redef fun stmt(v)
3409 do
3410 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3411
3412 var cond = v.expr_bool(self.n_expr)
3413 v.add("if (unlikely(!{cond})) \{")
3414 v.stmt(self.n_else)
3415 var nid = self.n_id
3416 if nid != null then
3417 v.add_abort("Assert '{nid.text}' failed")
3418 else
3419 v.add_abort("Assert failed")
3420 end
3421 v.add("\}")
3422 end
3423 end
3424
3425 redef class AOrExpr
3426 redef fun expr(v)
3427 do
3428 var res = v.new_var(self.mtype.as(not null))
3429 var i1 = v.expr_bool(self.n_expr)
3430 v.add("if ({i1}) \{")
3431 v.add("{res} = 1;")
3432 v.add("\} else \{")
3433 var i2 = v.expr_bool(self.n_expr2)
3434 v.add("{res} = {i2};")
3435 v.add("\}")
3436 return res
3437 end
3438 end
3439
3440 redef class AImpliesExpr
3441 redef fun expr(v)
3442 do
3443 var res = v.new_var(self.mtype.as(not null))
3444 var i1 = v.expr_bool(self.n_expr)
3445 v.add("if (!{i1}) \{")
3446 v.add("{res} = 1;")
3447 v.add("\} else \{")
3448 var i2 = v.expr_bool(self.n_expr2)
3449 v.add("{res} = {i2};")
3450 v.add("\}")
3451 return res
3452 end
3453 end
3454
3455 redef class AAndExpr
3456 redef fun expr(v)
3457 do
3458 var res = v.new_var(self.mtype.as(not null))
3459 var i1 = v.expr_bool(self.n_expr)
3460 v.add("if (!{i1}) \{")
3461 v.add("{res} = 0;")
3462 v.add("\} else \{")
3463 var i2 = v.expr_bool(self.n_expr2)
3464 v.add("{res} = {i2};")
3465 v.add("\}")
3466 return res
3467 end
3468 end
3469
3470 redef class ANotExpr
3471 redef fun expr(v)
3472 do
3473 var cond = v.expr_bool(self.n_expr)
3474 return v.new_expr("!{cond}", self.mtype.as(not null))
3475 end
3476 end
3477
3478 redef class AOrElseExpr
3479 redef fun expr(v)
3480 do
3481 var res = v.new_var(self.mtype.as(not null))
3482 var i1 = v.expr(self.n_expr, null)
3483 v.add("if ({i1}!=NULL) \{")
3484 v.assign(res, i1)
3485 v.add("\} else \{")
3486 var i2 = v.expr(self.n_expr2, null)
3487 v.assign(res, i2)
3488 v.add("\}")
3489 return res
3490 end
3491 end
3492
3493 redef class AIntegerExpr
3494 redef fun expr(v) do
3495 if value isa Int then return v.int_instance(value.as(Int))
3496 if value isa Byte then return v.byte_instance(value.as(Byte))
3497 if value isa Int8 then return v.int8_instance(value.as(Int8))
3498 if value isa Int16 then return v.int16_instance(value.as(Int16))
3499 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3500 if value isa Int32 then return v.int32_instance(value.as(Int32))
3501 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3502 # Should never happen
3503 abort
3504 end
3505 end
3506
3507 redef class AFloatExpr
3508 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3509 end
3510
3511 redef class ACharExpr
3512 redef fun expr(v) do return v.char_instance(self.value.as(not null))
3513 end
3514
3515 redef class AArrayExpr
3516 redef fun expr(v)
3517 do
3518 var mtype = self.element_mtype.as(not null)
3519 var array = new Array[RuntimeVariable]
3520 var res = v.array_instance(array, mtype)
3521
3522 var old_comprehension = v.frame.comprehension
3523 v.frame.comprehension = res
3524 for nexpr in self.n_exprs do
3525 v.stmt(nexpr)
3526 end
3527 v.frame.comprehension = old_comprehension
3528
3529 return res
3530 end
3531 end
3532
3533 redef class AStringFormExpr
3534 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3535 end
3536
3537 redef class ASuperstringExpr
3538 redef fun expr(v)
3539 do
3540 var type_string = mtype.as(not null)
3541
3542 # Collect elements of the superstring
3543 var array = new Array[AExpr]
3544 for ne in self.n_exprs do
3545 # Drop literal empty string.
3546 # They appears in things like "{a}" that is ["", a, ""]
3547 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3548 array.add(ne)
3549 end
3550
3551 # Store the allocated native array in a static variable
3552 # For reusing later
3553 var varonce = v.get_name("varonce")
3554 v.add("if (unlikely({varonce}==NULL)) \{")
3555
3556 # The native array that will contains the elements to_s-ized.
3557 # For fast concatenation.
3558 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3559
3560 v.add_decl("static {a.mtype.ctype} {varonce};")
3561
3562 # Pre-fill the array with the literal string parts.
3563 # So they do not need to be filled again when reused
3564 for i in [0..array.length[ do
3565 var ne = array[i]
3566 if not ne isa AStringFormExpr then continue
3567 var e = v.expr(ne, null)
3568 v.native_array_set(a, i, e)
3569 end
3570
3571 v.add("\} else \{")
3572 # Take the native-array from the store.
3573 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3574 # WARNING: not thread safe! (FIXME?)
3575 v.add("{a} = {varonce};")
3576 v.add("{varonce} = NULL;")
3577 v.add("\}")
3578
3579 # Stringify the elements and put them in the native array
3580 var to_s_method = v.get_property("to_s", v.object_type)
3581 for i in [0..array.length[ do
3582 var ne = array[i]
3583 if ne isa AStringFormExpr then continue
3584 var e = v.expr(ne, null)
3585 # Skip the `to_s` if the element is already a String
3586 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3587 e = v.send(to_s_method, [e]).as(not null)
3588 end
3589 v.native_array_set(a, i, e)
3590 end
3591
3592 # Fast join the native string to get the result
3593 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3594
3595 # We finish to work with the native array,
3596 # so store it so that it can be reused
3597 v.add("{varonce} = {a};")
3598 return res
3599 end
3600 end
3601
3602 redef class ACrangeExpr
3603 redef fun expr(v)
3604 do
3605 var i1 = v.expr(self.n_expr, null)
3606 var i2 = v.expr(self.n_expr2, null)
3607 var mtype = self.mtype.as(MClassType)
3608 var res = v.init_instance(mtype)
3609 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3610 return res
3611 end
3612 end
3613
3614 redef class AOrangeExpr
3615 redef fun expr(v)
3616 do
3617 var i1 = v.expr(self.n_expr, null)
3618 var i2 = v.expr(self.n_expr2, null)
3619 var mtype = self.mtype.as(MClassType)
3620 var res = v.init_instance(mtype)
3621 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3622 return res
3623 end
3624 end
3625
3626 redef class ATrueExpr
3627 redef fun expr(v) do return v.bool_instance(true)
3628 end
3629
3630 redef class AFalseExpr
3631 redef fun expr(v) do return v.bool_instance(false)
3632 end
3633
3634 redef class ANullExpr
3635 redef fun expr(v) do return v.null_instance
3636 end
3637
3638 redef class AIsaExpr
3639 redef fun expr(v)
3640 do
3641 var i = v.expr(self.n_expr, null)
3642 var cast_type = self.cast_type
3643 if cast_type == null then return null # no-no on broken node
3644 return v.type_test(i, cast_type, "isa")
3645 end
3646 end
3647
3648 redef class AAsCastExpr
3649 redef fun expr(v)
3650 do
3651 var i = v.expr(self.n_expr, null)
3652 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3653
3654 v.add_cast(i, self.mtype.as(not null), "as")
3655 return i
3656 end
3657 end
3658
3659 redef class AAsNotnullExpr
3660 redef fun expr(v)
3661 do
3662 var i = v.expr(self.n_expr, null)
3663 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3664
3665 if i.mtype.is_c_primitive then return i
3666
3667 v.add("if (unlikely({i} == NULL)) \{")
3668 v.add_abort("Cast failed")
3669 v.add("\}")
3670 return i
3671 end
3672 end
3673
3674 redef class AParExpr
3675 redef fun expr(v) do return v.expr(self.n_expr, null)
3676 end
3677
3678 redef class AOnceExpr
3679 redef fun expr(v)
3680 do
3681 var mtype = self.mtype.as(not null)
3682 var name = v.get_name("varonce")
3683 var guard = v.get_name(name + "_guard")
3684 v.add_decl("static {mtype.ctype} {name};")
3685 v.add_decl("static int {guard};")
3686 var res = v.new_var(mtype)
3687 v.add("if (likely({guard})) \{")
3688 v.add("{res} = {name};")
3689 v.add("\} else \{")
3690 var i = v.expr(self.n_expr, mtype)
3691 v.add("{res} = {i};")
3692 v.add("{name} = {res};")
3693 v.add("{guard} = 1;")
3694 v.add("\}")
3695 return res
3696 end
3697 end
3698
3699 redef class ASendExpr
3700 redef fun expr(v)
3701 do
3702 var recv = v.expr(self.n_expr, null)
3703 var callsite = self.callsite.as(not null)
3704 if callsite.is_broken then return null
3705 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3706 return v.compile_callsite(callsite, args)
3707 end
3708 end
3709
3710 redef class ASendReassignFormExpr
3711 redef fun stmt(v)
3712 do
3713 var recv = v.expr(self.n_expr, null)
3714 var callsite = self.callsite.as(not null)
3715 if callsite.is_broken then return
3716 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3717
3718 var value = v.expr(self.n_value, null)
3719
3720 var left = v.compile_callsite(callsite, args)
3721 assert left != null
3722
3723 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3724 assert res != null
3725
3726 args.add(res)
3727 v.compile_callsite(self.write_callsite.as(not null), args)
3728 end
3729 end
3730
3731 redef class ASuperExpr
3732 redef fun expr(v)
3733 do
3734 var frame = v.frame.as(not null)
3735 var recv = frame.arguments.first
3736
3737 var callsite = self.callsite
3738 if callsite != null then
3739 if callsite.is_broken then return null
3740 var args
3741
3742 if self.n_args.n_exprs.is_empty then
3743 # Add automatic arguments for the super init call
3744 args = [recv]
3745 for i in [0..callsite.msignature.arity[ do
3746 args.add(frame.arguments[i+1])
3747 end
3748 else
3749 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3750 end
3751
3752 # Super init call
3753 var res = v.compile_callsite(callsite, args)
3754 return res
3755 end
3756
3757 var mpropdef = self.mpropdef.as(not null)
3758
3759 var args
3760 if self.n_args.n_exprs.is_empty then
3761 args = frame.arguments
3762 else
3763 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3764 end
3765
3766 # Standard call-next-method
3767 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3768 end
3769 end
3770
3771 redef class ANewExpr
3772 redef fun expr(v)
3773 do
3774 var mtype = self.recvtype
3775 assert mtype != null
3776
3777 if mtype.mclass.name == "NativeArray" then
3778 assert self.n_args.n_exprs.length == 1
3779 var l = v.expr(self.n_args.n_exprs.first, null)
3780 assert mtype isa MGenericType
3781 var elttype = mtype.arguments.first
3782 return v.native_array_instance(elttype, l)
3783 end
3784
3785 var recv = v.init_instance_or_extern(mtype)
3786
3787 var callsite = self.callsite
3788 if callsite == null then return recv
3789 if callsite.is_broken then return null
3790
3791 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3792 var res2 = v.compile_callsite(callsite, args)
3793 if res2 != null then
3794 #self.debug("got {res2} from {mproperty}. drop {recv}")
3795 return res2
3796 end
3797 return recv
3798 end
3799 end
3800
3801 redef class AAttrExpr
3802 redef fun expr(v)
3803 do
3804 var recv = v.expr(self.n_expr, null)
3805 var mproperty = self.mproperty.as(not null)
3806 return v.read_attribute(mproperty, recv)
3807 end
3808 end
3809
3810 redef class AAttrAssignExpr
3811 redef fun expr(v)
3812 do
3813 var recv = v.expr(self.n_expr, null)
3814 var i = v.expr(self.n_value, null)
3815 var mproperty = self.mproperty.as(not null)
3816 v.write_attribute(mproperty, recv, i)
3817 return i
3818 end
3819 end
3820
3821 redef class AAttrReassignExpr
3822 redef fun stmt(v)
3823 do
3824 var recv = v.expr(self.n_expr, null)
3825 var value = v.expr(self.n_value, null)
3826 var mproperty = self.mproperty.as(not null)
3827 var attr = v.read_attribute(mproperty, recv)
3828 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3829 assert res != null
3830 v.write_attribute(mproperty, recv, res)
3831 end
3832 end
3833
3834 redef class AIssetAttrExpr
3835 redef fun expr(v)
3836 do
3837 var recv = v.expr(self.n_expr, null)
3838 var mproperty = self.mproperty.as(not null)
3839 return v.isset_attribute(mproperty, recv)
3840 end
3841 end
3842
3843 redef class AVarargExpr
3844 redef fun expr(v)
3845 do
3846 return v.expr(self.n_expr, null)
3847 end
3848 end
3849
3850 redef class ANamedargExpr
3851 redef fun expr(v)
3852 do
3853 return v.expr(self.n_expr, null)
3854 end
3855 end
3856
3857 redef class ADebugTypeExpr
3858 redef fun stmt(v)
3859 do
3860 # do nothing
3861 end
3862 end
3863
3864 # Utils
3865
3866 redef class Array[E]
3867 # Return a new `Array` with the elements only contened in self and not in `o`
3868 fun -(o: Array[E]): Array[E] do
3869 var res = new Array[E]
3870 for e in self do if not o.has(e) then res.add(e)
3871 return res
3872 end
3873 end
3874
3875 redef class MModule
3876 # All `MProperty` associated to all `MClassDef` of `mclass`
3877 fun properties(mclass: MClass): Set[MProperty] do
3878 if not self.properties_cache.has_key(mclass) then
3879 var properties = new HashSet[MProperty]
3880 var parents = new Array[MClass]
3881 if self.flatten_mclass_hierarchy.has(mclass) then
3882 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3883 end
3884 for parent in parents do
3885 properties.add_all(self.properties(parent))
3886 end
3887 for mclassdef in mclass.mclassdefs do
3888 if not self.in_importation <= mclassdef.mmodule then continue
3889 for mprop in mclassdef.intro_mproperties do
3890 properties.add(mprop)
3891 end
3892 end
3893 self.properties_cache[mclass] = properties
3894 end
3895 return properties_cache[mclass]
3896 end
3897 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3898
3899 # Write FFI and nitni results to file
3900 fun finalize_ffi(c: AbstractCompiler) do end
3901
3902 # Give requided addinional system libraries (as given to LD_LIBS)
3903 # Note: can return null instead of an empty set
3904 fun collect_linker_libs: nullable Array[String] do return null
3905 end
3906
3907 # Create a tool context to handle options and paths
3908 var toolcontext = new ToolContext
3909
3910 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3911
3912 # We do not add other options, so process them now!
3913 toolcontext.process_options(args)
3914
3915 # We need a model to collect stufs
3916 var model = new Model
3917 # An a model builder to parse files
3918 var modelbuilder = new ModelBuilder(model, toolcontext)
3919
3920 var arguments = toolcontext.option_context.rest
3921 if arguments.length > 1 and toolcontext.opt_output.value != null then
3922 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3923 exit 1
3924 end
3925
3926 # Here we load an process all modules passed on the command line
3927 var mmodules = modelbuilder.parse(arguments)
3928
3929 if mmodules.is_empty then toolcontext.quit
3930
3931 modelbuilder.run_phases
3932
3933 for mmodule in mmodules do
3934 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3935 var ms = [mmodule]
3936 toolcontext.run_global_phases(ms)
3937 end