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