model: remove `new_msignature` and special call as the new signature is the method...
[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 -Wno-trigraphs\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("#ifdef __linux__")
658 self.header.add_decl(" #include <endian.h>")
659 self.header.add_decl("#endif")
660 self.header.add_decl("#include <inttypes.h>\n")
661 self.header.add_decl("#include \"gc_chooser.h\"")
662 self.header.add_decl("#ifdef __APPLE__")
663 self.header.add_decl(" #include <libkern/OSByteOrder.h>")
664 self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
665 self.header.add_decl("#endif")
666 self.header.add_decl("#ifdef __pnacl__")
667 self.header.add_decl(" #define be16toh(val) (((val) >> 8) | ((val) << 8))")
668 self.header.add_decl(" #define be32toh(val) ((be16toh((val) << 16) | (be16toh((val) >> 16))))")
669 self.header.add_decl("#endif")
670 self.header.add_decl("#ifdef ANDROID")
671 self.header.add_decl(" #define be32toh(val) betoh32(val)")
672 self.header.add_decl(" #include <android/log.h>")
673 self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
674 self.header.add_decl("#else")
675 self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
676 self.header.add_decl("#endif")
677
678 compile_header_structs
679 compile_nitni_structs
680
681 var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
682 if gccd_disable.has("noreturn") or gccd_disable.has("all") then
683 # Signal handler function prototype
684 self.header.add_decl("void fatal_exit(int);")
685 else
686 self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
687 end
688
689 if gccd_disable.has("likely") or gccd_disable.has("all") then
690 self.header.add_decl("#define likely(x) (x)")
691 self.header.add_decl("#define unlikely(x) (x)")
692 else if gccd_disable.has("correct-likely") then
693 # invert the `likely` definition
694 # Used by masochists to bench the worst case
695 self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
696 self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
697 else
698 self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
699 self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
700 end
701
702 # Global variable used by intern methods
703 self.header.add_decl("extern int glob_argc;")
704 self.header.add_decl("extern char **glob_argv;")
705 self.header.add_decl("extern val *glob_sys;")
706 end
707
708 # Declaration of structures for live Nit types
709 protected fun compile_header_structs is abstract
710
711 # Declaration of structures for nitni undelying the FFI
712 protected fun compile_nitni_structs
713 do
714 self.header.add_decl """
715 /* Native reference to Nit objects */
716 /* This structure is used to represent every Nit type in extern methods and custom C code. */
717 struct nitni_ref {
718 struct nitni_ref *next,
719 *prev; /* adjacent global references in global list */
720 int count; /* number of time this global reference has been marked */
721 };
722
723 /* List of global references from C code to Nit objects */
724 /* Instanciated empty at init of Nit system and filled explicitly by user in C code */
725 struct nitni_global_ref_list_t {
726 struct nitni_ref *head, *tail;
727 };
728 extern struct nitni_global_ref_list_t *nitni_global_ref_list;
729
730 /* Initializer of global reference list */
731 extern void nitni_global_ref_list_init();
732
733 /* Intern function to add a global reference to the list */
734 extern void nitni_global_ref_add( struct nitni_ref *ref );
735
736 /* Intern function to remove a global reference from the list */
737 extern void nitni_global_ref_remove( struct nitni_ref *ref );
738
739 /* Increase count on an existing global reference */
740 extern void nitni_global_ref_incr( struct nitni_ref *ref );
741
742 /* Decrease count on an existing global reference */
743 extern void nitni_global_ref_decr( struct nitni_ref *ref );
744 """
745 end
746
747 fun compile_finalizer_function
748 do
749 var finalizable_type = mainmodule.finalizable_type
750 if finalizable_type == null then return
751
752 var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
753
754 if finalize_meth == null then
755 modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
756 return
757 end
758
759 var v = self.new_visitor
760 v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
761 var recv = v.new_expr("obj", finalizable_type)
762 v.send(finalize_meth, [recv])
763 v.add "\}"
764 end
765
766 # Generate the main C function.
767 #
768 # This function:
769 #
770 # * allocate the Sys object if it exists
771 # * call init if is exists
772 # * call main if it exists
773 fun compile_main_function
774 do
775 var v = self.new_visitor
776 v.add_decl("#include <signal.h>")
777 var platform = target_platform
778
779 var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
780
781 if platform.supports_libunwind then
782 v.add_decl("#ifndef NO_STACKTRACE")
783 v.add_decl("#define UNW_LOCAL_ONLY")
784 v.add_decl("#include <libunwind.h>")
785 v.add_decl("#include \"c_functions_hash.h\"")
786 v.add_decl("#endif")
787 end
788 v.add_decl("int glob_argc;")
789 v.add_decl("char **glob_argv;")
790 v.add_decl("val *glob_sys;")
791
792 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
793 for tag in count_type_test_tags do
794 v.add_decl("long count_type_test_resolved_{tag};")
795 v.add_decl("long count_type_test_unresolved_{tag};")
796 v.add_decl("long count_type_test_skipped_{tag};")
797 v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
798 v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
799 v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
800 end
801 end
802
803 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
804 v.add_decl("long count_invoke_by_tables;")
805 v.add_decl("long count_invoke_by_direct;")
806 v.add_decl("long count_invoke_by_inline;")
807 v.compiler.header.add_decl("extern long count_invoke_by_tables;")
808 v.compiler.header.add_decl("extern long count_invoke_by_direct;")
809 v.compiler.header.add_decl("extern long count_invoke_by_inline;")
810 end
811
812 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
813 v.add_decl("long count_attr_reads = 0;")
814 v.add_decl("long count_isset_checks = 0;")
815 v.compiler.header.add_decl("extern long count_attr_reads;")
816 v.compiler.header.add_decl("extern long count_isset_checks;")
817 end
818
819 v.add_decl("static void show_backtrace(void) \{")
820 if platform.supports_libunwind then
821 v.add_decl("#ifndef NO_STACKTRACE")
822 v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
823 v.add_decl("unw_cursor_t cursor;")
824 v.add_decl("if(opt==NULL)\{")
825 v.add_decl("unw_context_t uc;")
826 v.add_decl("unw_word_t ip;")
827 v.add_decl("char* procname = malloc(sizeof(char) * 100);")
828 v.add_decl("unw_getcontext(&uc);")
829 v.add_decl("unw_init_local(&cursor, &uc);")
830 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
831 v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
832 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
833 v.add_decl("while (unw_step(&cursor) > 0) \{")
834 v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
835 v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
836 v.add_decl(" if (recv != NULL)\{")
837 v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
838 v.add_decl(" \}else\{")
839 v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
840 v.add_decl(" \}")
841 v.add_decl("\}")
842 v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
843 v.add_decl("free(procname);")
844 v.add_decl("\}")
845 v.add_decl("#endif /* NO_STACKTRACE */")
846 end
847 v.add_decl("\}")
848
849 v.add_decl("void sig_handler(int signo)\{")
850 v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
851 v.add_decl("show_backtrace();")
852 # rethrows
853 v.add_decl("signal(signo, SIG_DFL);")
854 v.add_decl("kill(getpid(), signo);")
855 v.add_decl("\}")
856
857 v.add_decl("void fatal_exit(int status) \{")
858 v.add_decl("show_backtrace();")
859 v.add_decl("exit(status);")
860 v.add_decl("\}")
861
862 if no_main then
863 v.add_decl("int nit_main(int argc, char** argv) \{")
864 else
865 v.add_decl("int main(int argc, char** argv) \{")
866 end
867
868 v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
869 v.add("signal(SIGABRT, sig_handler);")
870 v.add("signal(SIGFPE, sig_handler);")
871 v.add("signal(SIGILL, sig_handler);")
872 v.add("signal(SIGINT, sig_handler);")
873 v.add("signal(SIGTERM, sig_handler);")
874 v.add("signal(SIGSEGV, sig_handler);")
875 v.add "#endif"
876 v.add("signal(SIGPIPE, SIG_IGN);")
877
878 v.add("glob_argc = argc; glob_argv = argv;")
879 v.add("initialize_gc_option();")
880
881 v.add "initialize_nitni_global_refs();"
882
883 var main_type = mainmodule.sys_type
884 if main_type != null then
885 var mainmodule = v.compiler.mainmodule
886 var glob_sys = v.init_instance(main_type)
887 v.add("glob_sys = {glob_sys};")
888 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
889 if main_init != null then
890 v.send(main_init, [glob_sys])
891 end
892 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
893 mainmodule.try_get_primitive_method("main", main_type.mclass)
894 if main_method != null then
895 v.send(main_method, [glob_sys])
896 end
897 end
898
899 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
900 v.add_decl("long count_type_test_resolved_total = 0;")
901 v.add_decl("long count_type_test_unresolved_total = 0;")
902 v.add_decl("long count_type_test_skipped_total = 0;")
903 v.add_decl("long count_type_test_total_total = 0;")
904 for tag in count_type_test_tags do
905 v.add_decl("long count_type_test_total_{tag};")
906 v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
907 v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
908 v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
909 v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
910 v.add("count_type_test_total_total += count_type_test_total_{tag};")
911 end
912 v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
913 v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
914 var tags = count_type_test_tags.to_a
915 tags.add("total")
916 for tag in tags do
917 v.add("printf(\"{tag}\");")
918 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
919 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
920 v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
921 v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
922 end
923 end
924
925 if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
926 v.add_decl("long count_invoke_total;")
927 v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
928 v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
929 v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
930 v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
931 v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
932 end
933
934 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
935 v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
936 v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
937 end
938
939 v.add("return 0;")
940 v.add("\}")
941
942 for m in mainmodule.in_importation.greaters do
943 var f = "FILE_"+m.c_name
944 v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
945 provide_declaration(f, "extern const char {f}[];")
946 end
947 end
948
949 # Copile all C functions related to the [incr|decr]_ref features of the FFI
950 fun compile_nitni_global_ref_functions
951 do
952 var v = self.new_visitor
953 v.add """
954 struct nitni_global_ref_list_t *nitni_global_ref_list;
955 void initialize_nitni_global_refs() {
956 nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
957 nitni_global_ref_list->head = NULL;
958 nitni_global_ref_list->tail = NULL;
959 }
960
961 void nitni_global_ref_add( struct nitni_ref *ref ) {
962 if ( nitni_global_ref_list->head == NULL ) {
963 nitni_global_ref_list->head = ref;
964 ref->prev = NULL;
965 } else {
966 nitni_global_ref_list->tail->next = ref;
967 ref->prev = nitni_global_ref_list->tail;
968 }
969 nitni_global_ref_list->tail = ref;
970
971 ref->next = NULL;
972 }
973
974 void nitni_global_ref_remove( struct nitni_ref *ref ) {
975 if ( ref->prev == NULL ) {
976 nitni_global_ref_list->head = ref->next;
977 } else {
978 ref->prev->next = ref->next;
979 }
980
981 if ( ref->next == NULL ) {
982 nitni_global_ref_list->tail = ref->prev;
983 } else {
984 ref->next->prev = ref->prev;
985 }
986 }
987
988 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
989 if ( ref->count == 0 ) /* not registered */
990 {
991 /* add to list */
992 nitni_global_ref_add( ref );
993 }
994
995 ref->count ++;
996 }
997
998 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
999 if ( ref->count == 1 ) /* was last reference */
1000 {
1001 /* remove from list */
1002 nitni_global_ref_remove( ref );
1003 }
1004
1005 ref->count --;
1006 }
1007 """
1008 end
1009
1010 # List of additional files required to compile (FFI)
1011 var extern_bodies = new Array[ExternFile]
1012
1013 # List of source files to copy over to the compile dir
1014 var files_to_copy = new Array[String]
1015
1016 # This is used to avoid adding an extern file more than once
1017 private var seen_extern = new ArraySet[String]
1018
1019 # Generate code that initialize the attributes on a new instance
1020 fun generate_init_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.init_expr(v, recv)
1027 end
1028 end
1029 end
1030
1031 # Generate code that check if an attribute is correctly initialized
1032 fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
1033 do
1034 var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
1035 self.mainmodule.linearize_mclassdefs(cds)
1036 for cd in cds do
1037 for npropdef in modelbuilder.collect_attr_propdef(cd) do
1038 npropdef.check_expr(v, recv)
1039 end
1040 end
1041 end
1042
1043 # stats
1044
1045 var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
1046 var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
1047 var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
1048 var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
1049
1050 protected fun init_count_type_test_tags: HashMap[String, Int]
1051 do
1052 var res = new HashMap[String, Int]
1053 for tag in count_type_test_tags do
1054 res[tag] = 0
1055 end
1056 return res
1057 end
1058
1059 # Display stats about compilation process
1060 #
1061 # Metrics used:
1062 #
1063 # * type tests against resolved types (`x isa Collection[Animal]`)
1064 # * type tests against unresolved types (`x isa Collection[E]`)
1065 # * type tests skipped
1066 # * type tests total
1067 fun display_stats
1068 do
1069 if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1070 print "# static count_type_test"
1071 print "\tresolved:\tunresolved\tskipped\ttotal"
1072 var count_type_test_total = init_count_type_test_tags
1073 count_type_test_resolved["total"] = 0
1074 count_type_test_unresolved["total"] = 0
1075 count_type_test_skipped["total"] = 0
1076 count_type_test_total["total"] = 0
1077 for tag in count_type_test_tags do
1078 count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
1079 count_type_test_resolved["total"] += count_type_test_resolved[tag]
1080 count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
1081 count_type_test_skipped["total"] += count_type_test_skipped[tag]
1082 count_type_test_total["total"] += count_type_test_total[tag]
1083 end
1084 var count_type_test = count_type_test_total["total"]
1085 var tags = count_type_test_tags.to_a
1086 tags.add("total")
1087 for tag in tags do
1088 printn tag
1089 printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
1090 printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
1091 printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
1092 printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
1093 print ""
1094 end
1095 end
1096 end
1097
1098 fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
1099 end
1100
1101 # A file unit (may be more than one file if
1102 # A file unit aim to be autonomous and is made or one or more `CodeWriter`s
1103 class CodeFile
1104 var name: String
1105 var writers = new Array[CodeWriter]
1106 var required_declarations = new HashSet[String]
1107 end
1108
1109 # Where to store generated lines
1110 class CodeWriter
1111 var file: CodeFile
1112 var lines: List[String] = new List[String]
1113 var decl_lines: List[String] = new List[String]
1114
1115 # Add a line in the main part of the generated C
1116 fun add(s: String) do self.lines.add(s)
1117
1118 # Add a line in the
1119 # (used for local or global declaration)
1120 fun add_decl(s: String) do self.decl_lines.add(s)
1121
1122 init
1123 do
1124 file.writers.add(self)
1125 end
1126 end
1127
1128 # A visitor on the AST of property definition that generate the C code.
1129 abstract class AbstractCompilerVisitor
1130
1131 type COMPILER: AbstractCompiler
1132
1133 # The associated compiler
1134 var compiler: COMPILER
1135
1136 # The current visited AST node
1137 var current_node: nullable ANode = null is writable
1138
1139 # The current `StaticFrame`
1140 var frame: nullable StaticFrame = null is writable
1141
1142 # Alias for self.compiler.mainmodule.object_type
1143 fun object_type: MClassType do return self.compiler.mainmodule.object_type
1144
1145 # Alias for self.compiler.mainmodule.bool_type
1146 fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
1147
1148 var writer: CodeWriter is noinit
1149
1150 init
1151 do
1152 self.writer = new CodeWriter(compiler.files.last)
1153 end
1154
1155 # Force to get the primitive property named `name` in the instance `recv` or abort
1156 fun get_property(name: String, recv: MType): MMethod
1157 do
1158 assert recv isa MClassType
1159 return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
1160 end
1161
1162 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1163 do
1164 if callsite.is_broken then return null
1165 return self.send(callsite.mproperty, arguments)
1166 end
1167
1168 fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
1169
1170 fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
1171
1172 fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
1173
1174 # Return an element of a native array.
1175 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1176 fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
1177
1178 # Store an element in a native array.
1179 # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
1180 fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
1181
1182 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
1183 # This method is used to manage varargs in signatures and returns the real array
1184 # of runtime variables to use in the call.
1185 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
1186 do
1187 var msignature = mpropdef.msignature.as(not null)
1188 var res = new Array[RuntimeVariable]
1189 res.add(recv)
1190
1191 if msignature.arity == 0 then return res
1192
1193 if map == null then
1194 assert args.length == msignature.arity
1195 for ne in args do
1196 res.add self.expr(ne, null)
1197 end
1198 return res
1199 end
1200
1201 # Eval in order of arguments, not parameters
1202 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
1203 for ne in args do
1204 exprs.add self.expr(ne, null)
1205 end
1206
1207 # Fill `res` with the result of the evaluation according to the mapping
1208 for i in [0..msignature.arity[ do
1209 var param = msignature.mparameters[i]
1210 var j = map.map.get_or_null(i)
1211 if j == null then
1212 # default value
1213 res.add(null_instance)
1214 continue
1215 end
1216 if param.is_vararg and args[i].vararg_decl > 0 then
1217 var vararg = exprs.sub(j, args[i].vararg_decl)
1218 var elttype = param.mtype
1219 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
1220 res.add(arg)
1221 continue
1222 end
1223 res.add exprs[j]
1224 end
1225 return res
1226 end
1227
1228 # Type handling
1229
1230 # Anchor a type to the main module and the current receiver
1231 fun anchor(mtype: MType): MType
1232 do
1233 if not mtype.need_anchor then return mtype
1234 return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
1235 end
1236
1237 fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
1238 do
1239 if not mtype.need_anchor then return mtype
1240 return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
1241 end
1242
1243 # Unsafely cast a value to a new type
1244 # ie the result share the same C variable but my have a different mcasttype
1245 # NOTE: if the adaptation is useless then `value` is returned as it.
1246 # ENSURE: `result.name == value.name`
1247 fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
1248 do
1249 mtype = self.anchor(mtype)
1250 var valmtype = value.mcasttype
1251 if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1252 return value
1253 end
1254
1255 if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
1256 var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
1257 return res
1258 else
1259 var res = new RuntimeVariable(value.name, valmtype, mtype)
1260 return res
1261 end
1262 end
1263
1264 # Generate a super call from a method definition
1265 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1266
1267 # Adapt the arguments of a method according to targetted `MMethodDef`
1268 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1269
1270 # Unbox all the arguments of a method when implemented `extern` or `intern`
1271 fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
1272
1273 # Box or unbox a value to another type iff a C type conversion is needed
1274 # ENSURE: `result.mtype.ctype == mtype.ctype`
1275 fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1276
1277 # Box extern classes to be used in the generated code
1278 fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1279
1280 # Unbox extern classes to be used in extern code (legacy NI and FFI)
1281 fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
1282
1283 # Generate a polymorphic subtype test
1284 fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
1285
1286 # Generate the code required to dynamically check if 2 objects share the same runtime type
1287 fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1288
1289 # Generate a Nit "is" for two runtime_variables
1290 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
1291
1292 # Sends
1293
1294 # Generate a static call on a method definition
1295 fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1296
1297 # Generate a polymorphic send for the method `m` and the arguments `args`
1298 fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1299
1300 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
1301 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1302 do
1303 assert t isa MClassType
1304 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
1305 return self.call(propdef, t, args)
1306 end
1307
1308 # Generate a monomorphic super send from the method `m`, the type `t` and the arguments `args`
1309 fun monomorphic_super_send(m: MMethodDef, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
1310 do
1311 assert t isa MClassType
1312 m = m.lookup_next_definition(self.compiler.mainmodule, t)
1313 return self.call(m, t, args)
1314 end
1315
1316 # Attributes handling
1317
1318 # Generate a polymorphic attribute is_set test
1319 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1320
1321 # Generate a polymorphic attribute read
1322 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
1323
1324 # Generate a polymorphic attribute write
1325 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
1326
1327 # Checks
1328
1329 # Add a check and an abort for a null receiver if needed
1330 fun check_recv_notnull(recv: RuntimeVariable)
1331 do
1332 if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
1333
1334 var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
1335 if maybenull then
1336 self.add("if (unlikely({recv} == NULL)) \{")
1337 self.add_abort("Receiver is null")
1338 self.add("\}")
1339 end
1340 end
1341
1342 # Names handling
1343
1344 private var names = new HashSet[String]
1345 private var last: Int = 0
1346
1347 # Return a new name based on `s` and unique in the visitor
1348 fun get_name(s: String): String
1349 do
1350 if not self.names.has(s) then
1351 self.names.add(s)
1352 return s
1353 end
1354 var i = self.last + 1
1355 loop
1356 var s2 = s + i.to_s
1357 if not self.names.has(s2) then
1358 self.last = i
1359 self.names.add(s2)
1360 return s2
1361 end
1362 i = i + 1
1363 end
1364 end
1365
1366 # Return an unique and stable identifier associated with an escapemark
1367 fun escapemark_name(e: nullable EscapeMark): String
1368 do
1369 assert e != null
1370 if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
1371 var name = e.name
1372 if name == null then name = "label"
1373 name = get_name(name)
1374 frame.escapemark_names[e] = name
1375 return name
1376 end
1377
1378 # Insert a C label for associated with an escapemark
1379 fun add_escape_label(e: nullable EscapeMark)
1380 do
1381 if e == null then return
1382 if e.escapes.is_empty then return
1383 add("BREAK_{escapemark_name(e)}: (void)0;")
1384 end
1385
1386 # Return a "const char*" variable associated to the classname of the dynamic type of an object
1387 # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
1388 fun class_name_string(value: RuntimeVariable): String is abstract
1389
1390 # Variables handling
1391
1392 protected var variables = new HashMap[Variable, RuntimeVariable]
1393
1394 # Return the local runtime_variable associated to a Nit local variable
1395 fun variable(variable: Variable): RuntimeVariable
1396 do
1397 if self.variables.has_key(variable) then
1398 return self.variables[variable]
1399 else
1400 var name = self.get_name("var_{variable.name}")
1401 var mtype = variable.declared_type.as(not null)
1402 mtype = self.anchor(mtype)
1403 var res = new RuntimeVariable(name, mtype, mtype)
1404 self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
1405 self.variables[variable] = res
1406 return res
1407 end
1408 end
1409
1410 # Return a new uninitialized local runtime_variable
1411 fun new_var(mtype: MType): RuntimeVariable
1412 do
1413 mtype = self.anchor(mtype)
1414 var name = self.get_name("var")
1415 var res = new RuntimeVariable(name, mtype, mtype)
1416 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1417 return res
1418 end
1419
1420 # The difference with `new_var` is the C static type of the local variable
1421 fun new_var_extern(mtype: MType): RuntimeVariable
1422 do
1423 mtype = self.anchor(mtype)
1424 var name = self.get_name("var")
1425 var res = new RuntimeVariable(name, mtype, mtype)
1426 self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
1427 return res
1428 end
1429
1430 # Return a new uninitialized named runtime_variable
1431 fun new_named_var(mtype: MType, name: String): RuntimeVariable
1432 do
1433 mtype = self.anchor(mtype)
1434 var res = new RuntimeVariable(name, mtype, mtype)
1435 self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
1436 return res
1437 end
1438
1439 # Correctly assign a left and a right value
1440 # Boxing and unboxing is performed if required
1441 fun assign(left, right: RuntimeVariable)
1442 do
1443 right = self.autobox(right, left.mtype)
1444 self.add("{left} = {right};")
1445 end
1446
1447 # Generate instances
1448
1449 # Generate a alloc-instance + init-attributes
1450 fun init_instance(mtype: MClassType): RuntimeVariable is abstract
1451
1452 # Allocate and init attributes of an instance of a standard or extern class
1453 #
1454 # Does not support universals and the pseudo-internal `NativeArray` class.
1455 fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
1456 do
1457 var recv
1458 var ctype = mtype.ctype
1459 assert mtype.mclass.name != "NativeArray"
1460 if not mtype.is_c_primitive then
1461 recv = init_instance(mtype)
1462 else if ctype == "char*" then
1463 recv = new_expr("NULL/*special!*/", mtype)
1464 else
1465 recv = new_expr("({ctype})0/*special!*/", mtype)
1466 end
1467 return recv
1468 end
1469
1470 # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
1471 fun set_finalizer(recv: RuntimeVariable)
1472 do
1473 var mtype = recv.mtype
1474 var finalizable_type = compiler.mainmodule.finalizable_type
1475 if finalizable_type != null and not mtype.need_anchor and
1476 mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
1477 add "gc_register_finalizer({recv});"
1478 end
1479 end
1480
1481 # The currently processed module
1482 #
1483 # alias for `compiler.mainmodule`
1484 fun mmodule: MModule do return compiler.mainmodule
1485
1486 # Generate an integer value
1487 fun int_instance(value: Int): RuntimeVariable
1488 do
1489 var t = mmodule.int_type
1490 var res = new RuntimeVariable("{value.to_s}l", t, t)
1491 return res
1492 end
1493
1494 # Generate a byte value
1495 fun byte_instance(value: Byte): RuntimeVariable
1496 do
1497 var t = mmodule.byte_type
1498 var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
1499 return res
1500 end
1501
1502 # Generate an int8 value
1503 fun int8_instance(value: Int8): RuntimeVariable
1504 do
1505 var t = mmodule.int8_type
1506 var res = new RuntimeVariable("((int8_t){value.to_s})", t, t)
1507 return res
1508 end
1509
1510 # Generate an int16 value
1511 fun int16_instance(value: Int16): RuntimeVariable
1512 do
1513 var t = mmodule.int16_type
1514 var res = new RuntimeVariable("((int16_t){value.to_s})", t, t)
1515 return res
1516 end
1517
1518 # Generate a uint16 value
1519 fun uint16_instance(value: UInt16): RuntimeVariable
1520 do
1521 var t = mmodule.uint16_type
1522 var res = new RuntimeVariable("((uint16_t){value.to_s})", t, t)
1523 return res
1524 end
1525
1526 # Generate an int32 value
1527 fun int32_instance(value: Int32): RuntimeVariable
1528 do
1529 var t = mmodule.int32_type
1530 var res = new RuntimeVariable("((int32_t){value.to_s})", t, t)
1531 return res
1532 end
1533
1534 # Generate a uint32 value
1535 fun uint32_instance(value: UInt32): RuntimeVariable
1536 do
1537 var t = mmodule.uint32_type
1538 var res = new RuntimeVariable("((uint32_t){value.to_s})", t, t)
1539 return res
1540 end
1541
1542 # Generate a char value
1543 fun char_instance(value: Char): RuntimeVariable
1544 do
1545 var t = mmodule.char_type
1546
1547 if value.code_point < 128 then
1548 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
1549 else
1550 return new RuntimeVariable("{value.code_point}", t, t)
1551 end
1552 end
1553
1554 # Generate a float value
1555 #
1556 # FIXME pass a Float, not a string
1557 fun float_instance(value: String): RuntimeVariable
1558 do
1559 var t = mmodule.float_type
1560 var res = new RuntimeVariable("{value}", t, t)
1561 return res
1562 end
1563
1564 # Generate an integer value
1565 fun bool_instance(value: Bool): RuntimeVariable
1566 do
1567 var s = if value then "1" else "0"
1568 var res = new RuntimeVariable(s, bool_type, bool_type)
1569 return res
1570 end
1571
1572 # Generate the `null` value
1573 fun null_instance: RuntimeVariable
1574 do
1575 var t = compiler.mainmodule.model.null_type
1576 var res = new RuntimeVariable("((val*)NULL)", t, t)
1577 return res
1578 end
1579
1580 # Generate a string value
1581 fun string_instance(string: String): RuntimeVariable
1582 do
1583 var mtype = mmodule.string_type
1584 var name = self.get_name("varonce")
1585 self.add_decl("static {mtype.ctype} {name};")
1586 var res = self.new_var(mtype)
1587 self.add("if (likely({name}!=NULL)) \{")
1588 self.add("{res} = {name};")
1589 self.add("\} else \{")
1590 var native_mtype = mmodule.native_string_type
1591 var nat = self.new_var(native_mtype)
1592 self.add("{nat} = \"{string.escape_to_c}\";")
1593 var bytelen = self.int_instance(string.bytelen)
1594 var unilen = self.int_instance(string.length)
1595 self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
1596 self.add("{name} = {res};")
1597 self.add("\}")
1598 return res
1599 end
1600
1601 fun value_instance(object: Object): RuntimeVariable
1602 do
1603 if object isa Int then
1604 return int_instance(object)
1605 else if object isa Bool then
1606 return bool_instance(object)
1607 else if object isa String then
1608 return string_instance(object)
1609 else
1610 abort
1611 end
1612 end
1613
1614 # Generate an array value
1615 fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1616
1617 # Get an instance of a array for a vararg
1618 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
1619
1620 # Code generation
1621
1622 # Add a line in the main part of the generated C
1623 fun add(s: String) do self.writer.lines.add(s)
1624
1625 # Add a line in the
1626 # (used for local or global declaration)
1627 fun add_decl(s: String) do self.writer.decl_lines.add(s)
1628
1629 # Request the presence of a global declaration
1630 fun require_declaration(key: String)
1631 do
1632 var reqs = self.writer.file.required_declarations
1633 if reqs.has(key) then return
1634 reqs.add(key)
1635 var node = current_node
1636 if node != null then compiler.requirers_of_declarations[key] = node
1637 end
1638
1639 # Add a declaration in the local-header
1640 # The declaration is ensured to be present once
1641 fun declare_once(s: String)
1642 do
1643 self.compiler.provide_declaration(s, s)
1644 self.require_declaration(s)
1645 end
1646
1647 # Look for a needed .h and .c file for a given module
1648 # This is used for the legacy FFI
1649 fun add_extern(mmodule: MModule)
1650 do
1651 var file = mmodule.filepath
1652 file = file.strip_extension(".nit")
1653 var tryfile = file + ".nit.h"
1654 if tryfile.file_exists then
1655 self.declare_once("#include \"{tryfile.basename}\"")
1656 self.compiler.files_to_copy.add(tryfile)
1657 end
1658 tryfile = file + "_nit.h"
1659 if tryfile.file_exists then
1660 self.declare_once("#include \"{tryfile.basename}\"")
1661 self.compiler.files_to_copy.add(tryfile)
1662 end
1663
1664 if self.compiler.seen_extern.has(file) then return
1665 self.compiler.seen_extern.add(file)
1666 tryfile = file + ".nit.c"
1667 if not tryfile.file_exists then
1668 tryfile = file + "_nit.c"
1669 if not tryfile.file_exists then return
1670 end
1671 var f = new ExternCFile(tryfile.basename, "")
1672 self.compiler.extern_bodies.add(f)
1673 self.compiler.files_to_copy.add(tryfile)
1674 end
1675
1676 # Return a new local runtime_variable initialized with the C expression `cexpr`.
1677 fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
1678 do
1679 var res = new_var(mtype)
1680 self.add("{res} = {cexpr};")
1681 return res
1682 end
1683
1684 # Generate generic abort
1685 # used by aborts, asserts, casts, etc.
1686 fun add_abort(message: String)
1687 do
1688 self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
1689 add_raw_abort
1690 end
1691
1692 fun add_raw_abort
1693 do
1694 if self.current_node != null and self.current_node.location.file != null and
1695 self.current_node.location.file.mmodule != null then
1696 var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
1697 self.require_declaration(f)
1698 self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
1699 else
1700 self.add("PRINT_ERROR(\"\\n\");")
1701 end
1702 self.add("fatal_exit(1);")
1703 end
1704
1705 # Add a dynamic cast
1706 fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
1707 do
1708 var res = self.type_test(value, mtype, tag)
1709 self.add("if (unlikely(!{res})) \{")
1710 var cn = self.class_name_string(value)
1711 self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
1712 self.add_raw_abort
1713 self.add("\}")
1714 end
1715
1716 # Generate a return with the value `s`
1717 fun ret(s: RuntimeVariable)
1718 do
1719 self.assign(self.frame.returnvar.as(not null), s)
1720 self.add("goto {self.frame.returnlabel.as(not null)};")
1721 end
1722
1723 # Compile a statement (if any)
1724 fun stmt(nexpr: nullable AExpr)
1725 do
1726 if nexpr == null then return
1727 if nexpr.is_broken then
1728 # Untyped expression.
1729 # Might mean dead code or invalid code
1730 # so aborts
1731 add_abort("FATAL: bad statement executed.")
1732 return
1733 end
1734
1735 var narray = nexpr.comprehension
1736 if narray != null then
1737 var recv = frame.comprehension.as(not null)
1738 var val = expr(nexpr, narray.element_mtype)
1739 compile_callsite(narray.push_callsite.as(not null), [recv, val])
1740 return
1741 end
1742
1743 var old = self.current_node
1744 self.current_node = nexpr
1745 nexpr.stmt(self)
1746 self.current_node = old
1747 end
1748
1749 # Compile an expression an return its result
1750 # `mtype` is the expected return type, pass null if no specific type is expected.
1751 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
1752 do
1753 var old = self.current_node
1754 self.current_node = nexpr
1755
1756 var res = null
1757 if nexpr.mtype != null then
1758 res = nexpr.expr(self)
1759 end
1760
1761 if res == null then
1762 # Untyped expression.
1763 # Might mean dead code or invalid code.
1764 # so aborts
1765 add_abort("FATAL: bad expression executed.")
1766 # and return a placebo result to please the C compiler
1767 if mtype == null then mtype = compiler.mainmodule.object_type
1768 res = new_var(mtype)
1769
1770 self.current_node = old
1771 return res
1772 end
1773
1774 if mtype != null then
1775 mtype = self.anchor(mtype)
1776 res = self.autobox(res, mtype)
1777 end
1778 res = autoadapt(res, nexpr.mtype.as(not null))
1779 var implicit_cast_to = nexpr.implicit_cast_to
1780 if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
1781 add_cast(res, implicit_cast_to, "auto")
1782 res = autoadapt(res, implicit_cast_to)
1783 end
1784 self.current_node = old
1785 return res
1786 end
1787
1788 # Alias for `self.expr(nexpr, self.bool_type)`
1789 fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
1790
1791 # Safely show a debug message on the current node and repeat the message in the C code as a comment
1792 fun debug(message: String)
1793 do
1794 var node = self.current_node
1795 if node == null then
1796 print "?: {message}"
1797 else
1798 node.debug(message)
1799 end
1800 self.add("/* DEBUG: {message} */")
1801 end
1802 end
1803
1804 # A C function associated to a Nit method
1805 # Because of customization, a given Nit method can be compiler more that once
1806 abstract class AbstractRuntimeFunction
1807
1808 type COMPILER: AbstractCompiler
1809 type VISITOR: AbstractCompilerVisitor
1810
1811 # The associated Nit method
1812 var mmethoddef: MMethodDef
1813
1814 # The mangled c name of the runtime_function
1815 # Subclasses should redefine `build_c_name` instead
1816 fun c_name: String
1817 do
1818 var res = self.c_name_cache
1819 if res != null then return res
1820 res = self.build_c_name
1821 self.c_name_cache = res
1822 return res
1823 end
1824
1825 # Non cached version of `c_name`
1826 protected fun build_c_name: String is abstract
1827
1828 protected var c_name_cache: nullable String = null is writable
1829
1830 # Implements a call of the runtime_function
1831 # May inline the body or generate a C function call
1832 fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
1833
1834 # Generate the code for the `AbstractRuntimeFunction`
1835 # Warning: compile more than once compilation makes CC unhappy
1836 fun compile_to_c(compiler: COMPILER) is abstract
1837 end
1838
1839 # A runtime variable hold a runtime value in C.
1840 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1841 #
1842 # 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.
1843 class RuntimeVariable
1844 # The name of the variable in the C code
1845 var name: String
1846
1847 # The static type of the variable (as declard in C)
1848 var mtype: MType
1849
1850 # The current casted type of the variable (as known in Nit)
1851 var mcasttype: MType is writable
1852
1853 # If the variable exaclty a mcasttype?
1854 # false (usual value) means that the variable is a mcasttype or a subtype.
1855 var is_exact: Bool = false is writable
1856
1857 init
1858 do
1859 assert not mtype.need_anchor
1860 assert not mcasttype.need_anchor
1861 end
1862
1863 redef fun to_s do return name
1864
1865 redef fun inspect
1866 do
1867 var exact_str
1868 if self.is_exact then
1869 exact_str = " exact"
1870 else
1871 exact_str = ""
1872 end
1873 var type_str
1874 if self.mtype == self.mcasttype then
1875 type_str = "{mtype}{exact_str}"
1876 else
1877 type_str = "{mtype}({mcasttype}{exact_str})"
1878 end
1879 return "<{name}:{type_str}>"
1880 end
1881 end
1882
1883 # The static context of a visited property in a `AbstractCompilerVisitor`
1884 class StaticFrame
1885
1886 type VISITOR: AbstractCompilerVisitor
1887
1888 # The associated visitor
1889 var visitor: VISITOR
1890
1891 # The executed property.
1892 # A Method in case of a call, an attribute in case of a default initialization.
1893 var mpropdef: MPropDef
1894
1895 # The static type of the receiver
1896 var receiver: MClassType
1897
1898 # Arguments of the method (the first is the receiver)
1899 var arguments: Array[RuntimeVariable]
1900
1901 # The runtime_variable associated to the return (in a function)
1902 var returnvar: nullable RuntimeVariable = null is writable
1903
1904 # The label at the end of the property
1905 var returnlabel: nullable String = null is writable
1906
1907 # Labels associated to a each escapemarks.
1908 # Because of inlinings, escape-marks must be associated to their context (the frame)
1909 private var escapemark_names = new HashMap[EscapeMark, String]
1910
1911 # The array comprehension currently filled, if any
1912 private var comprehension: nullable RuntimeVariable = null
1913 end
1914
1915 redef class MType
1916 # Return the C type associated to a given Nit static type
1917 fun ctype: String do return "val*"
1918
1919 # C type outside of the compiler code and in boxes
1920 fun ctype_extern: String do return "val*"
1921
1922 # Short name of the `ctype` to use in unions
1923 fun ctypename: String do return "val"
1924
1925 # Is the associated C type a primitive one?
1926 #
1927 # ENSURE `result == (ctype != "val*")`
1928 fun is_c_primitive: Bool do return false
1929 end
1930
1931 redef class MClassType
1932
1933 redef var ctype is lazy do
1934 if mclass.name == "Int" then
1935 return "long"
1936 else if mclass.name == "Bool" then
1937 return "short int"
1938 else if mclass.name == "Char" then
1939 return "uint32_t"
1940 else if mclass.name == "Float" then
1941 return "double"
1942 else if mclass.name == "Int8" then
1943 return "int8_t"
1944 else if mclass.name == "Byte" then
1945 return "unsigned char"
1946 else if mclass.name == "Int16" then
1947 return "int16_t"
1948 else if mclass.name == "UInt16" then
1949 return "uint16_t"
1950 else if mclass.name == "Int32" then
1951 return "int32_t"
1952 else if mclass.name == "UInt32" then
1953 return "uint32_t"
1954 else if mclass.name == "NativeString" then
1955 return "char*"
1956 else if mclass.name == "NativeArray" then
1957 return "val*"
1958 else
1959 return "val*"
1960 end
1961 end
1962
1963 redef var is_c_primitive is lazy do return ctype != "val*"
1964
1965 redef fun ctype_extern: String
1966 do
1967 if mclass.kind == extern_kind then
1968 return "void*"
1969 else
1970 return ctype
1971 end
1972 end
1973
1974 redef fun ctypename: String
1975 do
1976 if mclass.name == "Int" then
1977 return "l"
1978 else if mclass.name == "Bool" then
1979 return "s"
1980 else if mclass.name == "Char" then
1981 return "c"
1982 else if mclass.name == "Float" then
1983 return "d"
1984 else if mclass.name == "Int8" then
1985 return "i8"
1986 else if mclass.name == "Byte" then
1987 return "b"
1988 else if mclass.name == "Int16" then
1989 return "i16"
1990 else if mclass.name == "UInt16" then
1991 return "u16"
1992 else if mclass.name == "Int32" then
1993 return "i32"
1994 else if mclass.name == "UInt32" then
1995 return "u32"
1996 else if mclass.name == "NativeString" then
1997 return "str"
1998 else if mclass.name == "NativeArray" then
1999 #return "{self.arguments.first.ctype}*"
2000 return "val"
2001 else
2002 return "val"
2003 end
2004 end
2005 end
2006
2007 redef class MPropDef
2008 type VISITOR: AbstractCompilerVisitor
2009 end
2010
2011 redef class MMethodDef
2012 # Can the body be inlined?
2013 fun can_inline(v: VISITOR): Bool
2014 do
2015 if is_abstract then return true
2016 if constant_value != null then return true
2017 var modelbuilder = v.compiler.modelbuilder
2018 var node = modelbuilder.mpropdef2node(self)
2019 if node isa APropdef then
2020 return node.can_inline
2021 else if node isa AClassdef then
2022 # Automatic free init is always inlined since it is empty or contains only attribtes assigments
2023 return true
2024 else
2025 abort
2026 end
2027 end
2028
2029 # Inline the body in another visitor
2030 fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
2031 do
2032 var modelbuilder = v.compiler.modelbuilder
2033 var val = constant_value
2034 var node = modelbuilder.mpropdef2node(self)
2035
2036 if is_abstract then
2037 var cn = v.class_name_string(arguments.first)
2038 v.current_node = node
2039 v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
2040 v.add_raw_abort
2041 return null
2042 end
2043
2044 if node isa APropdef then
2045 var oldnode = v.current_node
2046 v.current_node = node
2047 self.compile_parameter_check(v, arguments)
2048 node.compile_to_c(v, self, arguments)
2049 v.current_node = oldnode
2050 else if node isa AClassdef then
2051 var oldnode = v.current_node
2052 v.current_node = node
2053 self.compile_parameter_check(v, arguments)
2054 node.compile_to_c(v, self, arguments)
2055 v.current_node = oldnode
2056 else if val != null then
2057 v.ret(v.value_instance(val))
2058 else
2059 abort
2060 end
2061 return null
2062 end
2063
2064 # Generate type checks in the C code to check covariant parameters
2065 fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
2066 do
2067 if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
2068
2069 var msignature = self.msignature.as(not null)
2070
2071 for i in [0..msignature.arity[ do
2072 var mp = msignature.mparameters[i]
2073 # skip test for vararg since the array is instantiated with the correct polymorphic type
2074 if mp.is_vararg then continue
2075
2076 # skip if the cast is not required
2077 var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
2078 if not origmtype.need_anchor then continue
2079
2080 # get the parameter type
2081 var mtype = mp.mtype
2082
2083 # generate the cast
2084 # note that v decides if and how to implements the cast
2085 v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
2086 v.add_cast(arguments[i+1], mtype, "covariance")
2087 end
2088 end
2089 end
2090
2091 # Node visit
2092
2093 redef class APropdef
2094 fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
2095 do
2096 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
2097 debug("Not yet implemented")
2098 end
2099
2100 fun can_inline: Bool do return true
2101 end
2102
2103 redef class AMethPropdef
2104 redef fun compile_to_c(v, mpropdef, arguments)
2105 do
2106 # Call the implicit super-init
2107 var auto_super_inits = self.auto_super_inits
2108 if auto_super_inits != null then
2109 var args = [arguments.first]
2110 for auto_super_init in auto_super_inits do
2111 assert auto_super_init.mproperty != mpropdef.mproperty
2112 args.clear
2113 for i in [0..auto_super_init.msignature.arity+1[ do
2114 args.add(arguments[i])
2115 end
2116 assert auto_super_init.mproperty != mpropdef.mproperty
2117 v.compile_callsite(auto_super_init, args)
2118 end
2119 end
2120 if auto_super_call then
2121 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
2122 end
2123
2124 # Try special compilation
2125 if mpropdef.is_intern then
2126 if compile_intern_to_c(v, mpropdef, arguments) then return
2127 end
2128 if mpropdef.is_extern then
2129 if mpropdef.mproperty.is_init then
2130 if compile_externinit_to_c(v, mpropdef, arguments) then return
2131 else
2132 if compile_externmeth_to_c(v, mpropdef, arguments) then return
2133 end
2134 end
2135
2136 # Compile block if any
2137 var n_block = n_block
2138 if n_block != null then
2139 for i in [0..mpropdef.msignature.arity[ do
2140 var variable = self.n_signature.n_params[i].variable.as(not null)
2141 v.assign(v.variable(variable), arguments[i+1])
2142 end
2143 v.stmt(n_block)
2144 return
2145 end
2146
2147 # We have a problem
2148 var cn = v.class_name_string(arguments.first)
2149 v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
2150 v.add_raw_abort
2151 end
2152
2153 redef fun can_inline
2154 do
2155 if self.auto_super_inits != null then return false
2156 var nblock = self.n_block
2157 if nblock == null then return true
2158 if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
2159 if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
2160 return false
2161 end
2162
2163 fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2164 do
2165 var pname = mpropdef.mproperty.name
2166 var cname = mpropdef.mclassdef.mclass.name
2167 var ret = mpropdef.msignature.return_mtype
2168 if ret != null then
2169 ret = v.resolve_for(ret, arguments.first)
2170 end
2171 if pname != "==" and pname != "!=" then
2172 v.adapt_signature(mpropdef, arguments)
2173 v.unbox_signature_extern(mpropdef, arguments)
2174 end
2175 if cname == "Int" then
2176 if pname == "output" then
2177 v.add("printf(\"%ld\\n\", {arguments.first});")
2178 return true
2179 else if pname == "object_id" then
2180 v.ret(arguments.first)
2181 return true
2182 else if pname == "+" then
2183 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2184 return true
2185 else if pname == "-" then
2186 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2187 return true
2188 else if pname == "unary -" then
2189 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2190 return true
2191 else if pname == "unary +" then
2192 v.ret(arguments[0])
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 == "/" then
2198 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2199 return true
2200 else if pname == "%" then
2201 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2202 return true
2203 else if pname == "==" then
2204 v.ret(v.equal_test(arguments[0], arguments[1]))
2205 return true
2206 else if pname == "!=" then
2207 var res = v.equal_test(arguments[0], arguments[1])
2208 v.ret(v.new_expr("!{res}", ret.as(not null)))
2209 return true
2210 else if pname == "<" then
2211 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2212 return true
2213 else if pname == ">" then
2214 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2215 return true
2216 else if pname == "<=" then
2217 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", 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 == "to_i8" then
2223 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2224 return true
2225 else if pname == "to_i16" then
2226 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2227 return true
2228 else if pname == "to_u16" then
2229 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2230 return true
2231 else if pname == "to_i32" then
2232 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2233 return true
2234 else if pname == "to_u32" then
2235 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2236 return true
2237 else if pname == "to_f" then
2238 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2239 return true
2240 else if pname == "to_b" then
2241 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2242 return true
2243 else if pname == "code_point" then
2244 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2245 return true
2246 else if pname == "&" then
2247 v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
2248 return true
2249 else if pname == "|" then
2250 v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
2251 return true
2252 else if pname == ">>" then
2253 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", 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 end
2259 else if cname == "Char" then
2260 if pname == "object_id" then
2261 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2262 return true
2263 else if pname == "successor" then
2264 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2265 return true
2266 else if pname == "predecessor" then
2267 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2268 return true
2269 else if pname == "==" then
2270 v.ret(v.equal_test(arguments[0], arguments[1]))
2271 return true
2272 else if pname == "!=" then
2273 var res = v.equal_test(arguments[0], arguments[1])
2274 v.ret(v.new_expr("!{res}", ret.as(not null)))
2275 return true
2276 else if pname == "<" then
2277 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2278 return true
2279 else if pname == ">" then
2280 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2281 return true
2282 else if pname == "<=" then
2283 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", 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 == "to_i" then
2289 v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
2290 return true
2291 else if pname == "code_point" then
2292 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2293 return true
2294 end
2295 else if cname == "Byte" then
2296 if pname == "output" then
2297 v.add("printf(\"%x\\n\", {arguments.first});")
2298 return true
2299 else if pname == "object_id" then
2300 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2301 return true
2302 else if pname == "+" then
2303 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2304 return true
2305 else if pname == "-" then
2306 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2307 return true
2308 else if pname == "unary -" then
2309 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2310 return true
2311 else if pname == "unary +" then
2312 v.ret(arguments[0])
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 == "/" then
2318 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2319 return true
2320 else if pname == "%" then
2321 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
2322 return true
2323 else if pname == "==" then
2324 v.ret(v.equal_test(arguments[0], arguments[1]))
2325 return true
2326 else if pname == "!=" then
2327 var res = v.equal_test(arguments[0], arguments[1])
2328 v.ret(v.new_expr("!{res}", ret.as(not null)))
2329 return true
2330 else if pname == "<" then
2331 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2332 return true
2333 else if pname == ">" then
2334 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2335 return true
2336 else if pname == "<=" then
2337 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", 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 == "to_i" then
2352 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2353 return true
2354 else if pname == "to_f" then
2355 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
2356 return true
2357 else if pname == "to_i8" then
2358 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2359 return true
2360 else if pname == "to_i16" then
2361 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2362 return true
2363 else if pname == "to_u16" then
2364 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2365 return true
2366 else if pname == "to_i32" then
2367 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2368 return true
2369 else if pname == "to_u32" then
2370 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2371 return true
2372 else if pname == "ascii" then
2373 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2374 return true
2375 end
2376 else if cname == "Bool" then
2377 if pname == "output" then
2378 v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
2379 return true
2380 else if pname == "object_id" then
2381 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2382 return true
2383 else if pname == "==" then
2384 v.ret(v.equal_test(arguments[0], arguments[1]))
2385 return true
2386 else if pname == "!=" then
2387 var res = v.equal_test(arguments[0], arguments[1])
2388 v.ret(v.new_expr("!{res}", ret.as(not null)))
2389 return true
2390 end
2391 else if cname == "Float" then
2392 if pname == "output" then
2393 v.add("printf(\"%f\\n\", {arguments.first});")
2394 return true
2395 else if pname == "object_id" then
2396 v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
2397 return true
2398 else if pname == "+" then
2399 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2400 return true
2401 else if pname == "-" then
2402 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2403 return true
2404 else if pname == "unary -" then
2405 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2406 return true
2407 else if pname == "unary +" then
2408 v.ret(arguments[0])
2409 return true
2410 else if pname == "succ" then
2411 v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
2412 return true
2413 else if pname == "prec" then
2414 v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
2415 return true
2416 else if pname == "*" then
2417 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
2418 return true
2419 else if pname == "/" then
2420 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
2421 return true
2422 else if pname == "==" then
2423 v.ret(v.equal_test(arguments[0], arguments[1]))
2424 return true
2425 else if pname == "!=" then
2426 var res = v.equal_test(arguments[0], arguments[1])
2427 v.ret(v.new_expr("!{res}", ret.as(not null)))
2428 return true
2429 else if pname == "<" then
2430 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
2431 return true
2432 else if pname == ">" then
2433 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
2434 return true
2435 else if pname == "<=" then
2436 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", 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 == "to_i" then
2442 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2443 return true
2444 else if pname == "to_b" then
2445 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2446 return true
2447 else if pname == "to_i8" then
2448 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2449 return true
2450 else if pname == "to_i16" then
2451 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2452 return true
2453 else if pname == "to_u16" then
2454 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2455 return true
2456 else if pname == "to_i32" then
2457 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2458 return true
2459 else if pname == "to_u32" then
2460 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2461 return true
2462 end
2463 else if cname == "NativeString" then
2464 if pname == "[]" then
2465 v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
2466 return true
2467 else if pname == "[]=" then
2468 v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
2469 return true
2470 else if pname == "copy_to" then
2471 v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
2472 return true
2473 else if pname == "atoi" then
2474 v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
2475 return true
2476 else if pname == "fast_cstring" then
2477 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
2478 return true
2479 else if pname == "==" then
2480 v.ret(v.equal_test(arguments[0], arguments[1]))
2481 return true
2482 else if pname == "!=" then
2483 var res = v.equal_test(arguments[0], arguments[1])
2484 v.ret(v.new_expr("!{res}", ret.as(not null)))
2485 return true
2486 else if pname == "new" then
2487 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2488 return true
2489 else if pname == "fetch_4_chars" then
2490 v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
2491 return true
2492 else if pname == "fetch_4_hchars" then
2493 v.ret(v.new_expr("(long)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
2494 return true
2495 end
2496 else if cname == "NativeArray" then
2497 return v.native_array_def(pname, ret, arguments)
2498 else if cname == "Int8" then
2499 if pname == "output" then
2500 v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
2501 return true
2502 else if pname == "object_id" then
2503 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
2509 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2510 return true
2511 else if pname == "unary -" then
2512 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2513 return true
2514 else if pname == "unary +" then
2515 v.ret(arguments[0])
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.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2531 return true
2532 else if pname == "==" then
2533 v.ret(v.equal_test(arguments[0], arguments[1]))
2534 return true
2535 else if pname == "!=" then
2536 var res = v.equal_test(arguments[0], arguments[1])
2537 v.ret(v.new_expr("!{res}", 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 == ">=" then
2549 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2550 return true
2551 else if pname == "to_i" then
2552 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2553 return true
2554 else if pname == "to_b" then
2555 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2556 return true
2557 else if pname == "to_i16" then
2558 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2559 return true
2560 else if pname == "to_u16" then
2561 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2562 return true
2563 else if pname == "to_i32" then
2564 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2565 return true
2566 else if pname == "to_u32" then
2567 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2568 return true
2569 else if pname == "to_f" then
2570 v.ret(v.new_expr("(double){arguments[0]}", 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 == "^" then
2579 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2580 return true
2581 else if pname == "unary ~" then
2582 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2583 return true
2584 end
2585 else if cname == "Int16" then
2586 if pname == "output" then
2587 v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
2588 return true
2589 else if pname == "object_id" then
2590 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
2596 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2597 return true
2598 else if pname == "unary -" then
2599 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2600 return true
2601 else if pname == "unary +" then
2602 v.ret(arguments[0])
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.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2618 return true
2619 else if pname == "==" then
2620 v.ret(v.equal_test(arguments[0], arguments[1]))
2621 return true
2622 else if pname == "!=" then
2623 var res = v.equal_test(arguments[0], arguments[1])
2624 v.ret(v.new_expr("!{res}", 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 == ">=" then
2636 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2637 return true
2638 else if pname == "to_i" then
2639 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2640 return true
2641 else if pname == "to_b" then
2642 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2643 return true
2644 else if pname == "to_i8" then
2645 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2646 return true
2647 else if pname == "to_u16" then
2648 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2649 return true
2650 else if pname == "to_i32" then
2651 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2652 return true
2653 else if pname == "to_u32" then
2654 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2655 return true
2656 else if pname == "to_f" then
2657 v.ret(v.new_expr("(double){arguments[0]}", 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 == "^" then
2666 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2667 return true
2668 else if pname == "unary ~" then
2669 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2670 return true
2671 end
2672 else if cname == "UInt16" then
2673 if pname == "output" then
2674 v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
2675 return true
2676 else if pname == "object_id" then
2677 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
2683 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2684 return true
2685 else if pname == "unary -" then
2686 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2687 return true
2688 else if pname == "unary +" then
2689 v.ret(arguments[0])
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.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2705 return true
2706 else if pname == "==" then
2707 v.ret(v.equal_test(arguments[0], arguments[1]))
2708 return true
2709 else if pname == "!=" then
2710 var res = v.equal_test(arguments[0], arguments[1])
2711 v.ret(v.new_expr("!{res}", 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 == ">=" then
2723 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2724 return true
2725 else if pname == "to_i" then
2726 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2727 return true
2728 else if pname == "to_b" then
2729 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2730 return true
2731 else if pname == "to_i8" then
2732 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2733 return true
2734 else if pname == "to_i16" then
2735 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2736 return true
2737 else if pname == "to_i32" then
2738 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2739 return true
2740 else if pname == "to_u32" then
2741 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2742 return true
2743 else if pname == "to_f" then
2744 v.ret(v.new_expr("(double){arguments[0]}", 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 == "^" then
2753 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2754 return true
2755 else if pname == "unary ~" then
2756 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2757 return true
2758 end
2759 else if cname == "Int32" then
2760 if pname == "output" then
2761 v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
2762 return true
2763 else if pname == "object_id" then
2764 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
2770 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2771 return true
2772 else if pname == "unary -" then
2773 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2774 return true
2775 else if pname == "unary +" then
2776 v.ret(arguments[0])
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.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2792 return true
2793 else if pname == "==" then
2794 v.ret(v.equal_test(arguments[0], arguments[1]))
2795 return true
2796 else if pname == "!=" then
2797 var res = v.equal_test(arguments[0], arguments[1])
2798 v.ret(v.new_expr("!{res}", 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 == ">=" then
2810 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2811 return true
2812 else if pname == "to_i" then
2813 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2814 return true
2815 else if pname == "to_b" then
2816 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2817 return true
2818 else if pname == "to_i8" then
2819 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2820 return true
2821 else if pname == "to_i16" then
2822 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2823 return true
2824 else if pname == "to_u16" then
2825 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2826 return true
2827 else if pname == "to_u32" then
2828 v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
2829 return true
2830 else if pname == "to_f" then
2831 v.ret(v.new_expr("(double){arguments[0]}", 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 == "^" then
2840 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2841 return true
2842 else if pname == "unary ~" then
2843 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2844 return true
2845 end
2846 else if cname == "UInt32" then
2847 if pname == "output" then
2848 v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
2849 return true
2850 else if pname == "object_id" then
2851 v.ret(v.new_expr("(long){arguments.first}", 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 == "-" then
2857 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
2858 return true
2859 else if pname == "unary -" then
2860 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
2861 return true
2862 else if pname == "unary +" then
2863 v.ret(arguments[0])
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.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
2879 return true
2880 else if pname == "==" then
2881 v.ret(v.equal_test(arguments[0], arguments[1]))
2882 return true
2883 else if pname == "!=" then
2884 var res = v.equal_test(arguments[0], arguments[1])
2885 v.ret(v.new_expr("!{res}", 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 == ">=" then
2897 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
2898 return true
2899 else if pname == "to_i" then
2900 v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
2901 return true
2902 else if pname == "to_b" then
2903 v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
2904 return true
2905 else if pname == "to_i8" then
2906 v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
2907 return true
2908 else if pname == "to_i16" then
2909 v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
2910 return true
2911 else if pname == "to_u16" then
2912 v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
2913 return true
2914 else if pname == "to_i32" then
2915 v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
2916 return true
2917 else if pname == "to_f" then
2918 v.ret(v.new_expr("(double){arguments[0]}", 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 == "^" then
2927 v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
2928 return true
2929 else if pname == "unary ~" then
2930 v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
2931 return true
2932 end
2933 end
2934 if pname == "exit" then
2935 v.add("exit({arguments[1]});")
2936 return true
2937 else if pname == "sys" then
2938 v.ret(v.new_expr("glob_sys", ret.as(not null)))
2939 return true
2940 else if pname == "calloc_string" then
2941 v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
2942 return true
2943 else if pname == "calloc_array" then
2944 v.calloc_array(ret.as(not null), arguments)
2945 return true
2946 else if pname == "object_id" then
2947 v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
2948 return true
2949 else if pname == "is_same_type" then
2950 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
2951 return true
2952 else if pname == "is_same_instance" then
2953 v.ret(v.equal_test(arguments[0], arguments[1]))
2954 return true
2955 else if pname == "output_class_name" then
2956 var nat = v.class_name_string(arguments.first)
2957 v.add("printf(\"%s\\n\", {nat});")
2958 return true
2959 else if pname == "native_class_name" then
2960 var nat = v.class_name_string(arguments.first)
2961 v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
2962 return true
2963 else if pname == "force_garbage_collection" then
2964 v.add("nit_gcollect();")
2965 return true
2966 else if pname == "native_argc" then
2967 v.ret(v.new_expr("glob_argc", ret.as(not null)))
2968 return true
2969 else if pname == "native_argv" then
2970 v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
2971 return true
2972 end
2973 return false
2974 end
2975
2976 # Compile an extern method
2977 # Return `true` if the compilation was successful, `false` if a fall-back is needed
2978 fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
2979 do
2980 var externname
2981 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
2982 if at != null and at.n_args.length == 1 then
2983 externname = at.arg_as_string(v.compiler.modelbuilder)
2984 if externname == null then return false
2985 else
2986 return false
2987 end
2988 v.add_extern(mpropdef.mclassdef.mmodule)
2989 var res: nullable RuntimeVariable = null
2990 var ret = mpropdef.msignature.return_mtype
2991 if ret != null then
2992 ret = v.resolve_for(ret, arguments.first)
2993 res = v.new_var_extern(ret)
2994 end
2995 v.adapt_signature(mpropdef, arguments)
2996 v.unbox_signature_extern(mpropdef, arguments)
2997
2998 if res == null then
2999 v.add("{externname}({arguments.join(", ")});")
3000 else
3001 v.add("{res} = {externname}({arguments.join(", ")});")
3002 res = v.box_extern(res, ret.as(not null))
3003 v.ret(res)
3004 end
3005 return true
3006 end
3007
3008 # Compile an extern factory
3009 # Return `true` if the compilation was successful, `false` if a fall-back is needed
3010 fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
3011 do
3012 var externname
3013 var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
3014 if at != null then
3015 externname = at.arg_as_string(v.compiler.modelbuilder)
3016 if externname == null then return false
3017 else
3018 return false
3019 end
3020 v.add_extern(mpropdef.mclassdef.mmodule)
3021 v.adapt_signature(mpropdef, arguments)
3022 v.unbox_signature_extern(mpropdef, arguments)
3023 var ret = arguments.first.mtype
3024 var res = v.new_var_extern(ret)
3025
3026 arguments.shift
3027
3028 v.add("{res} = {externname}({arguments.join(", ")});")
3029 res = v.box_extern(res, ret)
3030 v.ret(res)
3031 return true
3032 end
3033 end
3034
3035 redef class AAttrPropdef
3036 redef fun can_inline: Bool do return not is_lazy
3037
3038 redef fun compile_to_c(v, mpropdef, arguments)
3039 do
3040 if mpropdef == mreadpropdef then
3041 assert arguments.length == 1
3042 var recv = arguments.first
3043 var res
3044 if is_lazy then
3045 var set
3046 var ret = self.mtype
3047 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3048 var guard = self.mlazypropdef.mproperty
3049 if useiset then
3050 set = v.isset_attribute(self.mpropdef.mproperty, recv)
3051 else
3052 set = v.read_attribute(guard, recv)
3053 end
3054 v.add("if(likely({set})) \{")
3055 res = v.read_attribute(self.mpropdef.mproperty, recv)
3056 v.add("\} else \{")
3057
3058 var value = evaluate_expr(v, recv)
3059
3060 v.assign(res, value)
3061 if not useiset then
3062 var true_v = v.bool_instance(true)
3063 v.write_attribute(guard, arguments.first, true_v)
3064 end
3065 v.add("\}")
3066 else
3067 res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
3068 end
3069 v.assign(v.frame.returnvar.as(not null), res)
3070 else if mpropdef == mwritepropdef then
3071 assert arguments.length == 2
3072 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
3073 if is_lazy then
3074 var ret = self.mtype
3075 var useiset = not ret.is_c_primitive and not ret isa MNullableType
3076 if not useiset then
3077 v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
3078 end
3079 end
3080 else
3081 abort
3082 end
3083 end
3084
3085 fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3086 do
3087 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
3088 end
3089
3090 # Evaluate, store and return the default value of the attribute
3091 private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
3092 do
3093 var oldnode = v.current_node
3094 v.current_node = self
3095 var old_frame = v.frame
3096 var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
3097 v.frame = frame
3098
3099 var value
3100 var mtype = self.mtype
3101 assert mtype != null
3102
3103 var nexpr = self.n_expr
3104 var nblock = self.n_block
3105 if nexpr != null then
3106 value = v.expr(nexpr, mtype)
3107 else if nblock != null then
3108 value = v.new_var(mtype)
3109 frame.returnvar = value
3110 frame.returnlabel = v.get_name("RET_LABEL")
3111 v.add("\{")
3112 v.stmt(nblock)
3113 v.add("{frame.returnlabel.as(not null)}:(void)0;")
3114 v.add("\}")
3115 else
3116 abort
3117 end
3118
3119 v.write_attribute(self.mpropdef.mproperty, recv, value)
3120
3121 v.frame = old_frame
3122 v.current_node = oldnode
3123
3124 return value
3125 end
3126
3127 fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
3128 do
3129 var nexpr = self.n_expr
3130 if nexpr != null then return
3131
3132 var oldnode = v.current_node
3133 v.current_node = self
3134 var old_frame = v.frame
3135 var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
3136 v.frame = frame
3137 # Force read to check the initialization
3138 v.read_attribute(self.mpropdef.mproperty, recv)
3139 v.frame = old_frame
3140 v.current_node = oldnode
3141 end
3142 end
3143
3144 redef class AClassdef
3145 private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
3146 do
3147 if mpropdef.mproperty.is_root_init then
3148 assert arguments.length == 1
3149 if not mpropdef.is_intro then
3150 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
3151 end
3152 return
3153 else if mclassdef.auto_init == mpropdef then
3154 var recv = arguments.first
3155 var initializers = mpropdef.initializers
3156 var no_init = false
3157 if not initializers.is_empty then
3158
3159 var i = 1
3160 for p in initializers do
3161 if p isa MMethod then
3162 var args = [recv]
3163 for x in p.intro.msignature.mparameters do
3164 args.add arguments[i]
3165 i += 1
3166 end
3167 v.send(p, args)
3168 if p.intro.is_calling_init then no_init = true
3169 else if p isa MAttribute then
3170 v.write_attribute(p, recv, arguments[i])
3171 i += 1
3172 else abort
3173 end
3174 assert i == arguments.length
3175
3176 end
3177 if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
3178 return
3179 else
3180 abort
3181 end
3182 end
3183 end
3184
3185 redef class AExpr
3186 # Try to compile self as an expression
3187 # Do not call this method directly, use `v.expr` instead
3188 private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
3189 do
3190 v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
3191 var mtype = self.mtype
3192 if mtype == null then
3193 return null
3194 else
3195 var res = v.new_var(mtype)
3196 v.add("/* {res} = NOT YET {class_name} */")
3197 return res
3198 end
3199 end
3200
3201 # Try to compile self as a statement
3202 # Do not call this method directly, use `v.stmt` instead
3203 private fun stmt(v: AbstractCompilerVisitor)
3204 do
3205 expr(v)
3206 end
3207 end
3208
3209 redef class ABlockExpr
3210 redef fun stmt(v)
3211 do
3212 for e in self.n_expr do v.stmt(e)
3213 end
3214 redef fun expr(v)
3215 do
3216 var last = self.n_expr.last
3217 for e in self.n_expr do
3218 if e == last then break
3219 v.stmt(e)
3220 end
3221 return v.expr(last, null)
3222 end
3223 end
3224
3225 redef class AVardeclExpr
3226 redef fun stmt(v)
3227 do
3228 var variable = self.variable.as(not null)
3229 var ne = self.n_expr
3230 if ne != null then
3231 var i = v.expr(ne, variable.declared_type)
3232 v.assign(v.variable(variable), i)
3233 end
3234 end
3235 end
3236
3237 redef class AVarExpr
3238 redef fun expr(v)
3239 do
3240 var res = v.variable(self.variable.as(not null))
3241 var mtype = self.mtype.as(not null)
3242 return v.autoadapt(res, mtype)
3243 end
3244 end
3245
3246 redef class AVarAssignExpr
3247 redef fun expr(v)
3248 do
3249 var variable = self.variable.as(not null)
3250 var i = v.expr(self.n_value, variable.declared_type)
3251 v.assign(v.variable(variable), i)
3252 return i
3253 end
3254 end
3255
3256 redef class AVarReassignExpr
3257 redef fun stmt(v)
3258 do
3259 var variable = self.variable.as(not null)
3260 var vari = v.variable(variable)
3261 var value = v.expr(self.n_value, variable.declared_type)
3262 var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
3263 assert res != null
3264 v.assign(v.variable(variable), res)
3265 end
3266 end
3267
3268 redef class ASelfExpr
3269 redef fun expr(v) do return v.frame.arguments.first
3270 end
3271
3272 redef class AImplicitSelfExpr
3273 redef fun expr(v) do
3274 if not is_sys then return super
3275 return v.new_expr("glob_sys", mtype.as(not null))
3276 end
3277 end
3278
3279 redef class AEscapeExpr
3280 redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
3281 end
3282
3283 redef class AReturnExpr
3284 redef fun stmt(v)
3285 do
3286 var nexpr = self.n_expr
3287 if nexpr != null then
3288 var returnvar = v.frame.returnvar.as(not null)
3289 var i = v.expr(nexpr, returnvar.mtype)
3290 v.assign(returnvar, i)
3291 end
3292 v.add("goto {v.frame.returnlabel.as(not null)};")
3293 end
3294 end
3295
3296 redef class AAbortExpr
3297 redef fun stmt(v) do v.add_abort("Aborted")
3298 end
3299
3300 redef class AIfExpr
3301 redef fun stmt(v)
3302 do
3303 var cond = v.expr_bool(self.n_expr)
3304 v.add("if ({cond})\{")
3305 v.stmt(self.n_then)
3306 v.add("\} else \{")
3307 v.stmt(self.n_else)
3308 v.add("\}")
3309 end
3310
3311 redef fun expr(v)
3312 do
3313 var res = v.new_var(self.mtype.as(not null))
3314 var cond = v.expr_bool(self.n_expr)
3315 v.add("if ({cond})\{")
3316 v.assign(res, v.expr(self.n_then.as(not null), null))
3317 v.add("\} else \{")
3318 v.assign(res, v.expr(self.n_else.as(not null), null))
3319 v.add("\}")
3320 return res
3321 end
3322 end
3323
3324 redef class AIfexprExpr
3325 redef fun expr(v)
3326 do
3327 var res = v.new_var(self.mtype.as(not null))
3328 var cond = v.expr_bool(self.n_expr)
3329 v.add("if ({cond})\{")
3330 v.assign(res, v.expr(self.n_then, null))
3331 v.add("\} else \{")
3332 v.assign(res, v.expr(self.n_else, null))
3333 v.add("\}")
3334 return res
3335 end
3336 end
3337
3338 redef class ADoExpr
3339 redef fun stmt(v)
3340 do
3341 v.stmt(self.n_block)
3342 v.add_escape_label(break_mark)
3343 end
3344 end
3345
3346 redef class AWhileExpr
3347 redef fun stmt(v)
3348 do
3349 v.add("for(;;) \{")
3350 var cond = v.expr_bool(self.n_expr)
3351 v.add("if (!{cond}) break;")
3352 v.stmt(self.n_block)
3353 v.add_escape_label(continue_mark)
3354 v.add("\}")
3355 v.add_escape_label(break_mark)
3356 end
3357 end
3358
3359 redef class ALoopExpr
3360 redef fun stmt(v)
3361 do
3362 v.add("for(;;) \{")
3363 v.stmt(self.n_block)
3364 v.add_escape_label(continue_mark)
3365 v.add("\}")
3366 v.add_escape_label(break_mark)
3367 end
3368 end
3369
3370 redef class AForExpr
3371 redef fun stmt(v)
3372 do
3373 for g in n_groups do
3374 var cl = v.expr(g.n_expr, null)
3375 var it_meth = g.method_iterator
3376 assert it_meth != null
3377 var it = v.compile_callsite(it_meth, [cl])
3378 assert it != null
3379 g.it = it
3380 end
3381 v.add("for(;;) \{")
3382 for g in n_groups do
3383 var it = g.it
3384 var isok_meth = g.method_is_ok
3385 assert isok_meth != null
3386 var ok = v.compile_callsite(isok_meth, [it])
3387 assert ok != null
3388 v.add("if(!{ok}) break;")
3389 if g.variables.length == 1 then
3390 var item_meth = g.method_item
3391 assert item_meth != null
3392 var i = v.compile_callsite(item_meth, [it])
3393 assert i != null
3394 v.assign(v.variable(g.variables.first), i)
3395 else if g.variables.length == 2 then
3396 var key_meth = g.method_key
3397 assert key_meth != null
3398 var i = v.compile_callsite(key_meth, [it])
3399 assert i != null
3400 v.assign(v.variable(g.variables[0]), i)
3401 var item_meth = g.method_item
3402 assert item_meth != null
3403 i = v.compile_callsite(item_meth, [it])
3404 assert i != null
3405 v.assign(v.variable(g.variables[1]), i)
3406 else
3407 abort
3408 end
3409 end
3410 v.stmt(self.n_block)
3411 v.add_escape_label(continue_mark)
3412 for g in n_groups do
3413 var next_meth = g.method_next
3414 assert next_meth != null
3415 v.compile_callsite(next_meth, [g.it])
3416 end
3417 v.add("\}")
3418 v.add_escape_label(break_mark)
3419
3420 for g in n_groups do
3421 var method_finish = g.method_finish
3422 if method_finish != null then
3423 # TODO: Find a way to call this also in long escape (e.g. return)
3424 v.compile_callsite(method_finish, [g.it])
3425 end
3426 end
3427 end
3428 end
3429
3430 redef class AForGroup
3431 # C variable representing the iterator
3432 private var it: RuntimeVariable is noinit
3433 end
3434
3435 redef class AAssertExpr
3436 redef fun stmt(v)
3437 do
3438 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
3439
3440 var cond = v.expr_bool(self.n_expr)
3441 v.add("if (unlikely(!{cond})) \{")
3442 v.stmt(self.n_else)
3443 var nid = self.n_id
3444 if nid != null then
3445 v.add_abort("Assert '{nid.text}' failed")
3446 else
3447 v.add_abort("Assert failed")
3448 end
3449 v.add("\}")
3450 end
3451 end
3452
3453 redef class AOrExpr
3454 redef fun expr(v)
3455 do
3456 var res = v.new_var(self.mtype.as(not null))
3457 var i1 = v.expr_bool(self.n_expr)
3458 v.add("if ({i1}) \{")
3459 v.add("{res} = 1;")
3460 v.add("\} else \{")
3461 var i2 = v.expr_bool(self.n_expr2)
3462 v.add("{res} = {i2};")
3463 v.add("\}")
3464 return res
3465 end
3466 end
3467
3468 redef class AImpliesExpr
3469 redef fun expr(v)
3470 do
3471 var res = v.new_var(self.mtype.as(not null))
3472 var i1 = v.expr_bool(self.n_expr)
3473 v.add("if (!{i1}) \{")
3474 v.add("{res} = 1;")
3475 v.add("\} else \{")
3476 var i2 = v.expr_bool(self.n_expr2)
3477 v.add("{res} = {i2};")
3478 v.add("\}")
3479 return res
3480 end
3481 end
3482
3483 redef class AAndExpr
3484 redef fun expr(v)
3485 do
3486 var res = v.new_var(self.mtype.as(not null))
3487 var i1 = v.expr_bool(self.n_expr)
3488 v.add("if (!{i1}) \{")
3489 v.add("{res} = 0;")
3490 v.add("\} else \{")
3491 var i2 = v.expr_bool(self.n_expr2)
3492 v.add("{res} = {i2};")
3493 v.add("\}")
3494 return res
3495 end
3496 end
3497
3498 redef class ANotExpr
3499 redef fun expr(v)
3500 do
3501 var cond = v.expr_bool(self.n_expr)
3502 return v.new_expr("!{cond}", self.mtype.as(not null))
3503 end
3504 end
3505
3506 redef class AOrElseExpr
3507 redef fun expr(v)
3508 do
3509 var res = v.new_var(self.mtype.as(not null))
3510 var i1 = v.expr(self.n_expr, null)
3511 v.add("if ({i1}!=NULL) \{")
3512 v.assign(res, i1)
3513 v.add("\} else \{")
3514 var i2 = v.expr(self.n_expr2, null)
3515 v.assign(res, i2)
3516 v.add("\}")
3517 return res
3518 end
3519 end
3520
3521 redef class AIntegerExpr
3522 redef fun expr(v) do
3523 if value isa Int then return v.int_instance(value.as(Int))
3524 if value isa Byte then return v.byte_instance(value.as(Byte))
3525 if value isa Int8 then return v.int8_instance(value.as(Int8))
3526 if value isa Int16 then return v.int16_instance(value.as(Int16))
3527 if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
3528 if value isa Int32 then return v.int32_instance(value.as(Int32))
3529 if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
3530 # Should never happen
3531 abort
3532 end
3533 end
3534
3535 redef class AFloatExpr
3536 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
3537 end
3538
3539 redef class ACharExpr
3540 redef fun expr(v) do return v.char_instance(self.value.as(not null))
3541 end
3542
3543 redef class AArrayExpr
3544 redef fun expr(v)
3545 do
3546 var mtype = self.element_mtype.as(not null)
3547 var array = new Array[RuntimeVariable]
3548 var res = v.array_instance(array, mtype)
3549
3550 var old_comprehension = v.frame.comprehension
3551 v.frame.comprehension = res
3552 for nexpr in self.n_exprs do
3553 v.stmt(nexpr)
3554 end
3555 v.frame.comprehension = old_comprehension
3556
3557 return res
3558 end
3559 end
3560
3561 redef class AStringFormExpr
3562 redef fun expr(v) do return v.string_instance(self.value.as(not null))
3563 end
3564
3565 redef class ASuperstringExpr
3566 redef fun expr(v)
3567 do
3568 var type_string = mtype.as(not null)
3569
3570 # Collect elements of the superstring
3571 var array = new Array[AExpr]
3572 for ne in self.n_exprs do
3573 # Drop literal empty string.
3574 # They appears in things like "{a}" that is ["", a, ""]
3575 if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
3576 array.add(ne)
3577 end
3578
3579 # Store the allocated native array in a static variable
3580 # For reusing later
3581 var varonce = v.get_name("varonce")
3582 v.add("if (unlikely({varonce}==NULL)) \{")
3583
3584 # The native array that will contains the elements to_s-ized.
3585 # For fast concatenation.
3586 var a = v.native_array_instance(type_string, v.int_instance(array.length))
3587
3588 v.add_decl("static {a.mtype.ctype} {varonce};")
3589
3590 # Pre-fill the array with the literal string parts.
3591 # So they do not need to be filled again when reused
3592 for i in [0..array.length[ do
3593 var ne = array[i]
3594 if not ne isa AStringFormExpr then continue
3595 var e = v.expr(ne, null)
3596 v.native_array_set(a, i, e)
3597 end
3598
3599 v.add("\} else \{")
3600 # Take the native-array from the store.
3601 # The point is to prevent that some recursive execution use (and corrupt) the same native array
3602 # WARNING: not thread safe! (FIXME?)
3603 v.add("{a} = {varonce};")
3604 v.add("{varonce} = NULL;")
3605 v.add("\}")
3606
3607 # Stringify the elements and put them in the native array
3608 var to_s_method = v.get_property("to_s", v.object_type)
3609 for i in [0..array.length[ do
3610 var ne = array[i]
3611 if ne isa AStringFormExpr then continue
3612 var e = v.expr(ne, null)
3613 # Skip the `to_s` if the element is already a String
3614 if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
3615 e = v.send(to_s_method, [e]).as(not null)
3616 end
3617 v.native_array_set(a, i, e)
3618 end
3619
3620 # Fast join the native string to get the result
3621 var res = v.send(v.get_property("native_to_s", a.mtype), [a])
3622
3623 # We finish to work with the native array,
3624 # so store it so that it can be reused
3625 v.add("{varonce} = {a};")
3626 return res
3627 end
3628 end
3629
3630 redef class ACrangeExpr
3631 redef fun expr(v)
3632 do
3633 var i1 = v.expr(self.n_expr, null)
3634 var i2 = v.expr(self.n_expr2, null)
3635 var mtype = self.mtype.as(MClassType)
3636 var res = v.init_instance(mtype)
3637 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3638 return res
3639 end
3640 end
3641
3642 redef class AOrangeExpr
3643 redef fun expr(v)
3644 do
3645 var i1 = v.expr(self.n_expr, null)
3646 var i2 = v.expr(self.n_expr2, null)
3647 var mtype = self.mtype.as(MClassType)
3648 var res = v.init_instance(mtype)
3649 v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
3650 return res
3651 end
3652 end
3653
3654 redef class ATrueExpr
3655 redef fun expr(v) do return v.bool_instance(true)
3656 end
3657
3658 redef class AFalseExpr
3659 redef fun expr(v) do return v.bool_instance(false)
3660 end
3661
3662 redef class ANullExpr
3663 redef fun expr(v) do return v.null_instance
3664 end
3665
3666 redef class AIsaExpr
3667 redef fun expr(v)
3668 do
3669 var i = v.expr(self.n_expr, null)
3670 var cast_type = self.cast_type
3671 if cast_type == null then return null # no-no on broken node
3672 return v.type_test(i, cast_type, "isa")
3673 end
3674 end
3675
3676 redef class AAsCastExpr
3677 redef fun expr(v)
3678 do
3679 var i = v.expr(self.n_expr, null)
3680 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3681
3682 v.add_cast(i, self.mtype.as(not null), "as")
3683 return i
3684 end
3685 end
3686
3687 redef class AAsNotnullExpr
3688 redef fun expr(v)
3689 do
3690 var i = v.expr(self.n_expr, null)
3691 if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
3692
3693 if i.mtype.is_c_primitive then return i
3694
3695 v.add("if (unlikely({i} == NULL)) \{")
3696 v.add_abort("Cast failed")
3697 v.add("\}")
3698 return i
3699 end
3700 end
3701
3702 redef class AParExpr
3703 redef fun expr(v) do return v.expr(self.n_expr, null)
3704 end
3705
3706 redef class AOnceExpr
3707 redef fun expr(v)
3708 do
3709 var mtype = self.mtype.as(not null)
3710 var name = v.get_name("varonce")
3711 var guard = v.get_name(name + "_guard")
3712 v.add_decl("static {mtype.ctype} {name};")
3713 v.add_decl("static int {guard};")
3714 var res = v.new_var(mtype)
3715 v.add("if (likely({guard})) \{")
3716 v.add("{res} = {name};")
3717 v.add("\} else \{")
3718 var i = v.expr(self.n_expr, mtype)
3719 v.add("{res} = {i};")
3720 v.add("{name} = {res};")
3721 v.add("{guard} = 1;")
3722 v.add("\}")
3723 return res
3724 end
3725 end
3726
3727 redef class ASendExpr
3728 redef fun expr(v)
3729 do
3730 var recv = v.expr(self.n_expr, null)
3731 var callsite = self.callsite.as(not null)
3732 if callsite.is_broken then return null
3733 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3734 return v.compile_callsite(callsite, args)
3735 end
3736 end
3737
3738 redef class ASendReassignFormExpr
3739 redef fun stmt(v)
3740 do
3741 var recv = v.expr(self.n_expr, null)
3742 var callsite = self.callsite.as(not null)
3743 if callsite.is_broken then return
3744 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
3745
3746 var value = v.expr(self.n_value, null)
3747
3748 var left = v.compile_callsite(callsite, args)
3749 assert left != null
3750
3751 var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
3752 assert res != null
3753
3754 args.add(res)
3755 v.compile_callsite(self.write_callsite.as(not null), args)
3756 end
3757 end
3758
3759 redef class ASuperExpr
3760 redef fun expr(v)
3761 do
3762 var frame = v.frame.as(not null)
3763 var recv = frame.arguments.first
3764
3765 var callsite = self.callsite
3766 if callsite != null then
3767 if callsite.is_broken then return null
3768 var args
3769
3770 if self.n_args.n_exprs.is_empty then
3771 # Add automatic arguments for the super init call
3772 args = [recv]
3773 for i in [0..callsite.msignature.arity[ do
3774 args.add(frame.arguments[i+1])
3775 end
3776 else
3777 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3778 end
3779
3780 # Super init call
3781 var res = v.compile_callsite(callsite, args)
3782 return res
3783 end
3784
3785 var mpropdef = self.mpropdef.as(not null)
3786
3787 var args
3788 if self.n_args.n_exprs.is_empty then
3789 args = frame.arguments
3790 else
3791 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
3792 end
3793
3794 # Standard call-next-method
3795 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
3796 end
3797 end
3798
3799 redef class ANewExpr
3800 redef fun expr(v)
3801 do
3802 var mtype = self.recvtype
3803 assert mtype != null
3804
3805 if mtype.mclass.name == "NativeArray" then
3806 assert self.n_args.n_exprs.length == 1
3807 var l = v.expr(self.n_args.n_exprs.first, null)
3808 assert mtype isa MGenericType
3809 var elttype = mtype.arguments.first
3810 return v.native_array_instance(elttype, l)
3811 end
3812
3813 var recv = v.init_instance_or_extern(mtype)
3814
3815 var callsite = self.callsite
3816 if callsite == null then return recv
3817 if callsite.is_broken then return null
3818
3819 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
3820 var res2 = v.compile_callsite(callsite, args)
3821 if res2 != null then
3822 #self.debug("got {res2} from {mproperty}. drop {recv}")
3823 return res2
3824 end
3825 return recv
3826 end
3827 end
3828
3829 redef class AAttrExpr
3830 redef fun expr(v)
3831 do
3832 var recv = v.expr(self.n_expr, null)
3833 var mproperty = self.mproperty.as(not null)
3834 return v.read_attribute(mproperty, recv)
3835 end
3836 end
3837
3838 redef class AAttrAssignExpr
3839 redef fun expr(v)
3840 do
3841 var recv = v.expr(self.n_expr, null)
3842 var i = v.expr(self.n_value, null)
3843 var mproperty = self.mproperty.as(not null)
3844 v.write_attribute(mproperty, recv, i)
3845 return i
3846 end
3847 end
3848
3849 redef class AAttrReassignExpr
3850 redef fun stmt(v)
3851 do
3852 var recv = v.expr(self.n_expr, null)
3853 var value = v.expr(self.n_value, null)
3854 var mproperty = self.mproperty.as(not null)
3855 var attr = v.read_attribute(mproperty, recv)
3856 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
3857 assert res != null
3858 v.write_attribute(mproperty, recv, res)
3859 end
3860 end
3861
3862 redef class AIssetAttrExpr
3863 redef fun expr(v)
3864 do
3865 var recv = v.expr(self.n_expr, null)
3866 var mproperty = self.mproperty.as(not null)
3867 return v.isset_attribute(mproperty, recv)
3868 end
3869 end
3870
3871 redef class AVarargExpr
3872 redef fun expr(v)
3873 do
3874 return v.expr(self.n_expr, null)
3875 end
3876 end
3877
3878 redef class ANamedargExpr
3879 redef fun expr(v)
3880 do
3881 return v.expr(self.n_expr, null)
3882 end
3883 end
3884
3885 redef class ADebugTypeExpr
3886 redef fun stmt(v)
3887 do
3888 # do nothing
3889 end
3890 end
3891
3892 # Utils
3893
3894 redef class Array[E]
3895 # Return a new `Array` with the elements only contened in self and not in `o`
3896 fun -(o: Array[E]): Array[E] do
3897 var res = new Array[E]
3898 for e in self do if not o.has(e) then res.add(e)
3899 return res
3900 end
3901 end
3902
3903 redef class MModule
3904 # All `MProperty` associated to all `MClassDef` of `mclass`
3905 fun properties(mclass: MClass): Set[MProperty] do
3906 if not self.properties_cache.has_key(mclass) then
3907 var properties = new HashSet[MProperty]
3908 var parents = new Array[MClass]
3909 if self.flatten_mclass_hierarchy.has(mclass) then
3910 parents.add_all(mclass.in_hierarchy(self).direct_greaters)
3911 end
3912 for parent in parents do
3913 properties.add_all(self.properties(parent))
3914 end
3915 for mclassdef in mclass.mclassdefs do
3916 if not self.in_importation <= mclassdef.mmodule then continue
3917 for mprop in mclassdef.intro_mproperties do
3918 properties.add(mprop)
3919 end
3920 end
3921 self.properties_cache[mclass] = properties
3922 end
3923 return properties_cache[mclass]
3924 end
3925 private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
3926
3927 # Write FFI and nitni results to file
3928 fun finalize_ffi(c: AbstractCompiler) do end
3929
3930 # Give requided addinional system libraries (as given to LD_LIBS)
3931 # Note: can return null instead of an empty set
3932 fun collect_linker_libs: nullable Array[String] do return null
3933 end
3934
3935 # Create a tool context to handle options and paths
3936 var toolcontext = new ToolContext
3937
3938 toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
3939
3940 # We do not add other options, so process them now!
3941 toolcontext.process_options(args)
3942
3943 # We need a model to collect stufs
3944 var model = new Model
3945 # An a model builder to parse files
3946 var modelbuilder = new ModelBuilder(model, toolcontext)
3947
3948 var arguments = toolcontext.option_context.rest
3949 if arguments.length > 1 and toolcontext.opt_output.value != null then
3950 print "Option Error: --output needs a single source file. Do you prefer --dir?"
3951 exit 1
3952 end
3953
3954 # Here we load an process all modules passed on the command line
3955 var mmodules = modelbuilder.parse(arguments)
3956
3957 if mmodules.is_empty then toolcontext.quit
3958
3959 modelbuilder.run_phases
3960
3961 for mmodule in mmodules do
3962 toolcontext.info("*** PROCESS {mmodule} ***", 1)
3963 var ms = [mmodule]
3964 toolcontext.run_global_phases(ms)
3965 end