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