2 * Check that only JS_REQUIRES_STACK/JS_FORCES_STACK functions, and functions
3 * that have called a JS_FORCES_STACK function, access cx->fp directly or
7 require({ after_gcc_pass
: 'cfg' });
8 include('gcc_util.js');
9 include('unstable/adts.js');
10 include('unstable/analysis.js');
11 include('unstable/lazy_types.js');
12 include('unstable/esp.js');
14 var Zero_NonZero
= {};
15 include('unstable/zero_nonzero.js', Zero_NonZero
);
17 // Tell MapFactory we don't need multimaps (a speed optimization).
18 MapFactory
.use_injective
= true;
21 * There are two regions in the program: RED and GREEN. Functions and member
22 * variables may be declared RED in the C++ source. GREEN is the default.
24 * RED signals danger. A GREEN part of a function must not call a RED function
25 * or access a RED member.
27 * The body of a RED function is all red. The body of a GREEN function is all
28 * GREEN by default, but parts dominated by a call to a TURN_RED function are
29 * red. This way GREEN functions can safely access RED stuff by calling a
30 * TURN_RED function as preparation.
32 * The analysis does not attempt to prove anything about the body of a TURN_RED
33 * function. (Both annotations are trusted; only unannotated code is checked
36 const RED
= 'JS_REQUIRES_STACK';
37 const TURN_RED
= 'JS_FORCES_STACK';
39 function attrs(tree
) {
40 let a
= DECL_P(tree
) ? DECL_ATTRIBUTES(tree
) : TYPE_ATTRIBUTES(TREE_TYPE(tree
));
41 return translate_attributes(a
);
44 function hasUserAttribute(tree
, attrname
) {
45 let attributes
= attrs(tree
);
47 for (let i
= 0; i
< attributes
.length
; i
++) {
48 let attr
= attributes
[i
];
49 if (attr
.name
== 'user' && attr
.value
.length
== 1 && attr
.value
[0] == attrname
)
57 * x is an expression or decl. These functions assume that
59 function isRed(x
) { return hasUserAttribute(x
, RED
); }
60 function isTurnRed(x
) { return hasUserAttribute(x
, TURN_RED
); }
62 function process_tree(fndecl
)
64 if (!(isRed(fndecl
) || isTurnRed(fndecl
))) {
65 // Ordinarily a user of ESP runs the analysis, then generates output based
66 // on the results. But in our case (a) we need sub-basic-block resolution,
67 // which ESP doesn't keep; (b) it so happens that even though ESP can
68 // iterate over blocks multiple times, in our case that won't cause
69 // spurious output. (It could cause us to the same error message each time
70 // through--but that's easily avoided.) Therefore we generate the output
71 // while the ESP analysis is running.
72 let a
= new RedGreenCheck(fndecl
, 0);
78 function RedGreenCheck(fndecl
, trace
) {
79 //print("RedGreenCheck: " + fndecl.toCString());
80 this._fndecl
= fndecl
;
82 // Tell ESP that fndecl is a "property variable". This makes ESP track it in
83 // a flow-sensitive way. The variable will be 1 in RED regions and "don't
84 // know" in GREEN regions. (We are technically lying to ESP about fndecl
85 // being a variable--what we really want is a synthetic variable indicating
86 // RED/GREEN state, but ESP operates on GCC decl nodes.)
87 this._state_var_decl
= fndecl
;
88 let state_var
= new ESP
.PropVarSpec(this._state_var_decl
, true, undefined);
90 // Call base class constructor.
91 let cfg
= function_decl_cfg(fndecl
);
92 ESP
.Analysis
.apply(this, [cfg
, [state_var
], Zero_NonZero
.meet
, trace
]);
93 this.join
= Zero_NonZero
.join
;
95 // Preprocess all instructions in the cfg to determine whether this analysis
96 // is necessary and gather some information we'll use later.
98 // Each isn may include a function call, an assignment, and/or some reads.
99 // Using walk_tree to walk the isns is a little crazy but robust.
102 for (let bb
in cfg_bb_iterator(cfg
)) {
103 for (let isn
in bb_isn_iterator(bb
)) {
104 walk_tree(isn
, function(t
, stack
) {
105 switch (TREE_CODE(t
)) {
108 let varName
= dehydra_convert(t
).name
;
109 // location_of(t) is the location of the declaration.
110 isn
.redInfo
= ["cannot access JS_REQUIRES_STACK variable " + varName
,
111 location_of(stack
[stack
.length
- 1])];
117 let callee
= call_function_decl(t
);
120 let calleeName
= dehydra_convert(callee
).name
;
121 isn
.redInfo
= ["cannot call JS_REQUIRES_STACK function " + calleeName
,
124 } else if (isTurnRed(callee
)) {
135 // Initialize mixin for infeasible-path elimination.
136 this._zeroNonzero
= new Zero_NonZero
.Zero_NonZero();
139 RedGreenCheck
.prototype = new ESP
.Analysis
;
141 RedGreenCheck
.prototype.flowStateCond = function(isn
, truth
, state
) {
142 // forward event to mixin
143 this._zeroNonzero
.flowStateCond(isn
, truth
, state
);
146 RedGreenCheck
.prototype.flowState = function(isn
, state
) {
147 // forward event to mixin
148 //try { // The try/catch here is a workaround for some baffling bug in zero_nonzero.
149 this._zeroNonzero
.flowState(isn
, state
);
151 // warning(exc, location_of(isn));
152 // warning("(Remove the workaround in jsstack.js and recompile to get a JS stack trace.)",
153 // location_of(isn));
155 let green
= (state
.get(this._state_var_decl
) != 1);
156 let redInfo
= isn
.redInfo
;
157 if (green
&& redInfo
) {
158 error(redInfo
[0], redInfo
[1]);
159 delete isn
.redInfo
; // avoid duplicate messages about this instruction
162 // If we call a TURNS_RED function, it doesn't take effect until after the
163 // whole isn finishes executing (the most conservative rule).
165 state
.assignValue(this._state_var_decl
, 1, isn
);