# --hardening
var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
+ # --no-check-covariance
+ var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
+
+ # --no-check-initialization
+ var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization")
+
+ # --no-check-assert
+ var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+
+ # --no-check-autocast
+ var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
+
+ # --no-check-other
+ var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
+
redef init
do
super
self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_hardening)
+ self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
end
end
self.toolcontext.info("*** COMPILING TO C ***", 1)
var compiler = new GlobalCompiler(mainmodule, runtime_type_analysis, self)
- var v = new GlobalCompilerVisitor(compiler)
+ var v = compiler.new_visitor
v.add_decl("#include <stdlib.h>")
v.add_decl("#include <stdio.h>")
for t in runtime_type_analysis.live_types do
if t.ctype == "val*" then
compiler.generate_init_instance(t)
+ compiler.generate_check_init_instance(t)
else
compiler.generate_box_instance(t)
end
end
# The main function of the C
-
- v = new GlobalCompilerVisitor(compiler)
- v.add_decl("int glob_argc;")
- v.add_decl("char **glob_argv;")
- v.add_decl("val *glob_sys;")
- v.add_decl("int main(int argc, char** argv) \{")
- v.add("glob_argc = argc; glob_argv = argv;")
- var main_type = mainmodule.sys_type
- if main_type == null then return # Nothing to compile
- var glob_sys = v.init_instance(main_type)
- v.add("glob_sys = {glob_sys};")
- var main_init = mainmodule.try_get_primitive_method("init", main_type)
- if main_init != null then
- v.send(main_init, [glob_sys])
- end
- var main_method = mainmodule.try_get_primitive_method("main", main_type)
- if main_method != null then
- v.send(main_method, [glob_sys])
- end
- v.add("return 0;")
- v.add("\}")
+ compiler.compile_main_function
# Compile until all runtime_functions are visited
do
assert self.runtime_type_analysis.live_types.has(mtype)
assert mtype.ctype == "val*"
- var v = new GlobalCompilerVisitor(self)
+ var v = self.new_visitor
var is_native_array = mtype.mclass.name == "NativeArray"
end
v.add("{res}->classid = {self.classid(mtype)};")
+ self.generate_init_attr(v, res, mtype)
+ v.add("return {res};")
+ v.add("\}")
+ end
+
+ fun generate_check_init_instance(mtype: MClassType)
+ do
+ if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return
+
+ var v = self.new_visitor
+ var res = new RuntimeVariable("self", mtype, mtype)
+ self.header.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype});")
+ v.add_decl("/* allocate {mtype} */")
+ v.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype} {res}) \{")
+ self.generate_check_attr(v, res, mtype)
+ v.add("\}")
+ end
+
+ # Generate code that collect initialize the attributes on a new instance
+ fun generate_init_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
+ do
for cd in mtype.collect_mclassdefs(self.mainmodule)
do
var n = self.modelbuilder.mclassdef2nclassdef[cd]
for npropdef in n.n_propdefs do
if npropdef isa AAttrPropdef then
- npropdef.init_expr(v, res)
+ npropdef.init_expr(v, recv)
+ end
+ end
+ end
+ end
+
+ # Generate a check-init-instance
+ fun generate_check_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
+ do
+ for cd in mtype.collect_mclassdefs(self.mainmodule)
+ do
+ var n = self.modelbuilder.mclassdef2nclassdef[cd]
+ for npropdef in n.n_propdefs do
+ if npropdef isa AAttrPropdef then
+ npropdef.check_expr(v, recv)
end
end
end
- v.add("return {res};")
- v.add("\}")
end
fun generate_box_instance(mtype: MClassType)
do
assert self.runtime_type_analysis.live_types.has(mtype)
assert mtype.ctype != "val*"
- var v = new GlobalCompilerVisitor(self)
+ var v = self.new_visitor
self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
v.add_decl("/* allocate {mtype} */")
end
end
+ # Initialize a visitor specific for a compiler engine
+ fun new_visitor: GlobalCompilerVisitor do return new GlobalCompilerVisitor(self)
+
+ # Generate the main C function.
+ # This function:
+ # allocate the Sys object if it exists
+ # call init if is exists
+ # call main if it exists
+ fun compile_main_function
+ do
+ var v = self.new_visitor
+ v.add_decl("int glob_argc;")
+ v.add_decl("char **glob_argv;")
+ v.add_decl("val *glob_sys;")
+ v.add_decl("int main(int argc, char** argv) \{")
+ v.add("glob_argc = argc; glob_argv = argv;")
+ var main_type = mainmodule.sys_type
+ if main_type != null then
+ var mainmodule = v.compiler.mainmodule
+ var glob_sys = v.init_instance(main_type)
+ v.add("glob_sys = {glob_sys};")
+ var main_init = mainmodule.try_get_primitive_method("init", main_type)
+ if main_init != null then
+ v.send(main_init, [glob_sys])
+ end
+ var main_method = mainmodule.try_get_primitive_method("main", main_type)
+ if main_method != null then
+ v.send(main_method, [glob_sys])
+ end
+ end
+ v.add("return 0;")
+ v.add("\}")
+
+ end
+
private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
end
abort
end
- var v = new GlobalCompilerVisitor(compiler)
+ var v = compiler.new_visitor
var selfvar = new RuntimeVariable("self", recv, recv)
if compiler.runtime_type_analysis.live_types.has(recv) then
selfvar.is_exact = true
end
sig.append(self.c_name)
sig.append("({recv.ctype} {selfvar}")
- comment.append("(self: {selfvar}")
+ comment.append("(self: {recv}")
arguments.add(selfvar)
for i in [0..mmethoddef.msignature.arity[ do
var mtype = mmethoddef.msignature.mparameters[i].mtype
mtype = self.anchor(mtype)
res = self.autobox(res, mtype)
end
+ var implicit_cast_to = nexpr.implicit_cast_to
+ if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
+ var castres = self.type_test(res, implicit_cast_to)
+ self.add("if (!{castres}) \{")
+ self.add_abort("Cast failed")
+ self.add("\}")
+ end
self.current_node = old
return res
end
# ENSURE: result.mtype.ctype == mtype.ctype
fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable
do
- if value.mtype.ctype == mtype.ctype then
+ if value.mtype == mtype then
+ return value
+ else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
return value
else if value.mtype.ctype == "val*" then
return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
end
+ # Add a check and an abort for a null reciever is needed
+ fun check_recv_notnull(recv: RuntimeVariable)
+ do
+ if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
+
+ var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
+ if maybenull then
+ self.add("if ({recv} == NULL) \{")
+ self.add_abort("Reciever is null")
+ self.add("\}")
+ end
+ end
+
# Generate a polymorphic send for the method `m' and the arguments `args'
fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable
do
res = self.new_var(ret)
end
- if types.is_empty then
- self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
- return res
- end
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)
+ var mclasstype = args.first.mtype.as(MClassType)
+ if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
+ self.add("/* skip, no method {m} */")
+ return res
+ end
+ var propdefs = m.lookup_definitions(self.compiler.mainmodule, mclasstype)
if propdefs.length == 0 then
self.add("/* skip, no method {m} */")
return res
end
assert propdefs.length == 1
var propdef = propdefs.first
- var res2 = self.call(propdef, args.first.mtype.as(MClassType), args)
+ var res2 = self.call(propdef, mclasstype, args)
if res != null then self.assign(res, res2.as(not null))
return res
end
- if args.first.mcasttype isa MNullableType then
+ var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or m.name == "==" or m.name == "!="
+ if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then
# The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
self.add("if ({args.first} == NULL) \{ /* Special null case */")
if m.name == "==" then
end
self.add "\} else"
end
+ if types.is_empty then
+ self.add("\{")
+ self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
+ self.bugtype(args.first)
+ self.add("\}")
+ return res
+ end
+
self.add("switch({args.first}->classid) \{")
var last = types.last
var defaultpropdef: nullable MMethodDef = null
do
if recv.mtype.ctype != "val*" then return
self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
+ self.add("exit(1);")
end
# Generate a polymorphic attribute is_set test
fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
do
+ check_recv_notnull(recv)
+
var types = self.collect_types(recv)
var res = self.new_var(bool_type)
if types.is_empty then
self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+ self.bugtype(recv)
return res
end
self.add("/* isset {a} on {recv.inspect} */")
# Generate a polymorphic attribute read
fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
do
+ check_recv_notnull(recv)
+
var types = self.collect_types(recv)
var ret = a.intro.static_mtype.as(not null)
if types.is_empty then
self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+ self.bugtype(recv)
return res
end
self.add("/* read {a} on {recv.inspect} */")
var ta = a.intro.static_mtype.as(not null)
ta = self.resolve_for(ta, recv2)
var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
- if not ta isa MNullableType then
+ if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then
if ta.ctype == "val*" then
self.add("if ({res2} == NULL) \{")
self.add_abort("Uninitialized attribute {a.name}")
# Generate a polymorphic attribute write
fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable)
do
+ check_recv_notnull(recv)
+
var types = self.collect_types(recv)
if types.is_empty then
self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+ self.bugtype(recv)
return
end
self.add("/* write {a} on {recv.inspect} */")
fun type_test(value: RuntimeVariable, mtype: MType): RuntimeVariable
do
mtype = self.anchor(mtype)
+ var mclasstype = mtype
+ if mtype isa MNullableType then mclasstype = mtype.mtype
+ assert mclasstype isa MClassType
+ if not self.compiler.runtime_type_analysis.live_cast_types.has(mclasstype) then
+ debug "problem: {mtype} was detected cast-dead"
+ abort
+ end
+
var types = self.collect_types(value)
var res = self.new_var(bool_type)
self.add("/* isa {mtype} on {value.inspect} */")
- if value.mcasttype isa MNullableType then
+ if value.mtype.ctype != "val*" then
+ if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+ self.add("{res} = 1;")
+ else
+ self.add("{res} = 0;")
+ end
+ return res
+ end
+ if value.mcasttype isa MNullableType or value.mcasttype isa MNullType then
self.add("if ({value} == NULL) \{")
if mtype isa MNullableType then
self.add("{res} = 1; /* isa {mtype} */")
value2 = tmp
end
if value1.mtype.ctype != "val*" then
- if value2.mtype.ctype == value1.mtype.ctype then
+ if value2.mtype == value1.mtype then
self.add("{res} = {value1} == {value2};")
else if value2.mtype.ctype != "val*" then
self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
end
# Generate a check-init-instance
- fun check_init_instance(recv: RuntimeVariable)
+ fun check_init_instance(recv: RuntimeVariable, mtype: MClassType)
do
- var mtype = self.anchor(recv.mcasttype)
- for cd in mtype.collect_mclassdefs(self.compiler.mainmodule)
- do
- var n = self.compiler.modelbuilder.mclassdef2nclassdef[cd]
- for npropdef in n.n_propdefs do
- if npropdef isa AAttrPropdef then
- # Force read to check the initialization
- self.read_attribute(npropdef.mpropdef.mproperty, recv)
- end
- end
+ if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return
+
+ mtype = self.anchor(mtype).as(MClassType)
+ if not self.compiler.runtime_type_analysis.live_types.has(mtype) then
+ debug "problem: {mtype} was detected dead"
end
+
+ self.add("CHECK_NEW_{mtype.c_name}({recv});")
end
# Generate an integer value
end
var length = self.int_instance(array.length)
self.send(self.get_property("with_native", arraytype), [res, nat, length])
- self.check_init_instance(res)
+ self.check_init_instance(res, arraytype)
self.add("\}")
return res
end
self.add("{res} = {res2};")
var length = self.int_instance(string.length)
self.send(self.get_property("with_native", mtype), [res, nat, length])
- self.check_init_instance(res)
+ self.check_init_instance(res, mtype)
self.add("{name} = {res};")
self.add("\}")
return res
var npropdef = modelbuilder.mpropdef2npropdef[self]
var oldnode = v.current_node
v.current_node = npropdef
+ self.compile_parameter_check(v, arguments)
npropdef.compile_to_c(v, self, arguments)
v.current_node = oldnode
else if self.mproperty.name == "init" then
var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
var oldnode = v.current_node
v.current_node = nclassdef
+ self.compile_parameter_check(v, arguments)
nclassdef.compile_to_c(v, self, arguments)
v.current_node = oldnode
else
end
return null
end
+
+ # Generate type checks in the C code to check covariant parameters
+ fun compile_parameter_check(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable])
+ do
+ if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
+
+ for i in [0..msignature.arity[ do
+ # skip test for vararg since the array is instantiated with the correct polymorphic type
+ if msignature.vararg_rank == i 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
+
+ # 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} */")
+ var cond = v.type_test( arguments[i+1], mtype)
+ v.add("if (!{cond}) \{")
+ #var x = v.class_name_string(arguments[i+1])
+ #var y = v.class_name_string(arguments.first)
+ #v.add("fprintf(stderr, \"expected {mtype} (self is %s), got %s for {arguments[i+1].inspect}\\n\", {y}, {x});")
+ v.add_abort("Cast failed")
+ v.add("\}")
+ end
+ end
end
redef class APropdef
var nat = v.class_name_string(arguments.first)
v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
return
+ else if pname == "force_garbage_collection" then
+ v.add("GC_gcollect();")
+ return
end
v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
debug("Not implemented {mpropdef}")
var externname
var nextern = self.n_extern
if nextern == null then
- debug("{mpropdef} need extern name")
+ v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
+ v.add("exit(1);")
return
end
externname = nextern.text.substring(1, nextern.text.length-2)
do
var nexpr = self.n_expr
if nexpr != null then
+ 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])
v.frame = frame
var value = v.expr(nexpr, self.mpropdef.static_mtype)
v.write_attribute(self.mpropdef.mproperty, recv, value)
v.frame = old_frame
+ v.current_node = oldnode
end
end
+
+ fun check_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
+ do
+ var nexpr = self.n_expr
+ if nexpr != null then return
+
+ 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])
+ v.frame = frame
+ # Force read to check the initialization
+ v.read_attribute(self.mpropdef.mproperty, recv)
+ v.frame = old_frame
+ v.current_node = oldnode
+ end
end
redef class AClassdef
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);")
+ v.add_abort("Deferred method called")
end
redef fun can_inline do return true
# 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 YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
var mtype = self.mtype
if mtype == null then
redef class AAssertExpr
redef fun stmt(v)
do
+ if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
+
var cond = v.expr_bool(self.n_expr)
v.add("if (!{cond}) \{")
v.stmt(self.n_else)
do
var i1 = v.expr(self.n_expr, null)
var i2 = v.expr(self.n_expr2, null)
- var res = v.init_instance(self.mtype.as(MClassType))
+ var mtype = self.mtype.as(MClassType)
+ var res = v.init_instance(mtype)
var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
- v.check_init_instance(res)
+ v.check_init_instance(res, mtype)
return res
end
end
do
var i1 = v.expr(self.n_expr, null)
var i2 = v.expr(self.n_expr2, null)
- var res = v.init_instance(self.mtype.as(MClassType))
+ var mtype = self.mtype.as(MClassType)
+ var res = v.init_instance(mtype)
var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
- v.check_init_instance(res)
+ v.check_init_instance(res, mtype)
return res
end
end
redef fun expr(v)
do
var i = v.expr(self.n_expr, null)
+ if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
+
var cond = v.type_test(i, self.mtype.as(not null))
v.add("if (!{cond}) \{")
v.add_abort("Cast failed")
redef fun expr(v)
do
var i = v.expr(self.n_expr, null)
+ if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
+
v.add("if ({i} == NULL) \{")
v.add_abort("Cast failed")
v.add("\}")
#self.debug("got {res2} from {mproperty}. drop {recv}")
return res2
end
- v.check_init_instance(recv)
+ v.check_init_instance(recv, mtype)
return recv
end
end