X-Git-Url: http://nitlanguage.org diff --git a/src/global_compiler.nit b/src/global_compiler.nit index daffd0f..2f63d20 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -38,10 +38,26 @@ redef class ToolContext # --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 @@ -55,7 +71,7 @@ redef class ModelBuilder 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 ") v.add_decl("#include ") @@ -96,6 +112,7 @@ redef class ModelBuilder 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 @@ -342,7 +359,7 @@ class GlobalCompiler 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" @@ -366,24 +383,57 @@ class GlobalCompiler 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} */") @@ -689,7 +739,7 @@ private class CustomizedRuntimeFunction 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 @@ -904,6 +954,13 @@ class GlobalCompilerVisitor 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 @@ -933,7 +990,9 @@ class GlobalCompilerVisitor # 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) @@ -1136,6 +1195,19 @@ class GlobalCompilerVisitor 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 @@ -1153,24 +1225,26 @@ class GlobalCompilerVisitor 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 @@ -1196,6 +1270,14 @@ class GlobalCompilerVisitor 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 @@ -1333,17 +1415,21 @@ class GlobalCompilerVisitor 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} */") @@ -1380,6 +1466,8 @@ class GlobalCompilerVisitor # 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) @@ -1388,6 +1476,7 @@ class GlobalCompilerVisitor 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} */") @@ -1403,7 +1492,7 @@ class GlobalCompilerVisitor 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}") @@ -1427,10 +1516,13 @@ class GlobalCompilerVisitor # 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} */") @@ -1562,7 +1654,7 @@ class GlobalCompilerVisitor 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}*/") @@ -1590,19 +1682,16 @@ class GlobalCompilerVisitor 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 @@ -1629,7 +1718,7 @@ class GlobalCompilerVisitor 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 @@ -1650,7 +1739,7 @@ class GlobalCompilerVisitor 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 @@ -1749,6 +1838,8 @@ redef class MMethodDef # 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 @@ -2065,6 +2156,9 @@ redef class AInternMethPropdef 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}") @@ -2077,7 +2171,8 @@ redef class AExternMethPropdef 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) @@ -2142,14 +2237,33 @@ redef class AAttrPropdef 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 @@ -2193,7 +2307,6 @@ redef class AExpr # 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 @@ -2415,6 +2528,8 @@ end 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) @@ -2551,9 +2666,10 @@ redef class ACrangeExpr 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 @@ -2563,9 +2679,10 @@ redef class AOrangeExpr 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 @@ -2604,6 +2721,8 @@ redef class AAsCastExpr 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") @@ -2616,6 +2735,8 @@ redef class AAsNotnullExpr 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("\}") @@ -2747,7 +2868,7 @@ redef class ANewExpr #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