зеркало из https://github.com/mozilla/pjs.git
bug 432917: MUST_FLOW_THROUGH static check. r=bsmedberg
This commit is contained in:
Родитель
ab051ffc60
Коммит
83d0e98890
|
@ -511,6 +511,7 @@ DEHYDRA_MODULES = \
|
|||
TREEHYDRA_MODULES = \
|
||||
$(topsrcdir)/xpcom/analysis/outparams.js \
|
||||
$(topsrcdir)/xpcom/analysis/stack.js \
|
||||
$(topsrcdir)/xpcom/analysis/flow.js \
|
||||
$(NULL)
|
||||
|
||||
DEHYDRA_ARGS = \
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
require({ version: '1.8' });
|
||||
require({ after_gcc_pass: 'cfg' });
|
||||
|
||||
include('treehydra.js');
|
||||
|
||||
include('util.js');
|
||||
include('gcc_util.js');
|
||||
include('gcc_print.js');
|
||||
include('unstable/adts.js');
|
||||
include('unstable/analysis.js');
|
||||
include('unstable/esp.js');
|
||||
|
||||
/* This implements the control flows-through analysis in bug 432917 */
|
||||
var Zero_NonZero = {}
|
||||
include('unstable/zero_nonzero.js', Zero_NonZero);
|
||||
|
||||
MapFactory.use_injective = true;
|
||||
|
||||
// Print a trace for each function analyzed
|
||||
let TRACE_FUNCTIONS = 0;
|
||||
// Trace operation of the ESP analysis, use 2 or 3 for more detail
|
||||
let TRACE_ESP = 0;
|
||||
// Print time-taken stats
|
||||
let TRACE_PERF = 0;
|
||||
|
||||
function process_tree(fndecl) {
|
||||
// At this point we have a function we want to analyze
|
||||
if (TRACE_FUNCTIONS) {
|
||||
print('* function ' + decl_name(fndecl));
|
||||
print(' ' + loc_string(location_of(fndecl)));
|
||||
}
|
||||
if (TRACE_PERF) timer_start(fstring);
|
||||
|
||||
let cfg = function_decl_cfg(fndecl);
|
||||
|
||||
|
||||
try {
|
||||
let trace = TRACE_ESP;
|
||||
let a = new FlowCheck(cfg, trace);
|
||||
a.run();
|
||||
} catch (e if e == "skip") {
|
||||
return
|
||||
}
|
||||
print("checked " + decl_name(fndecl))
|
||||
if (cfg.x_exit_block_ptr.stateIn.substates)
|
||||
for each (let substate in cfg.x_exit_block_ptr.stateIn.substates.getValues()) {
|
||||
for each (let v in substate.getVariables()) {
|
||||
let var_state= substate.get (v)
|
||||
let blame = substate.getBlame(v)
|
||||
if (var_state != ESP.TOP && typeof var_state == 'string')
|
||||
error(decl_name(fndecl) + ": Control did not flow through " +var_state, location_of(blame))
|
||||
}
|
||||
}
|
||||
|
||||
if (TRACE_PERF) timer_stop(fstring);
|
||||
}
|
||||
|
||||
let track_return_loc = 0;
|
||||
const FLOW_THROUGH = "MUST_FLOW_THROUGH"
|
||||
|
||||
function FlowCheck(cfg, trace) {
|
||||
let found = create_decl_set(); // ones we already found
|
||||
for (let bb in cfg_bb_iterator(cfg)) {
|
||||
for (let isn in bb_isn_iterator(bb)) {
|
||||
switch (isn.tree_code()) {
|
||||
case CALL_EXPR: {
|
||||
let fn = call_function_decl(isn)
|
||||
if (!fn || decl_name(fn) != FLOW_THROUGH)
|
||||
continue;
|
||||
this.must_flow_fn = fn
|
||||
break
|
||||
}
|
||||
case RETURN_EXPR: {
|
||||
let ret_expr = isn.operands()[0]
|
||||
if (track_return_loc && ret_expr)
|
||||
switch (ret_expr.tree_code()) {
|
||||
case GIMPLE_MODIFY_STMT:
|
||||
this.rval = ret_expr.operands()[1].tree_check(VAR_DECL)
|
||||
break;
|
||||
case RESULT_DECL:
|
||||
this.rval = ret_expr
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unhandled return expression")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.must_flow_fn)
|
||||
throw "skip"
|
||||
|
||||
let psvar_list = [new ESP.PropVarSpec(this.must_flow_fn, true)]
|
||||
|
||||
if (this.rval)
|
||||
psvar_list.push(new ESP.PropVarSpec(this.rval))
|
||||
|
||||
this.zeroNonzero = new Zero_NonZero.Zero_NonZero()
|
||||
ESP.Analysis.call(this, cfg, psvar_list, Zero_NonZero.meet, trace);
|
||||
}
|
||||
|
||||
FlowCheck.prototype = new ESP.Analysis;
|
||||
|
||||
function char_star_arg2string(tree) {
|
||||
return TREE_STRING_POINTER(tree.tree_check(ADDR_EXPR).operands()[0].tree_check(ARRAY_REF).operands()[0])
|
||||
}
|
||||
|
||||
// State transition function. Mostly, we delegate everything to
|
||||
// another function as either an assignment or a call.
|
||||
FlowCheck.prototype.flowState = function(isn, state) {
|
||||
switch (TREE_CODE(isn)) {
|
||||
case CALL_EXPR: {
|
||||
let fn = call_function_decl(isn)
|
||||
if (fn == this.must_flow_fn)
|
||||
state.assignValue(fn, char_star_arg2string(call_arg(isn, 0)), isn)
|
||||
break
|
||||
}
|
||||
case LABEL_EXPR: {
|
||||
let label = decl_name(isn.operands()[0])
|
||||
for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
|
||||
if (label != value) continue
|
||||
// reached the goto label we wanted =D
|
||||
state.assignValue(this.must_flow_fn, ESP.TOP, isn)
|
||||
}
|
||||
break
|
||||
}
|
||||
case RETURN_EXPR: {
|
||||
for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
|
||||
if (typeof value != 'string') continue
|
||||
let loc;
|
||||
if (this.rval)
|
||||
for ([value, blame] in state.yieldPreconditions(this.rval)) {
|
||||
loc = value
|
||||
break
|
||||
}
|
||||
error("return without going through label "+ value, loc);
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
case GIMPLE_MODIFY_STMT:
|
||||
if (track_return_loc && isn.operands()[0] == this.rval) {
|
||||
state.assignValue(this.rval, location_of(isn), isn)
|
||||
break
|
||||
}
|
||||
default:
|
||||
this.zeroNonzero.flowState(isn, state)
|
||||
}
|
||||
}
|
||||
|
||||
// State transition function to apply branch filters. This is kind
|
||||
// of boilerplate--we're just handling some stuff that GCC generates.
|
||||
FlowCheck.prototype.flowStateCond = function(isn, truth, state) {
|
||||
this.zeroNonzero.flowStateCond (isn, truth, state)
|
||||
}
|
|
@ -100,9 +100,16 @@ OUTPARAMS_PASS_TESTCASES = \
|
|||
onull2.cpp \
|
||||
$(NULL)
|
||||
|
||||
FLOW_PASS_TESTCASES = \
|
||||
flow_through_pass.cpp
|
||||
|
||||
FLOW_FAILURE_TESTCASES = \
|
||||
flow_through_fail.cpp
|
||||
|
||||
STATIC_FAILURE_TESTCASES = \
|
||||
$(FINAL_FAILURE_TESTCASES) \
|
||||
$(STACK_FAILURE_TESTCASES) \
|
||||
$(FLOW_FAILURE_TESTCASES)
|
||||
$(NULL)
|
||||
|
||||
STATIC_WARNING_TESTCASES = \
|
||||
|
@ -112,6 +119,7 @@ STATIC_WARNING_TESTCASES = \
|
|||
STATIC_PASS_TESTCASES = \
|
||||
$(OUTPARAMS_PASS_TESTCASES) \
|
||||
$(STACK_PASS_TESTCASES) \
|
||||
$(FLOW_PASS_TESTCASES) \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
static void MUST_FLOW_THROUGH(const char *label) {
|
||||
}
|
||||
|
||||
int test(int x, int y) {
|
||||
MUST_FLOW_THROUGH("out");
|
||||
|
||||
if (!x) {
|
||||
x = y;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return y;
|
||||
out:
|
||||
x--;
|
||||
return x;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
static void MUST_FLOW_THROUGH(const char *label) {
|
||||
}
|
||||
|
||||
int test(int x, int y) {
|
||||
if (x == 3)
|
||||
return 0;
|
||||
|
||||
if(x)
|
||||
MUST_FLOW_THROUGH("out");
|
||||
|
||||
if (x) {
|
||||
x = y;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return y;
|
||||
out:
|
||||
x--;
|
||||
return x;
|
||||
}
|
Загрузка…
Ссылка в новой задаче