import c_tools
private import annotation
import mixin
+import counter
# Add compiling options
redef class ToolContext
var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
# --isset-checks-metrics
var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
- # --stacktrace
- var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
+ # --no-stacktrace
+ var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
# --no-gcc-directives
var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
# --release
self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
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)
self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
- self.option_context.add_option(self.opt_stacktrace)
+ self.option_context.add_option(self.opt_no_stacktrace)
self.option_context.add_option(self.opt_no_gcc_directive)
self.option_context.add_option(self.opt_release)
self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
do
super
- var st = opt_stacktrace.value
- if st == "none" or st == "libunwind" or st == "nitstack" then
- # Fine, do nothing
- else if st == "auto" or st == null then
- # Default is nitstack
- opt_stacktrace.value = "nitstack"
- else
- print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
- exit(1)
- end
-
if opt_output.value != null and opt_dir.value != null then
- print "Error: cannot use both --dir and --output"
+ print "Option Error: cannot use both --dir and --output"
exit(1)
end
end
redef class ModelBuilder
- # The compilation directory
- var compile_dir: String
-
# Simple indirection to `Toolchain::write_and_make`
protected fun write_and_make(compiler: AbstractCompiler)
do
- var platform = compiler.mainmodule.target_platform
- var toolchain
- if platform == null then
- toolchain = new MakefileToolchain(toolcontext)
- else
- toolchain = platform.toolchain(toolcontext)
- end
- compile_dir = toolchain.compile_dir
- toolchain.write_and_make compiler
+ var platform = compiler.target_platform
+ var toolchain = platform.toolchain(toolcontext, compiler)
+ compiler.toolchain = toolchain
+ toolchain.write_and_make
end
end
redef class Platform
- fun toolchain(toolcontext: ToolContext): Toolchain is abstract
+ # The specific tool-chain associated to the platform
+ fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
+ do
+ return new MakefileToolchain(toolcontext, compiler)
+ end
end
+# Build toolchain for a specific target program, varies per `Platform`
class Toolchain
+
+ # Toolcontext
var toolcontext: ToolContext
- fun compile_dir: String
+ # Compiler of the target program
+ var compiler: AbstractCompiler
+
+ # Directory where to generate all files
+ #
+ # The option `--compile_dir` change this directory.
+ fun root_compile_dir: String
do
var compile_dir = toolcontext.opt_compile_dir.value
- if compile_dir == null then compile_dir = ".nit_compile"
+ if compile_dir == null then compile_dir = "nit_compile"
return compile_dir
end
- fun write_and_make(compiler: AbstractCompiler) is abstract
+ # Directory where to generate all C files
+ #
+ # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
+ fun compile_dir: String do return root_compile_dir
+
+ # Write all C files and compile them
+ fun write_and_make is abstract
end
+# Default toolchain using a Makefile
class MakefileToolchain
super Toolchain
- redef fun write_and_make(compiler)
+ redef fun write_and_make
do
var compile_dir = compile_dir
+ # Remove the compilation directory unless explicitly set
+ var auto_remove = toolcontext.opt_compile_dir.value == null
+
# Generate the .h and .c files
# A single C file regroups many compiled rumtime functions
# 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
var time0 = get_time
self.toolcontext.info("*** WRITING C ***", 1)
+ root_compile_dir.mkdir
compile_dir.mkdir
var cfiles = new Array[String]
- write_files(compiler, compile_dir, cfiles)
+ write_files(compile_dir, cfiles)
# Generate the Makefile
- write_makefile(compiler, compile_dir, cfiles)
+ write_makefile(compile_dir, cfiles)
var time1 = get_time
self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
time0 = time1
self.toolcontext.info("*** COMPILING C ***", 1)
- compile_c_code(compiler, compile_dir)
+ compile_c_code(compile_dir)
+
+ if auto_remove then
+ sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
+ end
time1 = get_time
self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
end
- fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+ # Write all source files to the `compile_dir`
+ fun write_files(compile_dir: String, cfiles: Array[String])
do
- var platform = compiler.mainmodule.target_platform
- if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
+ var platform = compiler.target_platform
+ if platform.supports_libunwind then compiler.build_c_to_nit_bindings
var cc_opt_with_libgc = "-DWITH_LIBGC"
- if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
+ if not platform.supports_libgc then cc_opt_with_libgc = ""
# Add gc_choser.h to aditionnal bodies
var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
var hfilename = compiler.header.file.name + ".h"
var hfilepath = "{compile_dir}/{hfilename}"
- var h = new OFStream.open(hfilepath)
+ var h = new FileWriter.open(hfilepath)
for l in compiler.header.decl_lines do
h.write l
h.write "\n"
for f in compiler.files do
var i = 0
var count = 0
- var file: nullable OFStream = null
+ var file: nullable FileWriter = null
for vis in f.writers do
if vis == compiler.header then continue
var total_lines = vis.lines.length + vis.decl_lines.length
var cfilepath = "{compile_dir}/{cfilename}"
self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
cfiles.add(cfilename)
- file = new OFStream.open(cfilepath)
+ file = new FileWriter.open(cfilepath)
file.write "#include \"{f.name}.0.h\"\n"
count = total_lines
end
var cfilename = "{f.name}.0.h"
var cfilepath = "{compile_dir}/{cfilename}"
- var hfile: nullable OFStream = null
- hfile = new OFStream.open(cfilepath)
+ var hfile: nullable FileWriter = null
+ hfile = new FileWriter.open(cfilepath)
hfile.write "#include \"{hfilename}\"\n"
for key in f.required_declarations do
if not compiler.provided_declarations.has_key(key) then
self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
end
- fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
+ # Get the name of the Makefile to use
+ fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
- fun default_outname(mainmodule: MModule): String
+ # Get the default name of the executable to produce
+ fun default_outname: String
do
- # Search a non fictive module
- var res = mainmodule.name
- while mainmodule.is_fictive do
- mainmodule = mainmodule.in_importation.direct_greaters.first
- res = mainmodule.name
- end
- return res
+ var mainmodule = compiler.mainmodule.first_real_mmodule
+ return mainmodule.name
end
# Combine options and platform informations to get the final path of the outfile
do
var res = self.toolcontext.opt_output.value
if res != null then return res
- res = default_outname(mainmodule)
+ res = default_outname
var dir = self.toolcontext.opt_dir.value
if dir != null then return dir.join_path(res)
return res
end
- fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+ # Write the Makefile
+ fun write_makefile(compile_dir: String, cfiles: Array[String])
do
var mainmodule = compiler.mainmodule
- var platform = compiler.mainmodule.target_platform
+ var platform = compiler.target_platform
var outname = outfile(mainmodule)
- var outpath = compile_dir.relpath(outname)
- var makename = makefile_name(mainmodule)
+ var real_outpath = compile_dir.relpath(outname)
+ var outpath = real_outpath.escape_to_mk
+ if outpath != real_outpath then
+ # If the name is crazy and need escaping, we will do an indirection
+ # 1. generate the binary in the nit_compile dir under an escaped name
+ # 2. copy the binary at the right place in the `all` goal.
+ outpath = mainmodule.c_name
+ end
+ var makename = makefile_name
var makepath = "{compile_dir}/{makename}"
- var makefile = new OFStream.open(makepath)
+ var makefile = new FileWriter.open(makepath)
var linker_options = new HashSet[String]
for m in mainmodule.in_importation.greaters do
if libs != null then linker_options.add_all(libs)
end
- makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
+ makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
- var ost = toolcontext.opt_stacktrace.value
- if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+ makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
+ if platform.supports_libunwind then
+ if toolcontext.opt_no_stacktrace.value then
+ makefile.write "NO_STACKTRACE=True"
+ else
+ makefile.write "NO_STACKTRACE= # Set to `True` to enable"
+ end
+ end
# Dynamic adaptations
# While `platform` enable complex toolchains, they are statically applied
# For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
+ makefile.write "\n\n"
# Check and adapt the targeted system
makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
- makefile.write("ifeq ($(uname_S),Darwin)\n")
- # remove -lunwind since it is already included on macosx
- makefile.write("\tNEED_LIBUNWIND :=\n")
- makefile.write("endif\n\n")
# Check and adapt for the compiler used
# clang need an additionnal `-Qunused-arguments`
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")
- makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
+ if platform.supports_libunwind then
+ makefile.write """
+ifneq ($(NO_STACKTRACE), True)
+ # Check and include lib-unwind in a portable way
+ ifneq ($(uname_S),Darwin)
+ # already included on macosx, but need to get the correct flags in other supported platforms.
+ ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
+ LDLIBS += `pkg-config --libs libunwind`
+ CFLAGS += `pkg-config --cflags libunwind`
+ else
+ $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
+ CFLAGS += -D NO_STACKTRACE
+ endif
+ endif
+else
+ # Stacktraces disabled
+ CFLAGS += -D NO_STACKTRACE
+endif
+
+"""
+ else
+ makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
+ end
- makefile.write("all: {outpath}\n\n")
+ makefile.write("all: {outpath}\n")
+ if outpath != real_outpath then
+ makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
+ end
+ makefile.write("\n")
var ofiles = new Array[String]
var dep_rules = new Array[String]
dep_rules.add(o)
end
+ # Generate linker script, if any
+ if not compiler.linker_script.is_empty then
+ var linker_script_path = "{compile_dir}/linker_script"
+ ofiles.add "linker_script"
+ var f = new FileWriter.open(linker_script_path)
+ for l in compiler.linker_script do
+ f.write l
+ f.write "\n"
+ end
+ f.close
+ end
+
var java_files = new Array[ExternFile]
var pkgconfigs = new Array[String]
if not pkgconfigs.is_empty then
pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
end
- makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
+ makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
# Clean
- makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
+ makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+ if outpath != real_outpath then
+ makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+ end
makefile.close
self.toolcontext.info("Generated makefile: {makepath}", 2)
makepath.file_copy_to "{compile_dir}/Makefile"
end
- fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
+ # The C code is generated, compile it to an executable
+ fun compile_c_code(compile_dir: String)
do
- var makename = makefile_name(compiler.mainmodule)
+ var makename = makefile_name
var makeflags = self.toolcontext.opt_make_flags.value
if makeflags == null then makeflags = ""
- self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
+
+ var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
+ self.toolcontext.info(command, 2)
var res
if self.toolcontext.verbose_level >= 3 then
- res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
+ res = sys.system("{command} 2>&1")
else
- res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
+ res = sys.system("{command} 2>&1 >/dev/null")
end
if res != 0 then
- toolcontext.error(null, "make failed! Error code: {res}.")
+ toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
end
end
end
# The modelbuilder used to know the model and the AST
var modelbuilder: ModelBuilder is protected writable
+ # The associated toolchain
+ #
+ # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
+ var toolchain: Toolchain is noinit
+
# Is hardening asked? (see --hardening)
fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
+ # The targeted specific platform
+ var target_platform: Platform is noinit
+
init
do
self.realmainmodule = mainmodule
+ target_platform = mainmodule.target_platform or else new Platform
end
+ # Do the full code generation of the program `mainmodule`
+ # It is the main method usually called after the instantiation
+ fun do_compilation is abstract
+
# Force the creation of a new file
# The point is to avoid contamination between must-be-compiled-separately files
fun new_file(name: String): CodeFile
# Where global declaration are stored (the main .h)
var header: CodeWriter is writable, noinit
+ # Additionnal linker script for `ld`.
+ # Mainly used to do specific link-time symbol resolution
+ var linker_script = new Array[String]
+
# Provide a declaration that can be requested (before or latter) by a visitor
fun provide_declaration(key: String, s: String)
do
# Binds the generated C function names to Nit function names
fun build_c_to_nit_bindings
do
- var compile_dir = modelbuilder.compile_dir
+ var compile_dir = toolchain.compile_dir
- var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
+ var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
stream.write("#include <string.h>\n")
stream.write("#include <stdlib.h>\n")
stream.write("#include \"c_functions_hash.h\"\n")
stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
for i in names.keys do
stream.write("\{\"")
- stream.write(i)
+ stream.write(i.escape_to_c)
stream.write("\",\"")
- stream.write(names[i])
+ stream.write(names[i].escape_to_c)
stream.write("\"\},\n")
end
stream.write("\};\n")
stream.write("\}\n")
stream.close
- stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
+ stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
stream.close
self.header.add_decl("#include <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
+ self.header.add_decl("#include <sys/types.h>\n")
+ self.header.add_decl("#include <unistd.h>\n")
+ self.header.add_decl("#include <stdint.h>\n")
self.header.add_decl("#include \"gc_chooser.h\"")
self.header.add_decl("#ifdef ANDROID")
self.header.add_decl(" #include <android/log.h>")
var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
if gccd_disable.has("noreturn") or gccd_disable.has("all") then
# Signal handler function prototype
- self.header.add_decl("void show_backtrace(int);")
+ self.header.add_decl("void fatal_exit(int);")
else
- self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
+ self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
end
if gccd_disable.has("likely") or gccd_disable.has("all") then
var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
if finalize_meth == null then
- modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
+ modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
return
end
do
var v = self.new_visitor
v.add_decl("#include <signal.h>")
- var ost = modelbuilder.toolcontext.opt_stacktrace.value
- var platform = mainmodule.target_platform
-
- if platform != null and not platform.supports_libunwind then ost = "none"
+ var platform = target_platform
- var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
+ var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
- if ost == "nitstack" or ost == "libunwind" then
+ if platform.supports_libunwind then
+ v.add_decl("#ifndef NO_STACKTRACE")
v.add_decl("#define UNW_LOCAL_ONLY")
v.add_decl("#include <libunwind.h>")
- if ost == "nitstack" then
- v.add_decl("#include \"c_functions_hash.h\"")
- end
+ v.add_decl("#include \"c_functions_hash.h\"")
+ v.add_decl("#endif")
end
v.add_decl("int glob_argc;")
v.add_decl("char **glob_argv;")
v.compiler.header.add_decl("extern long count_isset_checks;")
end
- v.add_decl("void sig_handler(int signo)\{")
- v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
- v.add_decl("show_backtrace(signo);")
- v.add_decl("\}")
-
- v.add_decl("void show_backtrace (int signo) \{")
- if ost == "nitstack" or ost == "libunwind" then
+ v.add_decl("static void show_backtrace(void) \{")
+ if platform.supports_libunwind then
+ v.add_decl("#ifndef NO_STACKTRACE")
v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
v.add_decl("unw_cursor_t cursor;")
v.add_decl("if(opt==NULL)\{")
v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
v.add_decl("while (unw_step(&cursor) > 0) \{")
v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
- if ost == "nitstack" then
v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
v.add_decl(" if (recv != NULL)\{")
v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
v.add_decl(" \}else\{")
v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
v.add_decl(" \}")
- else
- v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
- end
v.add_decl("\}")
v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
v.add_decl("free(procname);")
v.add_decl("\}")
+ v.add_decl("#endif /* NO_STACKTRACE */")
end
- v.add_decl("exit(signo);")
+ v.add_decl("\}")
+
+ v.add_decl("void sig_handler(int signo)\{")
+ v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
+ v.add_decl("show_backtrace();")
+ # rethrows
+ v.add_decl("signal(signo, SIG_DFL);")
+ v.add_decl("kill(getpid(), signo);")
+ v.add_decl("\}")
+
+ v.add_decl("void fatal_exit(int status) \{")
+ v.add_decl("show_backtrace();")
+ v.add_decl("exit(status);")
v.add_decl("\}")
if no_main then
v.add("return 0;")
v.add("\}")
+
+ for m in mainmodule.in_importation.greaters do
+ var f = "FILE_"+m.c_name
+ v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
+ provide_declaration(f, "extern const char {f}[];")
+ end
end
# Copile all C functions related to the [incr|decr]_ref features of the FFI
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
- if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
- var n = self.modelbuilder.mclassdef2nclassdef[cd]
- for npropdef in n.n_propdefs do
- if npropdef isa AAttrPropdef then
- npropdef.init_expr(v, recv)
- end
+ for npropdef in modelbuilder.collect_attr_propdef(cd) do
+ npropdef.init_expr(v, recv)
end
end
end
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
- if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
- var n = self.modelbuilder.mclassdef2nclassdef[cd]
- for npropdef in n.n_propdefs do
- if npropdef isa AAttrPropdef then
- npropdef.check_expr(v, recv)
- end
+ for npropdef in modelbuilder.collect_attr_propdef(cd) do
+ npropdef.check_expr(v, recv)
end
end
end
end
fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
-
- # Division facility
- # Avoid division by zero by returning the string "n/a"
- fun div(a,b:Int):String
- do
- if b == 0 then return "n/a"
- return ((a*10000/b).to_f / 100.0).to_precision(2)
- end
end
# A file unit (may be more than one file if
# The current visited AST node
var current_node: nullable ANode = null is writable
- # The current `Frame`
- var frame: nullable Frame = null is writable
+ # The current `StaticFrame`
+ var frame: nullable StaticFrame = null is writable
# Alias for self.compiler.mainmodule.object_type
fun object_type: MClassType do return self.compiler.mainmodule.object_type
self.writer = new CodeWriter(compiler.files.last)
end
- # Force to get the primitive class named `name` or abort
- fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
-
# Force to get the primitive property named `name` in the instance `recv` or abort
fun get_property(name: String, recv: MType): MMethod
do
fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+ # Return an element of a native array.
+ # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+ fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
+
+ # Store an element in a native array.
+ # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+ fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
+
# Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
# This method is used to manage varargs in signatures and returns the real array
# of runtime variables to use in the call.
- fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
+ fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
do
var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
var res = new Array[RuntimeVariable]
res.add(recv)
- if args.is_empty then return res
+ if msignature.arity == 0 then return res
+
+ if map == null then
+ assert args.length == msignature.arity
+ for ne in args do
+ res.add self.expr(ne, null)
+ end
+ return res
+ end
- var vararg_rank = msignature.vararg_rank
- var vararg_len = args.length - msignature.arity
- if vararg_len < 0 then vararg_len = 0
+ # Eval in order of arguments, not parameters
+ var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+ for ne in args do
+ exprs.add self.expr(ne, null)
+ end
+ # Fill `res` with the result of the evaluation according to the mapping
for i in [0..msignature.arity[ do
- if i == vararg_rank then
- var ne = args[i]
- if ne isa AVarargExpr then
- var e = self.expr(ne.n_expr, null)
- res.add(e)
- continue
- end
- var vararg = new Array[RuntimeVariable]
- for j in [vararg_rank..vararg_rank+vararg_len] do
- var e = self.expr(args[j], null)
- vararg.add(e)
- end
- var elttype = msignature.mparameters[vararg_rank].mtype
+ var param = msignature.mparameters[i]
+ var j = map.map.get_or_null(i)
+ if j == null then
+ # default value
+ res.add(null_instance)
+ continue
+ end
+ if param.is_vararg and map.vararg_decl > 0 then
+ var vararg = exprs.sub(j, map.vararg_decl)
+ var elttype = param.mtype
var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
res.add(arg)
- else
- var j = i
- if i > vararg_rank then j += vararg_len
- var e = self.expr(args[j], null)
- res.add(e)
+ continue
end
+ res.add exprs[j]
end
return res
end
fun escapemark_name(e: nullable EscapeMark): String
do
assert e != null
- if escapemark_names.has_key(e) then return escapemark_names[e]
+ if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
var name = e.name
if name == null then name = "label"
name = get_name(name)
- escapemark_names[e] = name
+ frame.escapemark_names[e] = name
return name
end
add("BREAK_{escapemark_name(e)}: (void)0;")
end
- private var escapemark_names = new HashMap[EscapeMark, String]
-
# Return a "const char*" variable associated to the classname of the dynamic type of an object
# NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
fun class_name_string(value: RuntimeVariable): String is abstract
# Generate a alloc-instance + init-attributes
fun init_instance(mtype: MClassType): RuntimeVariable is abstract
+ # Allocate and init attributes of an instance of a standard or extern class
+ #
+ # Does not support universals and the pseudo-internal `NativeArray` class.
+ fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
+ do
+ var recv
+ var ctype = mtype.ctype
+ assert mtype.mclass.name != "NativeArray"
+ if not mtype.is_c_primitive then
+ recv = init_instance(mtype)
+ else if ctype == "char*" then
+ recv = new_expr("NULL/*special!*/", mtype)
+ else
+ recv = new_expr("({ctype})0/*special!*/", mtype)
+ end
+ return recv
+ end
+
# Set a GC finalizer on `recv`, only if `recv` isa Finalizable
fun set_finalizer(recv: RuntimeVariable)
do
end
end
+ # The currently processed module
+ #
+ # alias for `compiler.mainmodule`
+ fun mmodule: MModule do return compiler.mainmodule
+
# Generate an integer value
fun int_instance(value: Int): RuntimeVariable
do
- var res = self.new_var(self.get_class("Int").mclass_type)
- self.add("{res} = {value};")
+ var t = mmodule.int_type
+ var res = new RuntimeVariable("{value.to_s}l", t, t)
+ return res
+ end
+
+ # Generate a byte value
+ fun byte_instance(value: Byte): RuntimeVariable
+ do
+ var t = mmodule.byte_type
+ var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
+ return res
+ end
+
+ # Generate a char value
+ fun char_instance(value: Char): RuntimeVariable
+ do
+ var t = mmodule.char_type
+ var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+ return res
+ end
+
+ # Generate a float value
+ #
+ # FIXME pass a Float, not a string
+ fun float_instance(value: String): RuntimeVariable
+ do
+ var t = mmodule.float_type
+ var res = new RuntimeVariable("{value}", t, t)
return res
end
# Generate an integer value
fun bool_instance(value: Bool): RuntimeVariable
do
- var res = self.new_var(self.get_class("Bool").mclass_type)
- if value then
- self.add("{res} = 1;")
- else
- self.add("{res} = 0;")
- end
+ var s = if value then "1" else "0"
+ var res = new RuntimeVariable(s, bool_type, bool_type)
+ return res
+ end
+
+ # Generate the `null` value
+ fun null_instance: RuntimeVariable
+ do
+ var t = compiler.mainmodule.model.null_type
+ var res = new RuntimeVariable("((val*)NULL)", t, t)
return res
end
# Generate a string value
fun string_instance(string: String): RuntimeVariable
do
- var mtype = self.get_class("String").mclass_type
+ var mtype = mmodule.string_type
var name = self.get_name("varonce")
self.add_decl("static {mtype.ctype} {name};")
var res = self.new_var(mtype)
- self.add("if ({name}) \{")
+ self.add("if (likely({name}!=NULL)) \{")
self.add("{res} = {name};")
self.add("\} else \{")
- var native_mtype = self.get_class("NativeString").mclass_type
+ var native_mtype = mmodule.native_string_type
var nat = self.new_var(native_mtype)
self.add("{nat} = \"{string.escape_to_c}\";")
var length = self.int_instance(string.length)
self.require_declaration(s)
end
- # look for a needed .h and .c file for a given .nit source-file
- # FIXME: bad API, parameter should be a `MModule`, not its source-file
- fun add_extern(file: String)
+ # Look for a needed .h and .c file for a given module
+ # This is used for the legacy FFI
+ fun add_extern(mmodule: MModule)
do
+ var file = mmodule.location.file.filename
file = file.strip_extension(".nit")
var tryfile = file + ".nit.h"
if tryfile.file_exists then
fun add_raw_abort
do
- if self.current_node != null and self.current_node.location.file != null then
- self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+ if self.current_node != null and self.current_node.location.file != null and
+ self.current_node.location.file.mmodule != null then
+ var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
+ self.require_declaration(f)
+ self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
else
self.add("PRINT_ERROR(\"\\n\");")
end
- self.add("show_backtrace(1);")
+ self.add("fatal_exit(1);")
end
# Add a dynamic cast
fun stmt(nexpr: nullable AExpr)
do
if nexpr == null then return
+ if nexpr.mtype == null and not nexpr.is_typed then
+ # Untyped expression.
+ # Might mean dead code or invalid code
+ # so aborts
+ add_abort("FATAL: bad statement executed.")
+ return
+ end
+
+ var narray = nexpr.comprehension
+ if narray != null then
+ var recv = frame.comprehension.as(not null)
+ var val = expr(nexpr, narray.element_mtype)
+ compile_callsite(narray.push_callsite.as(not null), [recv, val])
+ return
+ end
+
var old = self.current_node
self.current_node = nexpr
nexpr.stmt(self)
do
var old = self.current_node
self.current_node = nexpr
- var res = nexpr.expr(self).as(not null)
+
+ var res = null
+ if nexpr.mtype != null then
+ res = nexpr.expr(self)
+ end
+
+ if res == null then
+ # Untyped expression.
+ # Might mean dead code or invalid code.
+ # so aborts
+ add_abort("FATAL: bad expression executed.")
+ # and return a placebo result to please the C compiler
+ if mtype == null then mtype = compiler.mainmodule.object_type
+ res = new_var(mtype)
+
+ self.current_node = old
+ return res
+ end
+
if mtype != null then
mtype = self.anchor(mtype)
res = self.autobox(res, mtype)
end
end
-# A frame correspond to a visited property in a `GlobalCompilerVisitor`
-class Frame
+# The static context of a visited property in a `AbstractCompilerVisitor`
+class StaticFrame
type VISITOR: AbstractCompilerVisitor
# The label at the end of the property
var returnlabel: nullable String = null is writable
+
+ # Labels associated to a each escapemarks.
+ # Because of inlinings, escape-marks must be associated to their context (the frame)
+ private var escapemark_names = new HashMap[EscapeMark, String]
+
+ # The array comprehension currently filled, if any
+ private var comprehension: nullable RuntimeVariable = null
end
redef class MType
# Short name of the `ctype` to use in unions
fun ctypename: String do return "val"
- # Return the name of the C structure associated to a Nit live type
- fun c_name: String is abstract
- protected var c_name_cache: nullable String is protected writable
+ # Is the associated C type a primitive one?
+ #
+ # ENSURE `result == (ctype != "val*")`
+ fun is_c_primitive: Bool do return false
end
redef class MClassType
- redef fun c_name
- do
- var res = self.c_name_cache
- if res != null then return res
- res = "{mclass.intro_mmodule.c_name}__{mclass.name.to_cmangle}"
- self.c_name_cache = res
- return res
- end
- redef fun ctype: String
- do
+ redef var ctype is lazy do
if mclass.name == "Int" then
return "long"
else if mclass.name == "Bool" then
return "short int"
else if mclass.name == "Char" then
- return "char"
+ return "uint32_t"
else if mclass.name == "Float" then
return "double"
+ else if mclass.name == "Byte" then
+ return "unsigned char"
else if mclass.name == "NativeString" then
return "char*"
else if mclass.name == "NativeArray" then
end
end
+ redef var is_c_primitive is lazy do return ctype != "val*"
+
redef fun ctype_extern: String
do
if mclass.kind == extern_kind then
return "c"
else if mclass.name == "Float" then
return "d"
+ else if mclass.name == "Byte" then
+ return "b"
else if mclass.name == "NativeString" then
return "str"
else if mclass.name == "NativeArray" then
end
end
-redef class MGenericType
- redef fun c_name
- do
- var res = self.c_name_cache
- if res != null then return res
- res = super
- for t in self.arguments do
- res = res + t.c_name
- end
- self.c_name_cache = res
- return res
- end
-end
-
-redef class MParameterType
- redef fun c_name
- do
- var res = self.c_name_cache
- if res != null then return res
- res = "{self.mclass.c_name}_FT{self.rank}"
- self.c_name_cache = res
- return res
- end
-end
-
-redef class MVirtualType
- redef fun c_name
- do
- var res = self.c_name_cache
- if res != null then return res
- res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
- self.c_name_cache = res
- return res
- end
-end
-
-redef class MNullableType
- redef fun c_name
- do
- var res = self.c_name_cache
- if res != null then return res
- res = "nullable_{self.mtype.c_name}"
- self.c_name_cache = res
- return res
- end
-end
-
-redef class MClass
- # Return the name of the C structure associated to a Nit class
- fun c_name: String do
- var res = self.c_name_cache
- if res != null then return res
- res = "{intro_mmodule.c_name}__{name.to_cmangle}"
- self.c_name_cache = res
- return res
- end
- private var c_name_cache: nullable String
-end
-
-redef class MProperty
- fun c_name: String do
- var res = self.c_name_cache
- if res != null then return res
- res = "{self.intro.c_name}"
- self.c_name_cache = res
- return res
- end
- private var c_name_cache: nullable String
-end
-
redef class MPropDef
type VISITOR: AbstractCompilerVisitor
-
- private var c_name_cache: nullable String
-
- # The mangled name associated to the property
- fun c_name: String
- do
- var res = self.c_name_cache
- if res != null then return res
- res = "{self.mclassdef.mmodule.c_name}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
- self.c_name_cache = res
- return res
- end
end
redef class MMethodDef
do
if is_abstract then return true
var modelbuilder = v.compiler.modelbuilder
- if modelbuilder.mpropdef2npropdef.has_key(self) then
- var npropdef = modelbuilder.mpropdef2npropdef[self]
- return npropdef.can_inline
- else if self.mproperty.is_root_init then
+ var node = modelbuilder.mpropdef2node(self)
+ if node isa APropdef then
+ return node.can_inline
+ else if node isa AClassdef then
# Automatic free init is always inlined since it is empty or contains only attribtes assigments
return true
else
do
var modelbuilder = v.compiler.modelbuilder
var val = constant_value
- if modelbuilder.mpropdef2npropdef.has_key(self) then
- var npropdef = modelbuilder.mpropdef2npropdef[self]
+ var node = modelbuilder.mpropdef2node(self)
+
+ if is_abstract then
+ var cn = v.class_name_string(arguments.first)
+ v.current_node = node
+ v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+ v.add_raw_abort
+ return null
+ end
+
+ if node isa APropdef then
var oldnode = v.current_node
- v.current_node = npropdef
+ v.current_node = node
self.compile_parameter_check(v, arguments)
- npropdef.compile_to_c(v, self, arguments)
+ node.compile_to_c(v, self, arguments)
v.current_node = oldnode
- else if self.mproperty.is_root_init then
- var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
+ else if node isa AClassdef then
var oldnode = v.current_node
- v.current_node = nclassdef
+ v.current_node = node
self.compile_parameter_check(v, arguments)
- nclassdef.compile_to_c(v, self, arguments)
+ node.compile_to_c(v, self, arguments)
v.current_node = oldnode
else if val != null then
v.ret(v.value_instance(val))
redef class AMethPropdef
redef fun compile_to_c(v, mpropdef, arguments)
do
- if mpropdef.is_abstract then
- var cn = v.class_name_string(arguments.first)
- v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
- v.add_raw_abort
- return
- end
-
# Call the implicit super-init
var auto_super_inits = self.auto_super_inits
if auto_super_inits != null then
else if pname == "unary -" then
v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
return true
+ else if pname == "unary +" then
+ v.ret(arguments[0])
+ return true
else if pname == "*" then
v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
return true
else if pname == "to_f" then
v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "to_b" then
+ v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ return true
else if pname == "ascii" then
- v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
return true
end
else if cname == "Char" then
if pname == "output" then
- v.add("printf(\"%c\", {arguments.first});")
+ v.add("printf(\"%c\", ((unsigned char){arguments.first}));")
return true
else if pname == "object_id" then
v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
return true
else if pname == "ascii" then
- v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+ return true
+ end
+ else if cname == "Byte" then
+ if pname == "output" then
+ v.add("printf(\"%x\\n\", {arguments.first});")
+ return true
+ else if pname == "object_id" then
+ v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+ return true
+ else if pname == "+" then
+ v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "-" then
+ v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "unary -" then
+ v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "unary +" then
+ v.ret(arguments[0])
+ return true
+ else if pname == "*" then
+ v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "/" then
+ v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "%" then
+ v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "lshift" then
+ v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "rshift" then
+ v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "==" then
+ v.ret(v.equal_test(arguments[0], arguments[1]))
+ return true
+ else if pname == "!=" then
+ var res = v.equal_test(arguments[0], arguments[1])
+ v.ret(v.new_expr("!{res}", ret.as(not null)))
+ return true
+ else if pname == "<" then
+ v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == ">" then
+ v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "<=" then
+ v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == ">=" then
+ v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "to_i" then
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "to_f" then
+ v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "ascii" then
+ v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
return true
end
else if cname == "Bool" then
else if pname == "unary -" then
v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
return true
+ else if pname == "unary +" then
+ v.ret(arguments[0])
+ return true
else if pname == "succ" then
v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
return true
else if pname == "to_i" then
v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "to_b" then
+ v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "NativeString" then
if pname == "[]" then
- v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
+ v.ret(v.new_expr("(uint32_t)(unsigned char){arguments[0]}[{arguments[1]}]", ret.as(not null)))
return true
else if pname == "[]=" then
- v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
+ v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
return true
else if pname == "copy_to" then
v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
else if pname == "atoi" then
v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
return true
+ else if pname == "fast_cstring" then
+ v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+ return true
else if pname == "new" then
v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
return true
do
var externname
var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
- if at != null then
+ if at != null and at.n_args.length == 1 then
externname = at.arg_as_string(v.compiler.modelbuilder)
if externname == null then return false
else
return false
end
- if location.file != null then
- var file = location.file.filename
- v.add_extern(file)
- end
+ v.add_extern(mpropdef.mclassdef.mmodule)
var res: nullable RuntimeVariable = null
var ret = mpropdef.msignature.return_mtype
if ret != null then
else
return false
end
- if location.file != null then
- var file = location.file.filename
- v.add_extern(file)
- end
+ v.add_extern(mpropdef.mclassdef.mmodule)
v.adapt_signature(mpropdef, arguments)
v.unbox_signature_extern(mpropdef, arguments)
var ret = arguments.first.mtype
end
redef class AAttrPropdef
+ redef fun can_inline: Bool do return not is_lazy
+
redef fun compile_to_c(v, mpropdef, arguments)
do
if mpropdef == mreadpropdef then
var res
if is_lazy then
var set
- var ret = self.mpropdef.static_mtype
- var useiset = ret.ctype == "val*" and not ret isa MNullableType
+ var ret = self.mtype
+ var useiset = not ret.is_c_primitive and not ret isa MNullableType
var guard = self.mlazypropdef.mproperty
if useiset then
set = v.isset_attribute(self.mpropdef.mproperty, recv)
v.assign(res, value)
if not useiset then
- var true_v = v.new_expr("1", v.bool_type)
+ var true_v = v.bool_instance(true)
v.write_attribute(guard, arguments.first, true_v)
end
v.add("\}")
assert arguments.length == 2
v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
if is_lazy then
- var ret = self.mpropdef.static_mtype
- var useiset = ret.ctype == "val*" and not ret isa MNullableType
+ var ret = self.mtype
+ var useiset = not ret.is_c_primitive and not ret isa MNullableType
if not useiset then
- v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
+ v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
end
end
else
fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
do
- if has_value and not is_lazy then evaluate_expr(v, recv)
+ if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
end
# Evaluate, store and return the default value of the attribute
var oldnode = v.current_node
v.current_node = self
var old_frame = v.frame
- var frame = new Frame(v, self.mpropdef.as(not null), recv.mcasttype.as(MClassType), [recv])
+ var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
v.frame = frame
var value
- var mtype = self.mpropdef.static_mtype
+ var mtype = self.mtype
assert mtype != null
var nexpr = self.n_expr
var oldnode = v.current_node
v.current_node = self
var old_frame = v.frame
- var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
+ var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
v.frame = frame
# Force read to check the initialization
v.read_attribute(self.mpropdef.mproperty, recv)
redef fun expr(v) do return v.frame.arguments.first
end
+redef class AImplicitSelfExpr
+ redef fun expr(v) do
+ if not is_sys then return super
+ return v.new_expr("glob_sys", mtype.as(not null))
+ end
+end
+
redef class AEscapeExpr
redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
end
end
redef class AIntExpr
- redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
+ redef fun expr(v) do return v.int_instance(self.value.as(not null))
+end
+
+redef class AByteExpr
+ redef fun expr(v) do return v.byte_instance(self.value.as(not null))
end
redef class AFloatExpr
- redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
+ redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
end
redef class ACharExpr
- redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
+ redef fun expr(v) do return v.char_instance(self.value.as(not null))
end
redef class AArrayExpr
redef fun expr(v)
do
- var mtype = self.mtype.as(MClassType).arguments.first
+ var mtype = self.element_mtype.as(not null)
var array = new Array[RuntimeVariable]
- for nexpr in self.n_exprs.n_exprs do
- var i = v.expr(nexpr, mtype)
- array.add(i)
+ var res = v.array_instance(array, mtype)
+
+ var old_comprehension = v.frame.comprehension
+ v.frame.comprehension = res
+ for nexpr in self.n_exprs do
+ v.stmt(nexpr)
end
- return v.array_instance(array, mtype)
+ v.frame.comprehension = old_comprehension
+
+ return res
end
end
redef class ASuperstringExpr
redef fun expr(v)
do
- var array = new Array[RuntimeVariable]
+ var type_string = mtype.as(not null)
+
+ # Collect elements of the superstring
+ var array = new Array[AExpr]
for ne in self.n_exprs do
+ # Drop literal empty string.
+ # They appears in things like "{a}" that is ["", a, ""]
if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
- var i = v.expr(ne, null)
- array.add(i)
+ array.add(ne)
end
- var a = v.array_instance(array, v.object_type)
- var res = v.send(v.get_property("to_s", a.mtype), [a])
+
+ # Store the allocated native array in a static variable
+ # For reusing later
+ var varonce = v.get_name("varonce")
+ v.add("if (unlikely({varonce}==NULL)) \{")
+
+ # The native array that will contains the elements to_s-ized.
+ # For fast concatenation.
+ var a = v.native_array_instance(type_string, v.int_instance(array.length))
+
+ v.add_decl("static {a.mtype.ctype} {varonce};")
+
+ # Pre-fill the array with the literal string parts.
+ # So they do not need to be filled again when reused
+ for i in [0..array.length[ do
+ var ne = array[i]
+ if not ne isa AStringFormExpr then continue
+ var e = v.expr(ne, null)
+ v.native_array_set(a, i, e)
+ end
+
+ v.add("\} else \{")
+ # Take the native-array from the store.
+ # The point is to prevent that some recursive execution use (and corrupt) the same native array
+ # WARNING: not thread safe! (FIXME?)
+ v.add("{a} = {varonce};")
+ v.add("{varonce} = NULL;")
+ v.add("\}")
+
+ # Stringify the elements and put them in the native array
+ var to_s_method = v.get_property("to_s", v.object_type)
+ for i in [0..array.length[ do
+ var ne = array[i]
+ if ne isa AStringFormExpr then continue
+ var e = v.expr(ne, null)
+ # Skip the `to_s` if the element is already a String
+ if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
+ e = v.send(to_s_method, [e]).as(not null)
+ end
+ v.native_array_set(a, i, e)
+ end
+
+ # Fast join the native string to get the result
+ var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+
+ # We finish to work with the native array,
+ # so store it so that it can be reused
+ v.add("{varonce} = {a};")
return res
end
end
end
redef class ATrueExpr
- redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
+ redef fun expr(v) do return v.bool_instance(true)
end
redef class AFalseExpr
- redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
+ redef fun expr(v) do return v.bool_instance(false)
end
redef class ANullExpr
- redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
+ redef fun expr(v) do return v.null_instance
end
redef class AIsaExpr
redef fun expr(v)
do
var i = v.expr(self.n_expr, null)
- return v.type_test(i, self.cast_type.as(not null), "isa")
+ var cast_type = self.cast_type
+ if cast_type == null then return null # no-no on broken node
+ return v.type_test(i, cast_type, "isa")
end
end
var i = v.expr(self.n_expr, null)
if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
- if i.mtype.ctype != "val*" then return i
+ if i.mtype.is_c_primitive then return i
v.add("if (unlikely({i} == NULL)) \{")
v.add_abort("Cast failed")
v.add_decl("static {mtype.ctype} {name};")
v.add_decl("static int {guard};")
var res = v.new_var(mtype)
- v.add("if ({guard}) \{")
+ v.add("if (likely({guard})) \{")
v.add("{res} = {name};")
v.add("\} else \{")
var i = v.expr(self.n_expr, mtype)
do
var recv = v.expr(self.n_expr, null)
var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
return v.compile_callsite(callsite, args)
end
end
do
var recv = v.expr(self.n_expr, null)
var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
var value = v.expr(self.n_value, null)
var callsite = self.callsite
if callsite != null then
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var args
- # Add additional arguments for the super init call
- if args.length == 1 then
+ if self.n_args.n_exprs.is_empty then
+ # Add automatic arguments for the super init call
+ args = [recv]
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
end
+ else
+ args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
end
+
# Super init call
var res = v.compile_callsite(callsite, args)
return res
end
var mpropdef = self.mpropdef.as(not null)
- var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
- if args.length == 1 then
+
+ var args
+ if self.n_args.n_exprs.is_empty then
args = v.frame.arguments
+ else
+ args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
end
- # stantard call-next-method
+ # Standard call-next-method
return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
end
end
do
var mtype = self.recvtype
assert mtype != null
- var recv
- var ctype = mtype.ctype
+
if mtype.mclass.name == "NativeArray" then
assert self.n_args.n_exprs.length == 1
var l = v.expr(self.n_args.n_exprs.first, null)
assert mtype isa MGenericType
var elttype = mtype.arguments.first
return v.native_array_instance(elttype, l)
- else if ctype == "val*" then
- recv = v.init_instance(mtype)
- else if ctype == "char*" then
- recv = v.new_expr("NULL/*special!*/", mtype)
- else
- recv = v.new_expr("({ctype})0/*special!*/", mtype)
end
- var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var recv = v.init_instance_or_extern(mtype)
+
+ var callsite = self.callsite
+ if callsite == null then return recv
+
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
var res2 = v.compile_callsite(callsite, args)
if res2 != null then
#self.debug("got {res2} from {mproperty}. drop {recv}")
end
end
+redef class AVarargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr, null)
+ end
+end
+
+redef class ANamedargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr, null)
+ end
+end
+
redef class ADebugTypeExpr
redef fun stmt(v)
do
end
redef class MModule
- # Return the name of the global C identifier associated to `self`.
- # This name is used to prefix files and other C identifiers associated with `self`.
- var c_name: String is lazy do
- var g = mgroup
- var res
- if g != null and g.mproject.name != name then
- res = g.mproject.name.to_cmangle + "__" + name.to_cmangle
- else
- res = name.to_cmangle
- end
- return res
- end
-
# All `MProperty` associated to all `MClassDef` of `mclass`
fun properties(mclass: MClass): Set[MProperty] do
if not self.properties_cache.has_key(mclass) then
# Give requided addinional system libraries (as given to LD_LIBS)
# Note: can return null instead of an empty set
- fun collect_linker_libs: nullable Set[String] do return null
+ fun collect_linker_libs: nullable Array[String] do return null
end
# Create a tool context to handle options and paths
var toolcontext = new ToolContext
-toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
+toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
# We do not add other options, so process them now!
toolcontext.process_options(args)
var arguments = toolcontext.option_context.rest
if arguments.length > 1 and toolcontext.opt_output.value != null then
- print "Error: --output needs a single source file. Do you prefer --dir?"
+ print "Option Error: --output needs a single source file. Do you prefer --dir?"
exit 1
end