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.target_platform
- var toolchain = platform.toolchain(toolcontext)
- compile_dir = toolchain.compile_dir
- toolchain.write_and_make compiler
+ var toolchain = platform.toolchain(toolcontext, compiler)
+ compiler.toolchain = toolchain
+ toolchain.write_and_make
end
end
redef class Platform
# The specific tool-chain associated to the platform
- fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
+ 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.target_platform
- if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
+ if platform.supports_libunwind then compiler.build_c_to_nit_bindings
var cc_opt_with_libgc = "-DWITH_LIBGC"
if not platform.supports_libgc then cc_opt_with_libgc = ""
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.target_platform
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
+ # 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(mainmodule)
+ var makename = makefile_name
var makepath = "{compile_dir}/{makename}"
var makefile = new FileWriter.open(makepath)
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.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")
if outpath != real_outpath then
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
# 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 FileWriter.open("{compile_dir}/c_functions_hash.c")
stream.write("#include <string.h>\n")
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 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 = target_platform
- if not platform.supports_libunwind then ost = "none"
-
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;")
end
v.add_decl("static void show_backtrace(void) \{")
- if ost == "nitstack" or ost == "libunwind" then
+ 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("\}")
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
var recv
var ctype = mtype.ctype
assert mtype.mclass.name != "NativeArray"
- if ctype == "val*" then
+ if not mtype.is_c_primitive then
recv = init_instance(mtype)
else if ctype == "char*" then
recv = new_expr("NULL/*special!*/", mtype)
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 (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)
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
# `mtype` is the expected return type, pass null if no specific type is expected.
fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
do
+ if nexpr.mtype == 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
+ return new_var(mtype)
+ end
var old = self.current_node
self.current_node = nexpr
var res = nexpr.expr(self).as(not null)
# Short name of the `ctype` to use in unions
fun ctypename: String do return "val"
+
+ # 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 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
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
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
var oldnode = v.current_node
v.current_node = self
var old_frame = v.frame
- var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.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
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 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
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")
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
var recv = v.init_instance_or_extern(mtype)
- var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ 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
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