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