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