blob: ec52cc656895a7f214f77b66a390af3c0bf7399e [file] [log] [blame]
Elliott Hughes6c1a3942011-08-17 15:00:06 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "indirect_reference_table.h"
18#include "reference_table.h"
Ian Rogerscdd1d2d2011-08-18 09:58:17 -070019#include "utils.h"
Elliott Hughes6c1a3942011-08-17 15:00:06 -070020
21#include <cstdlib>
22
23namespace art {
24
25// TODO: implement this for art. (only needed for non-CheckJNI operation.)
26static void AbortMaybe() {
27 // If CheckJNI is on, it'll give a more detailed error before aborting.
28 // Otherwise, we want to abort rather than hand back a bad reference.
29// if (!gDvmJni.useCheckJni) {
30// LOG(FATAL) << "bye!";
31// }
32}
33
34IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
35 size_t maxCount, IndirectRefKind desiredKind)
36{
37 CHECK_GT(initialCount, 0U);
38 CHECK_LE(initialCount, maxCount);
39 CHECK_NE(desiredKind, kInvalid);
40
41 table_ = reinterpret_cast<Object**>(malloc(initialCount * sizeof(Object*)));
42 CHECK(table_ != NULL);
43#ifndef NDEBUG
44 memset(table_, 0xd1, initialCount * sizeof(Object*));
45#endif
46
47 slot_data_ = reinterpret_cast<IndirectRefSlot*>(calloc(initialCount, sizeof(IndirectRefSlot)));
48 CHECK(slot_data_ != NULL);
49
50 segmentState.all = IRT_FIRST_SEGMENT;
51 alloc_entries_ = initialCount;
52 max_entries_ = maxCount;
53 kind_ = desiredKind;
54}
55
56IndirectReferenceTable::~IndirectReferenceTable() {
57 free(table_);
58 free(slot_data_);
59 table_ = NULL;
60 slot_data_ = NULL;
61 alloc_entries_ = max_entries_ = -1;
62}
63
64/*
65 * Make sure that the entry at "idx" is correctly paired with "iref".
66 */
67bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int idx) const {
68 Object* obj = table_[idx];
69 IndirectRef checkRef = ToIndirectRef(obj, idx);
70 if (checkRef != iref) {
71 LOG(ERROR) << "JNI ERROR (app bug): attempt to " << what
72 << " stale " << kind_ << " " << iref
73 << " (should be " << checkRef << ")";
74 AbortMaybe();
75 return false;
76 }
77 return true;
78}
79
80IndirectRef IndirectReferenceTable::Add(uint32_t cookie, Object* obj) {
81 IRTSegmentState prevState;
82 prevState.all = cookie;
83 size_t topIndex = segmentState.parts.topIndex;
84
85 DCHECK(obj != NULL);
Ian Rogerscdd1d2d2011-08-18 09:58:17 -070086 // TODO: stronger sanity check on the object (such as in heap)
87 DCHECK(IsAligned(reinterpret_cast<intptr_t>(obj), 8));
Elliott Hughes6c1a3942011-08-17 15:00:06 -070088 DCHECK(table_ != NULL);
89 DCHECK_LE(alloc_entries_, max_entries_);
90 DCHECK_GE(segmentState.parts.numHoles, prevState.parts.numHoles);
91
92 if (topIndex == alloc_entries_) {
93 /* reached end of allocated space; did we hit buffer max? */
94 if (topIndex == max_entries_) {
95 LOG(ERROR) << "JNI ERROR (app bug): " << kind_ << " table overflow "
96 << "(max=" << max_entries_ << ")";
97 Dump();
98 LOG(FATAL); // TODO: operator<< for IndirectReferenceTable
99 }
100
101 size_t newSize = alloc_entries_ * 2;
102 if (newSize > max_entries_) {
103 newSize = max_entries_;
104 }
105 DCHECK_GT(newSize, alloc_entries_);
106
107 table_ = (Object**) realloc(table_, newSize * sizeof(Object*));
108 slot_data_ = (IndirectRefSlot*) realloc(slot_data_, newSize * sizeof(IndirectRefSlot));
109 if (table_ == NULL || slot_data_ == NULL) {
110 LOG(ERROR) << "JNI ERROR (app bug): unable to expand "
111 << kind_ << " table (from "
112 << alloc_entries_ << " to " << newSize
113 << ", max=" << max_entries_ << ")";
114 Dump();
115 LOG(FATAL); // TODO: operator<< for IndirectReferenceTable
116 }
117
118 // Clear the newly-allocated slot_data_ elements.
119 memset(slot_data_ + alloc_entries_, 0, (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
120
121 alloc_entries_ = newSize;
122 }
123
124 /*
125 * We know there's enough room in the table. Now we just need to find
126 * the right spot. If there's a hole, find it and fill it; otherwise,
127 * add to the end of the list.
128 */
129 IndirectRef result;
130 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
131 if (numHoles > 0) {
132 DCHECK_GT(topIndex, 1U);
133 /* find the first hole; likely to be near the end of the list */
134 Object** pScan = &table_[topIndex - 1];
135 DCHECK(*pScan != NULL);
136 while (*--pScan != NULL) {
137 DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
138 }
139 UpdateSlotAdd(obj, pScan - table_);
140 result = ToIndirectRef(obj, pScan - table_);
141 *pScan = obj;
142 segmentState.parts.numHoles--;
143 } else {
144 /* add to the end */
145 UpdateSlotAdd(obj, topIndex);
146 result = ToIndirectRef(obj, topIndex);
147 table_[topIndex++] = obj;
148 segmentState.parts.topIndex = topIndex;
149 }
150
151 DCHECK(result != NULL);
152 return result;
153}
154
155/*
156 * Verify that the indirect table lookup is valid.
157 *
158 * Returns "false" if something looks bad.
159 */
160bool IndirectReferenceTable::GetChecked(IndirectRef iref) const {
161 if (iref == NULL) {
162 LOG(WARNING) << "Attempt to look up NULL " << kind_;
163 return false;
164 }
165 if (GetIndirectRefKind(iref) == kInvalid) {
166 LOG(ERROR) << "JNI ERROR (app bug): invalid " << kind_ << " " << iref;
167 AbortMaybe();
168 return false;
169 }
170
171 int topIndex = segmentState.parts.topIndex;
172 int idx = ExtractIndex(iref);
173 if (idx >= topIndex) {
174 /* bad -- stale reference? */
175 LOG(ERROR) << "JNI ERROR (app bug): accessed stale " << kind_ << " " << iref << " (index " << idx << " in a table of size " << topIndex << ")";
176 AbortMaybe();
177 return false;
178 }
179
180 if (table_[idx] == NULL) {
181 LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
182 AbortMaybe();
183 return false;
184 }
185
186 if (!CheckEntry("use", iref, idx)) {
187 return false;
188 }
189
190 return true;
191}
192
193static int LinearScan(IndirectRef iref, int bottomIndex, int topIndex, Object** table) {
194 for (int i = bottomIndex; i < topIndex; ++i) {
195 if (table[i] == reinterpret_cast<Object*>(iref)) {
196 return i;
197 }
198 }
199 return -1;
200}
201
202bool IndirectReferenceTable::Contains(IndirectRef iref) const {
203 return LinearScan(iref, 0, segmentState.parts.topIndex, table_) != -1;
204}
205
206/*
207 * Remove "obj" from "pRef". We extract the table offset bits from "iref"
208 * and zap the corresponding entry, leaving a hole if it's not at the top.
209 *
210 * If the entry is not between the current top index and the bottom index
211 * specified by the cookie, we don't remove anything. This is the behavior
212 * required by JNI's DeleteLocalRef function.
213 *
214 * Note this is NOT called when a local frame is popped. This is only used
215 * for explicit single removals.
216 *
217 * Returns "false" if nothing was removed.
218 */
219bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
220 IRTSegmentState prevState;
221 prevState.all = cookie;
222 int topIndex = segmentState.parts.topIndex;
223 int bottomIndex = prevState.parts.topIndex;
224
225 DCHECK(table_ != NULL);
226 DCHECK_LE(alloc_entries_, max_entries_);
227 DCHECK_GE(segmentState.parts.numHoles, prevState.parts.numHoles);
228
229 int idx = ExtractIndex(iref);
230 bool workAroundAppJniBugs = false;
231
232 if (GetIndirectRefKind(iref) == kInvalid /*&& gDvmJni.workAroundAppJniBugs*/) { // TODO
233 idx = LinearScan(iref, bottomIndex, topIndex, table_);
234 workAroundAppJniBugs = true;
235 if (idx == -1) {
236 LOG(WARNING) << "trying to work around app JNI bugs, but didn't find " << iref << " in table!";
237 return false;
238 }
239 }
240
241 if (idx < bottomIndex) {
242 /* wrong segment */
243 LOG(INFO) << "Attempt to remove index outside index area (" << idx << " vs " << bottomIndex << "-" << topIndex << ")";
244 return false;
245 }
246 if (idx >= topIndex) {
247 /* bad -- stale reference? */
248 LOG(INFO) << "Attempt to remove invalid index " << idx << " (bottom=" << bottomIndex << " top=" << topIndex << ")";
249 return false;
250 }
251
252 if (idx == topIndex-1) {
253 // Top-most entry. Scan up and consume holes.
254
255 if (workAroundAppJniBugs == false && !CheckEntry("remove", iref, idx)) {
256 return false;
257 }
258
259 table_[idx] = NULL;
260 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
261 if (numHoles != 0) {
262 while (--topIndex > bottomIndex && numHoles != 0) {
263 //LOG(INFO) << "+++ checking for hole at " << topIndex-1 << " (cookie=" << cookie << ") val=" << table_[topIndex-1];
264 if (table_[topIndex-1] != NULL) {
265 break;
266 }
267 //LOG(INFO) << "+++ ate hole at " << (topIndex-1);
268 numHoles--;
269 }
270 segmentState.parts.numHoles = numHoles + prevState.parts.numHoles;
271 segmentState.parts.topIndex = topIndex;
272 } else {
273 segmentState.parts.topIndex = topIndex-1;
Elliott Hughesc5f7c912011-08-18 14:00:42 -0700274 //LOG(INFO) << "+++ ate last entry " << topIndex-1;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700275 }
276 } else {
277 /*
278 * Not the top-most entry. This creates a hole. We NULL out the
279 * entry to prevent somebody from deleting it twice and screwing up
280 * the hole count.
281 */
282 if (table_[idx] == NULL) {
283 LOG(INFO) << "--- WEIRD: removing null entry " << idx;
284 return false;
285 }
286 if (workAroundAppJniBugs == false && !CheckEntry("remove", iref, idx)) {
287 return false;
288 }
289
290 table_[idx] = NULL;
291 segmentState.parts.numHoles++;
292 //LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segmentState.parts.numHoles;
293 }
294
295 return true;
296}
297
298std::ostream& operator<<(std::ostream& os, IndirectRefKind rhs) {
299 switch (rhs) {
300 case kInvalid:
301 os << "invalid reference";
302 break;
303 case kLocal:
304 os << "local reference";
305 break;
306 case kGlobal:
307 os << "global reference";
308 break;
309 case kWeakGlobal:
310 os << "weak global reference";
311 break;
312 default:
313 os << "IndirectRefKind[" << static_cast<int>(rhs) << "]";
314 break;
315 }
316 return os;
317}
318
319void IndirectReferenceTable::Dump() const {
320 LOG(WARNING) << kind_ << " table dump:";
321 std::vector<Object*> entries(table_, table_ + Capacity());
322 ReferenceTable::Dump(entries);
323}
324
325} // namespace art