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