# --no-cc
var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
+ # --hardening
+ var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
+
redef init
do
super
- self.option_context.add_option(self.opt_output, self.opt_no_cc)
+ self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_hardening)
end
end
v.add_decl("#include <string.h>")
# TODO: Better way to activate the GC
- #v.add_decl("#include <gc/gc.h>")
- v.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
+ v.add_decl("#include <gc/gc.h>")
+ #v.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
# Declare structure for each live type
makefile.write("all: {outname}\n\n")
var ofiles = new Array[String]
+ # Compile each generated file
for f in cfiles do
var o = f.strip_extension(".c") + ".o"
- makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -I .nit_compile -I ../clib -c -o {o} {f}\n\n")
+ makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
ofiles.add(o)
end
-
- makefile.write("{outname}: {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\t$(CC) -Wl,--warn-unresolved-symbols $(CFLAGS) $(LDFLAGS) $(LDLIBS) -I .nit_compile -I ../clib -o {outname} {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\n")
+ # Compile each required extern body into a specific .o
+ for f in compiler.extern_bodies do
+ i += 1
+ var o = ".nit_compile/{mainmodule.name}.{i}.o"
+ makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
+ ofiles.add(o)
+ end
+ # Link edition
+ makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) $(LDLIBS) -o {outname} {ofiles.join(" ")}\n\n")
+ # Clean
+ makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
makefile.close
self.toolcontext.info("Generated makefile: {makename}", 2)
# The modeulbuilder used to know the model and the AST
var modelbuilder: ModelBuilder
+ # Is hardening asked (see --hardening)
+ fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
+
init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, modelbuilder: ModelBuilder)
do
self.mainmodule = mainmodule
v.add_decl("/* allocate {mtype} */")
v.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void) \{")
var res = v.new_var(mtype)
+ res.is_exact = true
v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
v.add("{res}->classid = {self.classid(mtype)};")
if tryfile.file_exists then
self.extern_bodies.add(tryfile)
end
- #(new OFStream.open("{file.basename("")}._nitni.h")).close
end
end
# The current casted type of the variable (as known in Nit)
var mcasttype: MType
+ # If the variable exaclty a mcasttype?
+ # false (usual value) means that the variable is a mcasttype or a subtype.
+ var is_exact: Bool = false
+
redef fun to_s do return name
- redef fun inspect do return "<{inspect_head} {name}:{mtype}({mcasttype})>"
+ redef fun inspect
+ do
+ var exact_str
+ if self.is_exact then
+ exact_str = " exact"
+ else
+ exact_str = ""
+ end
+ var type_str
+ if self.mtype == self.mcasttype then
+ type_str = "{mtype}{exact_str}"
+ else
+ type_str = "{mtype}({mcasttype}{exact_str})"
+ end
+ return "<{name}:{type_str}>"
+ end
end
# Visit the AST to generate the C code.
private var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
+ # Return an unique and stable identifier associated with an escapemark
+ fun escapemark_name(e: nullable EscapeMark): String
+ do
+ assert e != null
+ if escapemark_names.has_key(e) then return escapemark_names[e]
+ var name = e.name
+ if name == null then name = "label"
+ name = get_name(name)
+ escapemark_names[e] = name
+ return name
+ end
+
+ private var escapemark_names = new HashMap[EscapeMark, String]
+
# Return a new name based on `s' and unique in the visitor
fun get_name(s: String): String
do
fun collect_types(recv: RuntimeVariable): Array[MClassType]
do
var mtype = recv.mcasttype
+ if recv.is_exact then
+ assert mtype isa MClassType
+ assert self.compiler.runtime_type_analysis.live_types.has(mtype)
+ var types = [mtype]
+ return types
+ end
var cache = self.collect_types_cache
if cache.has_key(mtype) then
return cache[mtype]
end
if types.is_empty then
- self.add("/*BUG: no live types for {args.first.mtype} . {m}*/")
+ self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
return res
end
- self.add("/* send {m} on {args.first}: {args.first.mcasttype} (declared {args.first.mtype}) */")
+ self.add("/* send {m} on {args.first.inspect} */")
if args.first.mtype.ctype != "val*" then
var propdefs = m.lookup_definitions(self.compiler.mainmodule, args.first.mtype)
if propdefs.length == 0 then
- self.add("/* skip {args.first.mcasttype}, no method {m} */")
+ self.add("/* skip, no method {m} */")
return res
end
assert propdefs.length == 1
if args[1].mcasttype.ctype == "val*" then
self.add("{res} = ({args[1]} == NULL);")
else
- self.add("{res} = 0; /* {args[1]}: {args[1].mcasttype} cannot be null */")
+ self.add("{res} = 0; /* {args[1].inspect} cannot be null */")
end
else if m.name == "!=" then
assert res != null
if args[1].mcasttype.ctype == "val*" then
self.add("{res} = ({args[1]} != NULL);")
else
- self.add("{res} = 1; /* {args[1]}: {args[1].mcasttype} cannot be null */")
+ self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
end
else
self.add_abort("Reciever is null")
self.add "\} else"
end
self.add("switch({args.first}->classid) \{")
+ var last = types.last
var defaultpropdef: nullable MMethodDef = null
for t in types do
var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
defaultpropdef = propdef
continue
end
- self.add("case {self.compiler.classid(t)}: /* test {t} */")
+ if not self.compiler.hardening and t == last and defaultpropdef == null then
+ self.add("default: /* test {t} */")
+ else
+ self.add("case {self.compiler.classid(t)}: /* test {t} */")
+ end
var res2 = self.call(propdef, t, args)
if res != null then self.assign(res, res2.as(not null))
self.add "break;"
self.add("default: /* default is Object */")
var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args)
if res != null then self.assign(res, res2.as(not null))
- else
+ else if self.compiler.hardening then
self.add("default: /* bug */")
self.bugtype(args.first)
end
vararg.add(rawargs[i+1])
end
# FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
- var elttype = m.msignature.parameter_mtypes[vararg_rank]
+ var elttype = m.msignature.mparameters[vararg_rank].mtype
elttype = self.resolve_for(elttype, recv)
args.add(self.array_instance(vararg, elttype))
do
var recv = args.first
for i in [0..m.msignature.arity[ do
- var t = m.msignature.parameter_mtypes[i]
+ var t = m.msignature.mparameters[i].mtype
if i == m.msignature.vararg_rank then
t = args[i+1].mtype
end
var res = self.new_var(ret)
if types.is_empty then
- self.add("/*BUG: no live types for {recv.mtype} . {a}*/")
+ self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
return res
end
- self.add("/* read {a} on {recv.mcasttype} */")
+ self.add("/* read {a} on {recv.inspect} */")
self.add("switch({recv}->classid) \{")
+ var last = types.last
for t in types do
- self.add("case {self.compiler.classid(t)}:")
+ if not self.compiler.hardening and t == last then
+ self.add("default: /*{self.compiler.classid(t)}*/")
+ else
+ self.add("case {self.compiler.classid(t)}:")
+ end
var recv2 = self.autoadapt(recv, t)
var ta = a.intro.static_mtype.as(not null)
ta = self.resolve_for(ta, recv2)
self.assign(res, res2)
self.add("break;")
end
- self.add("default: /* Bug */")
- self.bugtype(recv)
+ if self.compiler.hardening then
+ self.add("default: /* Bug */")
+ self.bugtype(recv)
+ end
self.add("\}")
return res
var types = self.collect_types(recv)
if types.is_empty then
- self.add("/*BUG: no live types for {recv.mtype} . {a}*/")
+ self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
return
end
- self.add("/* write {a} on {recv.mcasttype} */")
+ self.add("/* write {a} on {recv.inspect} */")
self.add("switch({recv}->classid) \{")
+ var last = types.last
for t in types do
- self.add("case {self.compiler.classid(t)}:")
+ if not self.compiler.hardening and t == last then
+ self.add("default: /*{self.compiler.classid(t)}*/")
+ else
+ self.add("case {self.compiler.classid(t)}:")
+ end
var recv2 = self.autoadapt(recv, t)
var ta = a.intro.static_mtype.as(not null)
ta = self.resolve_for(ta, recv2)
self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};")
self.add("break;")
end
- self.add("default: /* Bug*/")
- self.bugtype(recv)
+ if self.compiler.hardening then
+ self.add("default: /* Bug*/")
+ self.bugtype(recv)
+ end
self.add("\}")
end
debug "problem: {mtype} was detected dead"
end
var res = self.new_expr("NEW_{mtype.c_name}()", mtype)
+ res.is_exact = true
return res
end
var res = self.new_var(bool_type)
- self.add("/* isa {mtype} on {value.mcasttype} */")
+ self.add("/* isa {mtype} on {value.inspect} */")
if value.mcasttype isa MNullableType then
self.add("if ({value} == NULL) \{")
if mtype isa MNullableType then
var res = self.init_instance(self.get_class("Array").get_mtype([elttype]))
self.add("\{ /* {res} = array_instance Array[{elttype}] */")
var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
+ nat.is_exact = true
self.add("{nat} = GC_MALLOC({array.length} * sizeof({elttype.ctype}));")
for i in [0..array.length[ do
var r = self.autobox(array[i], elttype)
# Generate a string value
fun string_instance(string: String): RuntimeVariable
do
+ var mtype = self.get_class("String").mclass_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("{res} = {name};")
+ self.add("\} else \{")
var nat = self.new_var(self.get_class("NativeString").mclass_type)
self.add("{nat} = \"{string.escape_to_c}\";")
- var res = self.init_instance(self.get_class("String").mclass_type)
+ var res2 = self.init_instance(mtype)
+ self.add("{res} = {res2};")
var length = self.int_instance(string.length)
self.send(self.get_property("with_native", res.mtype), [res, nat, length])
self.check_init_instance(res)
+ self.add("{name} = {res};")
+ self.add("\}")
return res
end
fun add_abort(message: String)
do
if self.current_node != null and self.current_node.location.file != null then
- self.add("fprintf(stderr, \"%s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+ self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
else
- self.add("fprintf(stderr, \"%s\\n\", \"{message.escape_to_c}\");")
+ self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
end
self.add("exit(1);")
end
var npropdef = modelbuilder.mpropdef2npropdef[self]
return npropdef.can_inline
else if self.mproperty.name == "init" then
- return false
+ # Automatic free init is always inlined since it is empty or contains only attribtes assigments
+ return true
else
abort
end
var v = new GlobalCompilerVisitor(compiler)
var selfvar = new RuntimeVariable("self", recv, recv)
+ if compiler.runtime_type_analysis.live_types.has(recv) then
+ selfvar.is_exact = true
+ end
var arguments = new Array[RuntimeVariable]
var frame = new Frame(v, self, recv, arguments)
v.frame = frame
comment.append("(self: {recv}")
arguments.add(selfvar)
for i in [0..self.msignature.arity[ do
- var mtype = self.msignature.parameter_mtypes[i]
+ var mtype = self.msignature.mparameters[i].mtype
if i == self.msignature.vararg_rank then
mtype = v.get_class("Array").get_mtype([mtype])
end
redef class APropdef
private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
do
- v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");")
+ v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
debug("Not yet implemented")
end
var nblock = self.n_block
if nblock == null then return true
if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
- #if nblock isa ABlockExpr and nblock.n_expr.length == 1 then return true
+ if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
return false
end
end
end
return
end
- v.add("printf(\"Not implemented {class_name}:{mpropdef} at {location.to_s}\\n\");")
+ v.add("printf(\"NOT IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
debug("Not implemented {mpropdef}")
end
end
end
end
+redef class ADeferredMethPropdef
+ redef fun compile_to_c(v, mpropdef, arguments)
+ do
+ v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");")
+ v.add("exit(1);")
+ end
+
+ redef fun can_inline do return true
+end
+
redef class AExpr
# Try to compile self as an expression
# Do not call this method directly, use `v.expr' instead
private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable
do
debug("Unimplemented expr {class_name}")
- v.add("printf(\"Not implemented {class_name}:{location.to_s}\\n\");")
+ v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
var mtype = self.mtype
if mtype == null then
return null
redef class AContinueExpr
redef fun stmt(v)
do
- v.add("goto CONTINUE_{self.escapemark.object_id};")
+ v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
end
end
redef class ABreakExpr
redef fun stmt(v)
do
- v.add("goto BREAK_{self.escapemark.object_id};")
+ v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
end
end
v.stmt(self.n_block)
var escapemark = self.escapemark
if escapemark != null then
- v.add("BREAK_{escapemark.object_id}: (void)0;")
+ v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
end
end
end
var cond = v.expr_bool(self.n_expr)
v.add("if (!{cond}) break;")
v.stmt(self.n_block)
- v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+ v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
v.add("\}")
- v.add("BREAK_{escapemark.object_id}: (void)0;")
+ v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
end
end
do
v.add("for(;;) \{")
v.stmt(self.n_block)
- v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+ v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
v.add("\}")
- v.add("BREAK_{escapemark.object_id}: (void)0;")
+ v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
end
end
var ok = v.send(v.get_property("is_ok", it.mtype), [it])
assert ok != null
v.add("if(!{ok}) break;")
- var i = v.send(v.get_property("item", it.mtype), [it])
- assert i != null
- v.assign(v.variable(variables.first), i)
+ if self.variables.length == 1 then
+ var i = v.send(v.get_property("item", it.mtype), [it])
+ assert i != null
+ v.assign(v.variable(variables.first), i)
+ else if self.variables.length == 2 then
+ var i = v.send(v.get_property("key", it.mtype), [it])
+ assert i != null
+ v.assign(v.variable(variables[0]), i)
+ i = v.send(v.get_property("item", it.mtype), [it])
+ assert i != null
+ v.assign(v.variable(variables[1]), i)
+ else
+ abort
+ end
v.stmt(self.n_block)
- v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+ v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
v.send(v.get_property("next", it.mtype), [it])
v.add("\}")
- v.add("BREAK_{escapemark.object_id}: (void)0;")
+ v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
end
end
do
var array = new Array[RuntimeVariable]
for ne in self.n_exprs do
+ if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
var i = v.expr(ne, null)
array.add(i)
end
do
var recv = v.expr(self.n_expr, null)
var args = [recv]
- for a in compute_raw_arguments do
+ for a in self.raw_arguments.as(not null) do
args.add(v.expr(a, null))
end
var mproperty = self.mproperty.as(not null)
do
var recv = v.expr(self.n_expr, null)
var args = [recv]
- for a in compute_raw_arguments do
+ for a in self.raw_arguments.as(not null) do
args.add(v.expr(a, null))
end
var value = v.expr(self.n_value, null)