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