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