|
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/. */ |
|
4 |
|
5 require({ after_gcc_pass: "cfg" }); |
|
6 |
|
7 include("gcc_util.js"); |
|
8 include("unstable/lazy_types.js"); |
|
9 |
|
10 function process_type(c) |
|
11 { |
|
12 if ((c.kind == 'class' || c.kind == 'struct') && |
|
13 !c.isIncomplete) |
|
14 isStack(c); |
|
15 } |
|
16 |
|
17 /** |
|
18 * A BlameChain records a chain of one or more location/message pairs. It |
|
19 * can be used to issue a complex error message such as: |
|
20 * location: error: Allocated class Foo on the heap |
|
21 * locationofFoo: class Foo inherits from class Bar |
|
22 * locationofBar: in class Bar |
|
23 * locationofBarMem: Member Bar::mFoo |
|
24 * locationofBaz: class Baz is annotated NS_STACK |
|
25 */ |
|
26 function BlameChain(loc, message, prev) |
|
27 { |
|
28 this.loc = loc; |
|
29 this.message = message; |
|
30 this.prev = prev; |
|
31 } |
|
32 |
|
33 BlameChain.prototype.toString = function() |
|
34 { |
|
35 let loc = this.loc; |
|
36 if (loc === undefined) |
|
37 loc = "<unknown location>"; |
|
38 |
|
39 let str = '%s: %s'.format(loc.toString(), this.message); |
|
40 if (this.prev) |
|
41 str += "\n%s".format(this.prev); |
|
42 return str; |
|
43 }; |
|
44 |
|
45 function isStack(c) |
|
46 { |
|
47 function calculate() |
|
48 { |
|
49 if (hasAttribute(c, 'NS_stack')) |
|
50 return new BlameChain(c.loc, '%s %s is annotated NS_STACK_CLASS'.format(c.kind, c.name)); |
|
51 |
|
52 for each (let base in c.bases) { |
|
53 let r = isStack(base.type); |
|
54 if (r != null) |
|
55 return new BlameChain(c.loc, '%s %s is a base of %s %s'.format(base.type.kind, base.type.name, c.kind, c.name), r); |
|
56 } |
|
57 |
|
58 for each (let member in c.members) { |
|
59 if (member.isFunction) |
|
60 continue; |
|
61 |
|
62 if (hasAttribute(member, 'NS_okonheap')) |
|
63 continue; |
|
64 |
|
65 let type = member.type; |
|
66 while (true) { |
|
67 if (type === undefined) |
|
68 break; |
|
69 |
|
70 if (type.isArray) { |
|
71 type = type.type; |
|
72 continue; |
|
73 } |
|
74 |
|
75 if (type.typedef) { |
|
76 if (hasAttribute(type, 'NS_stack')) |
|
77 return true; |
|
78 |
|
79 type = type.typedef; |
|
80 continue; |
|
81 } |
|
82 break; |
|
83 } |
|
84 |
|
85 if (type === undefined) { |
|
86 warning("incomplete type for member " + member + ".", member.loc); |
|
87 continue; |
|
88 } |
|
89 |
|
90 if (type.isPointer || type.isReference) |
|
91 continue; |
|
92 |
|
93 if (!type.kind || (type.kind != 'class' && type.kind != 'struct')) |
|
94 continue; |
|
95 |
|
96 let r = isStack(type); |
|
97 if (r != null) |
|
98 return new BlameChain(c.loc, 'In class %s'.format(c.name), |
|
99 new BlameChain(member.loc, 'Member %s'.format(member.name), r)); |
|
100 } |
|
101 return null; |
|
102 } |
|
103 |
|
104 if (!c.hasOwnProperty('isStack')) |
|
105 c.isStack = calculate(); |
|
106 |
|
107 return c.isStack; |
|
108 } |
|
109 |
|
110 function process_tree(fn) |
|
111 { |
|
112 if (hasAttribute(dehydra_convert(fn), 'NS_suppress_stackcheck')) |
|
113 return; |
|
114 |
|
115 let cfg = function_decl_cfg(fn); |
|
116 |
|
117 for (let bb in cfg_bb_iterator(cfg)) { |
|
118 let it = bb_isn_iterator(bb); |
|
119 for (let isn in it) { |
|
120 if (isn.tree_code() != GIMPLE_CALL) |
|
121 continue; |
|
122 |
|
123 let name = gimple_call_function_name(isn); |
|
124 if (name != "operator new" && name != "operator new []") |
|
125 continue; |
|
126 |
|
127 // ignore placement new |
|
128 // TODO? ensure 2nd arg is local stack variable |
|
129 if (gimple_call_num_args(isn) == 2 && |
|
130 TREE_TYPE(gimple_call_arg(isn, 1)).tree_code() == POINTER_TYPE) |
|
131 continue; |
|
132 |
|
133 let newLhs = gimple_call_lhs(isn); |
|
134 if (!newLhs) |
|
135 error("Non assigning call to operator new", location_of(isn)); |
|
136 |
|
137 // if isn is the last of its block there are other problems... |
|
138 assign = it.next(); |
|
139 |
|
140 // Calls to |new| are always followed by an assignment, casting the void ptr to which |
|
141 // |new| was assigned, to a ptr variable of the same type as the allocated object. |
|
142 // Exception: explicit calls to |::operator new (size_t)|, which can be ignored. |
|
143 if (assign.tree_code() != GIMPLE_ASSIGN) |
|
144 continue; |
|
145 |
|
146 let assignRhs = gimple_op(assign, 1); |
|
147 if (newLhs != assignRhs) |
|
148 continue; |
|
149 |
|
150 let assignLhs = gimple_op(assign, 0); |
|
151 let type = TREE_TYPE(TREE_TYPE(assignLhs)); |
|
152 let dehydraType = dehydra_convert(type); |
|
153 |
|
154 let r = isStack(dehydraType); |
|
155 if (r) |
|
156 warning("constructed object of type '%s' not on the stack: %s".format(dehydraType.name, r), location_of(isn)); |
|
157 } |
|
158 } |
|
159 } |