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