# Add compiling options
redef class ToolContext
# --output
- var opt_output = new OptionString("Output file", "-o", "--output")
+ var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
# --dir
var opt_dir = new OptionString("Output directory", "--dir")
# --no-cc
- var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
+ var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
# --no-main
var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
# --make-flags
- var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
+ var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
# --max-c-lines
var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
# --group-c-files
# --no-check-attr-isset
var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
# --no-check-assert
- var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+ var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit `assert` and `as` (dangerous)", "--no-check-assert")
# --no-check-autocast
var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
# --no-check-null
# --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")
+ var opt_no_gcc_directive = new OptionArray("Disable advanced gcc directives for optimization", "--no-gcc-directive")
# --release
var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
# -g
- var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "--debug", "-g")
+ var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
redef init
do
end
var debug = toolcontext.opt_debug.value
- makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
+ makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
if platform.supports_libunwind then
self.header.add_decl("#include <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
+ # longjmp !
+ self.header.add_decl("#include <setjmp.h>\n")
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("#ifdef __linux__")
+ self.header.add_decl(" #include <endian.h>")
+ self.header.add_decl("#endif")
self.header.add_decl("#include <inttypes.h>\n")
self.header.add_decl("#include \"gc_chooser.h\"")
+ self.header.add_decl("#ifdef __APPLE__")
+ self.header.add_decl(" #include <libkern/OSByteOrder.h>")
+ self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
+ self.header.add_decl("#endif")
+ self.header.add_decl("#ifdef __pnacl__")
+ self.header.add_decl(" #define be16toh(val) (((val) >> 8) | ((val) << 8))")
+ self.header.add_decl(" #define be32toh(val) ((be16toh((val) << 16) | (be16toh((val) >> 16))))")
+ self.header.add_decl("#endif")
self.header.add_decl("#ifdef ANDROID")
+ self.header.add_decl(" #ifndef be32toh")
+ self.header.add_decl(" #define be32toh(val) betoh32(val)")
+ self.header.add_decl(" #endif")
self.header.add_decl(" #include <android/log.h>")
self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
self.header.add_decl("#else")
compile_header_structs
compile_nitni_structs
+ compile_catch_stack
var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
if gccd_disable.has("noreturn") or gccd_disable.has("all") then
self.header.add_decl("extern val *glob_sys;")
end
+ # Stack stocking environment for longjumps
+ protected fun compile_catch_stack do
+ self.header.add_decl """
+struct catch_stack_t {
+ int cursor;
+ jmp_buf envs[100];
+};
+extern struct catch_stack_t catchStack;
+"""
+ end
+
# Declaration of structures for live Nit types
protected fun compile_header_structs is abstract
v.add_decl("int glob_argc;")
v.add_decl("char **glob_argv;")
v.add_decl("val *glob_sys;")
+ v.add_decl("struct catch_stack_t catchStack;")
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
for tag in count_type_test_tags do
v.add_decl("int main(int argc, char** argv) \{")
end
+ v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
v.add("signal(SIGABRT, sig_handler);")
v.add("signal(SIGFPE, sig_handler);")
v.add("signal(SIGILL, sig_handler);")
v.add("signal(SIGINT, sig_handler);")
v.add("signal(SIGTERM, sig_handler);")
v.add("signal(SIGSEGV, sig_handler);")
+ v.add "#endif"
v.add("signal(SIGPIPE, SIG_IGN);")
v.add("glob_argc = argc; glob_argv = argv;")
+ v.add("catchStack.cursor = -1;")
v.add("initialize_gc_option();")
v.add "initialize_nitni_global_refs();"
fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
do
- var initializers = callsite.mpropdef.initializers
- if not initializers.is_empty then
- var recv = arguments.first
-
- var i = 1
- for p in initializers do
- if p isa MMethod then
- var args = [recv]
- for x in p.intro.msignature.mparameters do
- args.add arguments[i]
- i += 1
- end
- self.send(p, args)
- else if p isa MAttribute then
- self.write_attribute(p, recv, arguments[i])
- i += 1
- else abort
- end
- assert i == arguments.length
-
- return self.send(callsite.mproperty, [recv])
- end
-
+ if callsite.is_broken then return null
return self.send(callsite.mproperty, arguments)
end
fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
- fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+ fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
# Return an element of a native array.
# The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
# of runtime variables to use in the call.
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 msignature = mpropdef.msignature.as(not null)
var res = new Array[RuntimeVariable]
res.add(recv)
res.add(null_instance)
continue
end
- if param.is_vararg and map.vararg_decl > 0 then
- var vararg = exprs.sub(j, map.vararg_decl)
+ if param.is_vararg and args[i].vararg_decl > 0 then
+ var vararg = exprs.sub(j, args[i].vararg_decl)
var elttype = param.mtype
var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
res.add(arg)
do
mtype = self.anchor(mtype)
var valmtype = value.mcasttype
+
+ # Do nothing if useless autocast
if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
return value
end
+ # Just as_not_null if the target is not nullable.
+ #
+ # eg `nullable PreciseType` adapted to `Object` gives precisetype.
if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
- var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
- return res
- else
- var res = new RuntimeVariable(value.name, valmtype, mtype)
- return res
+ mtype = valmtype.mtype
end
+
+ var res = new RuntimeVariable(value.name, value.mtype, mtype)
+ return res
end
# Generate a super call from a method definition
# Checks
+ # Can value be null? (according to current knowledge)
+ fun maybenull(value: RuntimeVariable): Bool
+ do
+ return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
+ end
+
# Add a check and an abort for a null receiver if needed
fun check_recv_notnull(recv: RuntimeVariable)
do
if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
- var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
- if maybenull then
+ if maybenull(recv) then
self.add("if (unlikely({recv} == NULL)) \{")
self.add_abort("Receiver is null")
self.add("\}")
return res
end
+ # Generates a NativeString instance fully escaped in C-style \xHH fashion
+ fun native_string_instance(ns: NativeString, len: Int): RuntimeVariable do
+ var mtype = mmodule.native_string_type
+ var nat = new_var(mtype)
+ var byte_esc = new Buffer.with_cap(len * 4)
+ for i in [0 .. len[ do
+ byte_esc.append("\\x{ns[i].to_s.substring_from(2)}")
+ end
+ self.add("{nat} = \"{byte_esc}\";")
+ return nat
+ end
+
# Generate a string value
fun string_instance(string: String): RuntimeVariable
do
# This is used for the legacy FFI
fun add_extern(mmodule: MModule)
do
- var file = mmodule.location.file.filename
+ var file = mmodule.filepath
file = file.strip_extension(".nit")
var tryfile = file + ".nit.h"
if tryfile.file_exists then
# used by aborts, asserts, casts, etc.
fun add_abort(message: String)
do
+ self.add("if(catchStack.cursor >= 0)\{")
+ self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
+ self.add("\}")
self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
add_raw_abort
end
fun stmt(nexpr: nullable AExpr)
do
if nexpr == null then return
- if nexpr.mtype == null and not nexpr.is_typed then
+ if nexpr.is_broken then
# Untyped expression.
# Might mean dead code or invalid code
# so aborts
fun can_inline(v: VISITOR): Bool
do
if is_abstract then return true
+ if constant_value != null then return true
var modelbuilder = v.compiler.modelbuilder
var node = modelbuilder.mpropdef2node(self)
if node isa APropdef then
else if node isa AClassdef then
# Automatic free init is always inlined since it is empty or contains only attribtes assigments
return true
+ else if node == null then
+ return true
else
abort
end
do
if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
+ var msignature = self.msignature.as(not null)
+
for i in [0..msignature.arity[ do
+ var mp = msignature.mparameters[i]
# skip test for vararg since the array is instantiated with the correct polymorphic type
- if msignature.vararg_rank == i then continue
+ if mp.is_vararg then continue
# skip if the cast is not required
var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype
if not origmtype.need_anchor then continue
# get the parameter type
- var mtype = self.msignature.mparameters[i].mtype
+ var mtype = mp.mtype
# generate the cast
# note that v decides if and how to implements the cast
- v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
+ v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
v.add_cast(arguments[i+1], mtype, "covariance")
end
end
# Try special compilation
if mpropdef.is_intern then
if compile_intern_to_c(v, mpropdef, arguments) then return
- else if mpropdef.is_extern then
+ end
+ if mpropdef.is_extern then
if mpropdef.mproperty.is_init then
if compile_externinit_to_c(v, mpropdef, arguments) then return
else
else if pname == "to_b" then
v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "code_point" then
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", 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
end
else if cname == "Char" then
if pname == "object_id" then
else if pname == "to_i" then
v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
return true
+ else if pname == "code_point" then
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "Byte" then
if pname == "output" then
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_u32" then
v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "ascii" then
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "Bool" then
if pname == "output" then
else if pname == "fast_cstring" 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 == "new" then
v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
return true
+ else if pname == "fetch_4_chars" then
+ v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
+ return true
+ else if pname == "fetch_4_hchars" then
+ v.ret(v.new_expr("(long)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
+ return true
end
else if cname == "NativeArray" then
- v.native_array_def(pname, ret, arguments)
- return true
+ return v.native_array_def(pname, ret, arguments)
else if cname == "Int8" then
if pname == "output" then
v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
v.assign(v.frame.returnvar.as(not null), res)
else if mpropdef == mwritepropdef then
assert arguments.length == 2
- v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+ var recv = arguments.first
+ var arg = arguments[1]
+ if is_optional and v.maybenull(arg) then
+ var value = v.new_var(self.mpropdef.static_mtype.as(not null))
+ v.add("if ({arg} == NULL) \{")
+ v.assign(value, evaluate_expr(v, recv))
+ v.add("\} else \{")
+ v.assign(value, arg)
+ v.add("\}")
+ arg = value
+ end
+ v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
if is_lazy then
var ret = self.mtype
var useiset = not ret.is_c_primitive and not ret isa MNullableType
redef class AClassdef
private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
do
- if mpropdef == self.mfree_init then
- assert mpropdef.mproperty.is_root_init
+ if mpropdef.mproperty.is_root_init then
assert arguments.length == 1
if not mpropdef.is_intro then
v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
end
return
+ else if mclassdef.auto_init == mpropdef then
+ var recv = arguments.first
+ var initializers = mpropdef.initializers
+ var no_init = false
+ if not initializers.is_empty then
+
+ var i = 1
+ for p in initializers do
+ if p isa MMethod then
+ var args = [recv]
+ for x in p.intro.msignature.mparameters do
+ args.add arguments[i]
+ i += 1
+ end
+ v.send(p, args)
+ if p.intro.is_calling_init then no_init = true
+ else if p isa MAttribute then
+ v.write_attribute(p, recv, arguments[i])
+ i += 1
+ else abort
+ end
+ assert i == arguments.length
+
+ end
+ if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
+ return
else
abort
end
redef class ADoExpr
redef fun stmt(v)
do
- v.stmt(self.n_block)
- v.add_escape_label(break_mark)
+ if self.n_catch != null then
+ v.add("catchStack.cursor += 1;")
+ v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
+ v.stmt(self.n_block)
+ v.add("catchStack.cursor -= 1;")
+ v.add("\}else \{")
+ v.add("catchStack.cursor -= 1;")
+ v.stmt(self.n_catch)
+ v.add("\}")
+ else
+ v.stmt(self.n_block)
+ end
+ v.add_escape_label(break_mark)
end
end
do
var res = v.new_var(self.mtype.as(not null))
var i1 = v.expr(self.n_expr, null)
+
+ if not v.maybenull(i1) then return i1
+
v.add("if ({i1}!=NULL) \{")
v.assign(res, i1)
v.add("\} else \{")
end
redef class ACharExpr
- redef fun expr(v) do return v.char_instance(self.value.as(not null))
+ redef fun expr(v) do
+ if is_ascii then return v.byte_instance(value.as(not null).ascii)
+ if is_code_point then return v.int_instance(value.as(not null).code_point)
+ return v.char_instance(self.value.as(not null))
+ end
end
redef class AArrayExpr
end
end
+redef class AugmentedStringFormExpr
+ # Factorize the making of a `Regex` object from a literal prefixed string
+ protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
+ var re = to_re
+ assert re != null
+ var res = v.compile_callsite(re, [rs])
+ if res == null then
+ print "Cannot call property `to_re` on {self}"
+ abort
+ end
+ for i in suffix.chars do
+ if i == 'i' then
+ var ign = ignore_case
+ assert ign != null
+ v.compile_callsite(ign, [res, v.bool_instance(true)])
+ continue
+ end
+ if i == 'm' then
+ var nl = newline
+ assert nl != null
+ v.compile_callsite(nl, [res, v.bool_instance(true)])
+ continue
+ end
+ if i == 'b' then
+ var ext = extended
+ assert ext != null
+ v.compile_callsite(ext, [res, v.bool_instance(false)])
+ continue
+ end
+ # Should not happen, this needs to be updated
+ # along with the addition of new suffixes
+ abort
+ end
+ return res
+ end
+end
+
redef class AStringFormExpr
- redef fun expr(v) do return v.string_instance(self.value.as(not null))
+ redef fun expr(v) do return v.string_instance(value)
+end
+
+redef class AStringExpr
+ redef fun expr(v) do
+ var s = v.string_instance(value)
+ if is_string then return s
+ if is_bytestring then
+ var ns = v.native_string_instance(bytes.items, bytes.length)
+ var ln = v.int_instance(bytes.length)
+ var cs = to_bytes_with_copy
+ assert cs != null
+ var res = v.compile_callsite(cs, [ns, ln])
+ assert res != null
+ s = res
+ else if is_re then
+ var res = make_re(v, s)
+ assert res != null
+ s = res
+ else
+ print "Unimplemented prefix or suffix for {self}"
+ abort
+ end
+ return s
+ end
end
redef class ASuperstringExpr
redef fun expr(v)
do
- var type_string = mtype.as(not null)
+ var type_string = v.mmodule.string_type
# Collect elements of the superstring
var array = new Array[AExpr]
# Fast join the native string to get the result
var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+ assert res != null
+
+ if is_re then res = make_re(v, res)
# 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
var i = v.expr(self.n_expr, null)
if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
- if i.mtype.is_c_primitive then return i
+ if not v.maybenull(i) 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)
+ if callsite.is_broken then return null
var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
return v.compile_callsite(callsite, args)
end
do
var recv = v.expr(self.n_expr, null)
var callsite = self.callsite.as(not null)
+ if callsite.is_broken then return
var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
var value = v.expr(self.n_value, null)
redef class ASuperExpr
redef fun expr(v)
do
- var recv = v.frame.arguments.first
+ var frame = v.frame.as(not null)
+ var recv = frame.arguments.first
var callsite = self.callsite
if callsite != null then
+ if callsite.is_broken then return null
var args
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])
+ args.add(frame.arguments[i+1])
end
else
args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
var args
if self.n_args.n_exprs.is_empty then
- args = v.frame.arguments
+ args = frame.arguments
else
args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
end
var callsite = self.callsite
if callsite == null then return recv
+ if callsite.is_broken then return null
var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
var res2 = v.compile_callsite(callsite, args)
# Here we load an process all modules passed on the command line
var mmodules = modelbuilder.parse(arguments)
-if mmodules.is_empty then return
+if mmodules.is_empty then toolcontext.quit
+
modelbuilder.run_phases
for mmodule in mmodules do