Backed out changeset 0b84a9bee07a (bug 937429) for Android test_videocontrols_standal...
[gecko.git] / xpcom / analysis / flow.js
blob5153f30568e80a58686e4417441ad16794022ad9
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 require({ version: '1.8' });
6 require({ after_gcc_pass: 'cfg' });
8 include('treehydra.js');
10 include('util.js');
11 include('gcc_util.js');
12 include('gcc_print.js');
13 include('unstable/adts.js');
14 include('unstable/analysis.js');
15 include('unstable/esp.js');
17 /* This implements the control flows-through analysis in bug 432917 */
18 var Zero_NonZero = {}
19 include('unstable/zero_nonzero.js', Zero_NonZero);
21 MapFactory.use_injective = true;
23 // Print a trace for each function analyzed
24 let TRACE_FUNCTIONS = 0;
25 // Trace operation of the ESP analysis, use 2 or 3 for more detail
26 let TRACE_ESP = 0;
27 // Print time-taken stats
28 let TRACE_PERF = 0;
30 function process_tree(fndecl) {
31   // At this point we have a function we want to analyze
32   if (TRACE_FUNCTIONS) {
33     print('* function ' + decl_name(fndecl));
34     print('    ' + loc_string(location_of(fndecl)));
35   }
36   if (TRACE_PERF) timer_start(fstring);
38   let cfg = function_decl_cfg(fndecl);
40   try {
41     let trace = TRACE_ESP;
42     let a = new FlowCheck(cfg, trace);
43     a.run();
44   } catch (e if e == "skip") {
45     return
46   }
47   print("checked " + decl_name(fndecl))
48   if (cfg.x_exit_block_ptr.stateIn.substates)
49     for each (let substate in cfg.x_exit_block_ptr.stateIn.substates.getValues()) {
50       for each (let v in substate.getVariables()) {
51         let var_state= substate.get (v)
52         let blame = substate.getBlame(v)
53         if (var_state != ESP.TOP && typeof var_state == 'string')
54           error(decl_name(fndecl) + ": Control did not flow through " +var_state, location_of(blame))
55       }
56     }
57   
58   if (TRACE_PERF) timer_stop(fstring);
61 let track_return_loc = 0;
62 const FLOW_THROUGH = "MUST_FLOW_THROUGH"
64 function FlowCheck(cfg, trace) {
65   let found = create_decl_set(); // ones we already found
66   for (let bb in cfg_bb_iterator(cfg)) {
67     for (let isn in bb_isn_iterator(bb)) {
68       switch (isn.tree_code()) {
69       case GIMPLE_CALL: {
70         let fn = gimple_call_fndecl(isn)
71         if (!fn || decl_name(fn) != FLOW_THROUGH)
72           continue;
73         this.must_flow_fn = fn
74         break
75       }
76       case GIMPLE_RETURN:
77         let ret_expr = return_expr(isn);
78         if (track_return_loc && ret_expr) {
79           TREE_CHECK(ret_expr, VAR_DECL, RESULT_DECL);
80           this.rval = ret_expr;
81         }
82         break;
83       }
84     }
85   }
86   if (!this.must_flow_fn)
87     throw "skip"
89   let psvar_list = [new ESP.PropVarSpec(this.must_flow_fn, true)]
90   
91   if (this.rval)
92     psvar_list.push(new ESP.PropVarSpec(this.rval))
94   this.zeroNonzero = new Zero_NonZero.Zero_NonZero()
95   ESP.Analysis.call(this, cfg, psvar_list, Zero_NonZero.meet, trace);
98 FlowCheck.prototype = new ESP.Analysis;
100 function char_star_arg2string(tree) {
101   return TREE_STRING_POINTER(tree.tree_check(ADDR_EXPR).operands()[0].tree_check(ARRAY_REF).operands()[0])
104 // State transition function. Mostly, we delegate everything to
105 // another function as either an assignment or a call.
106 FlowCheck.prototype.flowState = function(isn, state) {
107   switch (TREE_CODE(isn)) {
108   case GIMPLE_CALL: {
109     let fn = gimple_call_fndecl(isn)
110     if (fn == this.must_flow_fn)
111       state.assignValue(fn, char_star_arg2string(gimple_call_arg(isn, 0)), isn)
112     break
113   }
114   case GIMPLE_LABEL: {
115     let label = decl_name(gimple_op(isn, 0))
116     for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
117       if (label != value) continue
118       // reached the goto label we wanted =D
119       state.assignValue(this.must_flow_fn, ESP.TOP, isn)
120     }
121     break
122   }
123   case GIMPLE_RETURN:
124     for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
125       if (typeof value != 'string') continue
126       let loc;
127       if (this.rval)
128         for ([value, blame] in state.yieldPreconditions(this.rval)) {
129           loc = value
130           break
131         }
132       error("return without going through label "+ value, loc);
133       return  
134     }
135     break
136   case GIMPLE_ASSIGN:
137     if (track_return_loc && gimple_op(isn, 0) == this.rval) {
138       state.assignValue(this.rval, location_of(isn), isn)
139       break
140     }
141   default:
142     this.zeroNonzero.flowState(isn, state)
143   }
146 // State transition function to apply branch filters. This is kind
147 // of boilerplate--we're just handling some stuff that GCC generates.
148 FlowCheck.prototype.flowStateCond = function(isn, truth, state) {
149   this.zeroNonzero.flowStateCond (isn, truth, state)