blob: c38458d6a20e8fca3f432d05cac1c82ac4694ac0 [file] [log] [blame]
Andy McFadden734155e2009-07-16 18:11:22 -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/*
18 * Indirect reference table management.
19 */
20#include "Dalvik.h"
21
Elliott Hughes24c57f12011-06-29 11:10:21 -070022static void abortMaybe() {
23 // If CheckJNI is on, it'll give a more detailed error before aborting.
24 // Otherwise, we want to abort rather than hand back a bad reference.
25 if (!gDvmJni.useCheckJni) {
26 dvmAbort();
27 }
28}
29
Elliott Hughesce096832011-06-20 17:50:41 -070030bool IndirectRefTable::init(size_t initialCount,
31 size_t maxCount, IndirectRefKind desiredKind)
Andy McFadden734155e2009-07-16 18:11:22 -070032{
33 assert(initialCount > 0);
34 assert(initialCount <= maxCount);
Elliott Hughesb2734152011-07-20 16:41:37 -070035 assert(desiredKind != kIndirectKindInvalid);
Andy McFadden734155e2009-07-16 18:11:22 -070036
Jeff Brown68ecde12011-10-26 17:04:54 -070037 table_ = (IndirectRefSlot*) malloc(initialCount * sizeof(IndirectRefSlot));
Elliott Hughesb2734152011-07-20 16:41:37 -070038 if (table_ == NULL) {
Andy McFadden734155e2009-07-16 18:11:22 -070039 return false;
Elliott Hughesce096832011-06-20 17:50:41 -070040 }
Jeff Brown68ecde12011-10-26 17:04:54 -070041 memset(table_, 0xd1, initialCount * sizeof(IndirectRefSlot));
Andy McFadden5d599602009-08-31 16:39:23 -070042
Elliott Hughesce096832011-06-20 17:50:41 -070043 segmentState.all = IRT_FIRST_SEGMENT;
Elliott Hughesb2734152011-07-20 16:41:37 -070044 alloc_entries_ = initialCount;
45 max_entries_ = maxCount;
46 kind_ = desiredKind;
Andy McFadden734155e2009-07-16 18:11:22 -070047
48 return true;
49}
50
51/*
52 * Clears out the contents of a IndirectRefTable, freeing allocated storage.
53 */
Elliott Hughesce096832011-06-20 17:50:41 -070054void IndirectRefTable::destroy()
Andy McFadden734155e2009-07-16 18:11:22 -070055{
Elliott Hughesb2734152011-07-20 16:41:37 -070056 free(table_);
Elliott Hughesb2734152011-07-20 16:41:37 -070057 table_ = NULL;
Elliott Hughesb2734152011-07-20 16:41:37 -070058 alloc_entries_ = max_entries_ = -1;
Andy McFadden734155e2009-07-16 18:11:22 -070059}
60
Elliott Hughesce096832011-06-20 17:50:41 -070061IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
Andy McFadden734155e2009-07-16 18:11:22 -070062{
63 IRTSegmentState prevState;
64 prevState.all = cookie;
Elliott Hughesce096832011-06-20 17:50:41 -070065 size_t topIndex = segmentState.parts.topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -070066
67 assert(obj != NULL);
Elliott Hughes8bc8bf72011-07-19 17:53:51 -070068 assert(dvmIsHeapAddress(obj));
Elliott Hughesb2734152011-07-20 16:41:37 -070069 assert(table_ != NULL);
70 assert(alloc_entries_ <= max_entries_);
Elliott Hughesce096832011-06-20 17:50:41 -070071 assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -070072
Andy McFadden734155e2009-07-16 18:11:22 -070073 /*
74 * We know there's enough room in the table. Now we just need to find
75 * the right spot. If there's a hole, find it and fill it; otherwise,
76 * add to the end of the list.
77 */
Elliott Hughes259a8a52011-07-11 16:44:34 -070078 IndirectRef result;
Jeff Brown68ecde12011-10-26 17:04:54 -070079 IndirectRefSlot* slot;
Elliott Hughesce096832011-06-20 17:50:41 -070080 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
Andy McFadden734155e2009-07-16 18:11:22 -070081 if (numHoles > 0) {
82 assert(topIndex > 1);
Jeff Brown68ecde12011-10-26 17:04:54 -070083 /* find the first hole; likely to be near the end of the list,
84 * we know the item at the topIndex is not a hole */
85 slot = &table_[topIndex - 1];
86 assert(slot->obj != NULL);
87 while ((--slot)->obj != NULL) {
88 assert(slot >= table_ + prevState.parts.topIndex);
Andy McFadden734155e2009-07-16 18:11:22 -070089 }
Elliott Hughesce096832011-06-20 17:50:41 -070090 segmentState.parts.numHoles--;
Andy McFadden734155e2009-07-16 18:11:22 -070091 } else {
Jeff Brown68ecde12011-10-26 17:04:54 -070092 /* add to the end, grow if needed */
93 if (topIndex == alloc_entries_) {
94 /* reached end of allocated space; did we hit buffer max? */
95 if (topIndex == max_entries_) {
96 LOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)",
97 indirectRefKindToString(kind_), max_entries_);
98 return NULL;
99 }
100
101 size_t newSize = alloc_entries_ * 2;
102 if (newSize > max_entries_) {
103 newSize = max_entries_;
104 }
105 assert(newSize > alloc_entries_);
106
107 IndirectRefSlot* newTable =
108 (IndirectRefSlot*) realloc(table_, newSize * sizeof(IndirectRefSlot));
109 if (table_ == NULL) {
110 LOGE("JNI ERROR (app bug): unable to expand %s reference table "
111 "(from %d to %d, max=%d)",
112 indirectRefKindToString(kind_),
113 alloc_entries_, newSize, max_entries_);
114 return NULL;
115 }
116
Ben Chengee155d42011-11-30 10:43:09 -0800117 memset(newTable + alloc_entries_, 0xd1,
118 (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
119
Jeff Brown68ecde12011-10-26 17:04:54 -0700120 alloc_entries_ = newSize;
121 table_ = newTable;
122 }
123 slot = &table_[topIndex++];
Elliott Hughesce096832011-06-20 17:50:41 -0700124 segmentState.parts.topIndex = topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700125 }
126
Jeff Brown68ecde12011-10-26 17:04:54 -0700127 slot->obj = obj;
128 slot->serial = nextSerial(slot->serial);
129 result = toIndirectRef(slot - table_, slot->serial, kind_);
130
Andy McFadden734155e2009-07-16 18:11:22 -0700131 assert(result != NULL);
132 return result;
133}
134
135/*
Jeff Brown68ecde12011-10-26 17:04:54 -0700136 * Get the referent of an indirect ref from the table.
Andy McFadden734155e2009-07-16 18:11:22 -0700137 *
Jeff Brown68ecde12011-10-26 17:04:54 -0700138 * Returns kInvalidIndirectRefObject if iref is invalid.
Andy McFadden734155e2009-07-16 18:11:22 -0700139 */
Jeff Brown68ecde12011-10-26 17:04:54 -0700140Object* IndirectRefTable::get(IndirectRef iref) const {
141 IndirectRefKind kind = indirectRefKind(iref);
142 if (kind != kind_) {
143 if (iref == NULL) {
144 LOGW("Attempt to look up NULL %s reference", indirectRefKindToString(kind_));
145 return kInvalidIndirectRefObject;
146 }
147 if (kind == kIndirectKindInvalid) {
148 LOGE("JNI ERROR (app bug): invalid %s reference %p",
149 indirectRefKindToString(kind_), iref);
150 abortMaybe();
151 return kInvalidIndirectRefObject;
152 }
153 // References of the requested kind cannot appear within this table.
154 return kInvalidIndirectRefObject;
Elliott Hughesce096832011-06-20 17:50:41 -0700155 }
156
Jeff Brown68ecde12011-10-26 17:04:54 -0700157 u4 topIndex = segmentState.parts.topIndex;
158 u4 index = extractIndex(iref);
159 if (index >= topIndex) {
Andy McFadden734155e2009-07-16 18:11:22 -0700160 /* bad -- stale reference? */
Elliott Hughes259a8a52011-07-11 16:44:34 -0700161 LOGE("JNI ERROR (app bug): accessed stale %s reference %p (index %d in a table of size %d)",
Jeff Brown68ecde12011-10-26 17:04:54 -0700162 indirectRefKindToString(kind_), iref, index, topIndex);
Elliott Hughes259a8a52011-07-11 16:44:34 -0700163 abortMaybe();
Jeff Brown68ecde12011-10-26 17:04:54 -0700164 return kInvalidIndirectRefObject;
Elliott Hughes259a8a52011-07-11 16:44:34 -0700165 }
166
Jeff Brown68ecde12011-10-26 17:04:54 -0700167 Object* obj = table_[index].obj;
168 if (obj == NULL) {
Elliott Hughes259a8a52011-07-11 16:44:34 -0700169 LOGI("JNI ERROR (app bug): accessed deleted %s reference %p",
Elliott Hughesb2734152011-07-20 16:41:37 -0700170 indirectRefKindToString(kind_), iref);
Elliott Hughes24c57f12011-06-29 11:10:21 -0700171 abortMaybe();
Jeff Brown68ecde12011-10-26 17:04:54 -0700172 return kInvalidIndirectRefObject;
Andy McFadden734155e2009-07-16 18:11:22 -0700173 }
174
Jeff Brown68ecde12011-10-26 17:04:54 -0700175 u4 serial = extractSerial(iref);
176 if (serial != table_[index].serial) {
177 LOGE("JNI ERROR (app bug): attempt to use stale %s reference %p",
178 indirectRefKindToString(kind_), iref);
179 abortMaybe();
180 return kInvalidIndirectRefObject;
Elliott Hughesce096832011-06-20 17:50:41 -0700181 }
Andy McFadden734155e2009-07-16 18:11:22 -0700182
Jeff Brown68ecde12011-10-26 17:04:54 -0700183 return obj;
Andy McFadden734155e2009-07-16 18:11:22 -0700184}
185
Jeff Brown68ecde12011-10-26 17:04:54 -0700186static int findObject(const Object* obj, int bottomIndex, int topIndex,
187 const IndirectRefSlot* table) {
Elliott Hughesea333382011-07-10 19:15:13 -0700188 for (int i = bottomIndex; i < topIndex; ++i) {
Jeff Brown68ecde12011-10-26 17:04:54 -0700189 if (table[i].obj == obj) {
Elliott Hughesea333382011-07-10 19:15:13 -0700190 return i;
191 }
192 }
193 return -1;
194}
195
Jeff Brown68ecde12011-10-26 17:04:54 -0700196bool IndirectRefTable::contains(const Object* obj) const {
197 return findObject(obj, 0, segmentState.parts.topIndex, table_) >= 0;
Elliott Hughesea333382011-07-10 19:15:13 -0700198}
199
Andy McFadden734155e2009-07-16 18:11:22 -0700200/*
201 * Remove "obj" from "pRef". We extract the table offset bits from "iref"
202 * and zap the corresponding entry, leaving a hole if it's not at the top.
203 *
204 * If the entry is not between the current top index and the bottom index
205 * specified by the cookie, we don't remove anything. This is the behavior
206 * required by JNI's DeleteLocalRef function.
207 *
Andy McFadden5d599602009-08-31 16:39:23 -0700208 * Note this is NOT called when a local frame is popped. This is only used
Elliott Hughesddbd6f42011-07-06 14:22:18 -0700209 * for explicit single removals.
Andy McFadden5d599602009-08-31 16:39:23 -0700210 *
Andy McFadden734155e2009-07-16 18:11:22 -0700211 * Returns "false" if nothing was removed.
212 */
Elliott Hughesce096832011-06-20 17:50:41 -0700213bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
Andy McFadden734155e2009-07-16 18:11:22 -0700214{
215 IRTSegmentState prevState;
216 prevState.all = cookie;
Jeff Brown68ecde12011-10-26 17:04:54 -0700217 u4 topIndex = segmentState.parts.topIndex;
218 u4 bottomIndex = prevState.parts.topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700219
Elliott Hughesb2734152011-07-20 16:41:37 -0700220 assert(table_ != NULL);
221 assert(alloc_entries_ <= max_entries_);
Elliott Hughesce096832011-06-20 17:50:41 -0700222 assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -0700223
Jeff Brown68ecde12011-10-26 17:04:54 -0700224 IndirectRefKind kind = indirectRefKind(iref);
225 u4 index;
226 if (kind == kind_) {
227 index = extractIndex(iref);
228 if (index < bottomIndex) {
229 /* wrong segment */
230 LOGV("Attempt to remove index outside index area (%ud vs %ud-%ud)",
231 index, bottomIndex, topIndex);
232 return false;
233 }
234 if (index >= topIndex) {
235 /* bad -- stale reference? */
236 LOGD("Attempt to remove invalid index %ud (bottom=%ud top=%ud)",
237 index, bottomIndex, topIndex);
238 return false;
239 }
240 if (table_[index].obj == NULL) {
241 LOGD("Attempt to remove cleared %s reference %p",
242 indirectRefKindToString(kind_), iref);
243 return false;
244 }
245 u4 serial = extractSerial(iref);
246 if (table_[index].serial != serial) {
247 LOGD("Attempt to remove stale %s reference %p",
248 indirectRefKindToString(kind_), iref);
249 return false;
250 }
251 } else if (kind == kIndirectKindInvalid && gDvmJni.workAroundAppJniBugs) {
252 // reference looks like a pointer, scan the table to find the index
253 int i = findObject(reinterpret_cast<Object*>(iref), bottomIndex, topIndex, table_);
254 if (i < 0) {
Elliott Hughesea333382011-07-10 19:15:13 -0700255 LOGW("trying to work around app JNI bugs, but didn't find %p in table!", iref);
256 return false;
257 }
Jeff Brown68ecde12011-10-26 17:04:54 -0700258 index = i;
259 } else {
260 // References of the requested kind cannot appear within this table.
Andy McFadden734155e2009-07-16 18:11:22 -0700261 return false;
262 }
263
Jeff Brown68ecde12011-10-26 17:04:54 -0700264 if (index == topIndex - 1) {
Elliott Hughes259a8a52011-07-11 16:44:34 -0700265 // Top-most entry. Scan up and consume holes.
Elliott Hughes259a8a52011-07-11 16:44:34 -0700266 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
Andy McFadden734155e2009-07-16 18:11:22 -0700267 if (numHoles != 0) {
268 while (--topIndex > bottomIndex && numHoles != 0) {
Dan Bornstein60fc8062011-05-26 10:11:58 -0700269 LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
Jeff Brown68ecde12011-10-26 17:04:54 -0700270 topIndex-1, cookie, table_[topIndex-1].obj);
271 if (table_[topIndex-1].obj != NULL) {
Andy McFadden734155e2009-07-16 18:11:22 -0700272 break;
Elliott Hughes259a8a52011-07-11 16:44:34 -0700273 }
Dan Bornstein60fc8062011-05-26 10:11:58 -0700274 LOGV("+++ ate hole at %d", topIndex-1);
Andy McFadden734155e2009-07-16 18:11:22 -0700275 numHoles--;
276 }
Elliott Hughes259a8a52011-07-11 16:44:34 -0700277 segmentState.parts.numHoles = numHoles + prevState.parts.numHoles;
Elliott Hughesce096832011-06-20 17:50:41 -0700278 segmentState.parts.topIndex = topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700279 } else {
Elliott Hughesce096832011-06-20 17:50:41 -0700280 segmentState.parts.topIndex = topIndex-1;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700281 LOGV("+++ ate last entry %d", topIndex-1);
Andy McFadden734155e2009-07-16 18:11:22 -0700282 }
283 } else {
284 /*
285 * Not the top-most entry. This creates a hole. We NULL out the
286 * entry to prevent somebody from deleting it twice and screwing up
287 * the hole count.
288 */
Jeff Brown68ecde12011-10-26 17:04:54 -0700289 table_[index].obj = NULL;
Elliott Hughesce096832011-06-20 17:50:41 -0700290 segmentState.parts.numHoles++;
Jeff Brown68ecde12011-10-26 17:04:54 -0700291 LOGV("+++ left hole at %d, holes=%d", index, segmentState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -0700292 }
293
294 return true;
295}
296
Elliott Hughesce096832011-06-20 17:50:41 -0700297const char* indirectRefKindToString(IndirectRefKind kind)
Andy McFaddenc759b9f2011-04-07 10:36:56 -0700298{
Elliott Hughesce096832011-06-20 17:50:41 -0700299 switch (kind) {
Andy McFaddenc759b9f2011-04-07 10:36:56 -0700300 case kIndirectKindInvalid: return "invalid";
301 case kIndirectKindLocal: return "local";
302 case kIndirectKindGlobal: return "global";
303 case kIndirectKindWeakGlobal: return "weak global";
304 default: return "UNKNOWN";
305 }
306}
307
Elliott Hughesce096832011-06-20 17:50:41 -0700308void IndirectRefTable::dump(const char* descr) const
Andy McFadden734155e2009-07-16 18:11:22 -0700309{
Jeff Brown68ecde12011-10-26 17:04:54 -0700310 size_t count = capacity();
311 Object** copy = new Object*[count];
312 for (size_t i = 0; i < count; i++) {
313 copy[i] = table_[i].obj;
314 }
315 dvmDumpReferenceTableContents(copy, count, descr);
316 delete[] copy;
Andy McFadden734155e2009-07-16 18:11:22 -0700317}