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