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