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