blob: fcf59afbfe1b9f928f9f4068be63d3f2effe12b0 [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);
Carl Shapiroe4c3b5e2011-03-08 13:44:51 -080035 assert(kind != kIndirectKindInvalid);
Andy McFadden734155e2009-07-16 18:11:22 -070036
Elliott Hughesce096832011-06-20 17:50:41 -070037 table = (Object**) malloc(initialCount * sizeof(Object*));
38 if (table == NULL) {
Andy McFadden734155e2009-07-16 18:11:22 -070039 return false;
Elliott Hughesce096832011-06-20 17:50:41 -070040 }
Andy McFadden734155e2009-07-16 18:11:22 -070041#ifndef NDEBUG
Elliott Hughesce096832011-06-20 17:50:41 -070042 memset(table, 0xd1, initialCount * sizeof(Object*));
Andy McFadden734155e2009-07-16 18:11:22 -070043#endif
Andy McFadden5d599602009-08-31 16:39:23 -070044
Elliott Hughesce096832011-06-20 17:50:41 -070045 slotData =
Andy McFadden5d599602009-08-31 16:39:23 -070046 (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
Elliott Hughesce096832011-06-20 17:50:41 -070047 if (slotData == NULL) {
Andy McFadden5d599602009-08-31 16:39:23 -070048 return false;
Elliott Hughesce096832011-06-20 17:50:41 -070049 }
Andy McFadden5d599602009-08-31 16:39:23 -070050
Elliott Hughesce096832011-06-20 17:50:41 -070051 segmentState.all = IRT_FIRST_SEGMENT;
52 allocEntries = initialCount;
53 maxEntries = maxCount;
54 kind = desiredKind;
Andy McFadden734155e2009-07-16 18:11:22 -070055
56 return true;
57}
58
59/*
60 * Clears out the contents of a IndirectRefTable, freeing allocated storage.
61 */
Elliott Hughesce096832011-06-20 17:50:41 -070062void IndirectRefTable::destroy()
Andy McFadden734155e2009-07-16 18:11:22 -070063{
Elliott Hughesce096832011-06-20 17:50:41 -070064 free(table);
65 free(slotData);
66 table = NULL;
67 allocEntries = maxEntries = -1;
Andy McFadden734155e2009-07-16 18:11:22 -070068}
69
70/*
71 * Make sure that the entry at "idx" is correctly paired with "iref".
72 */
Elliott Hughesddbd6f42011-07-06 14:22:18 -070073bool IndirectRefTable::checkEntry(const char* what, IndirectRef iref, int idx) const
Andy McFadden734155e2009-07-16 18:11:22 -070074{
Elliott Hughesce096832011-06-20 17:50:41 -070075 Object* obj = table[idx];
76 IndirectRef checkRef = toIndirectRef(obj, idx);
Andy McFadden734155e2009-07-16 18:11:22 -070077 if (checkRef != iref) {
Elliott Hughesddbd6f42011-07-06 14:22:18 -070078 if (indirectRefKind(iref) != kIndirectKindWeakGlobal) {
79 LOGE("JNI ERROR (app bug): attempt to %s stale %s reference (req=%p vs cur=%p; table=%p)",
80 what, indirectRefKindToString(kind), iref, checkRef, this);
81 abortMaybe();
82 }
Andy McFadden734155e2009-07-16 18:11:22 -070083 return false;
84 }
85 return true;
86}
87
Elliott Hughesce096832011-06-20 17:50:41 -070088IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
Andy McFadden734155e2009-07-16 18:11:22 -070089{
90 IRTSegmentState prevState;
91 prevState.all = cookie;
Elliott Hughesce096832011-06-20 17:50:41 -070092 size_t topIndex = segmentState.parts.topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -070093
94 assert(obj != NULL);
95 assert(dvmIsValidObject(obj));
Elliott Hughesce096832011-06-20 17:50:41 -070096 assert(table != NULL);
97 assert(allocEntries <= maxEntries);
98 assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -070099
Elliott Hughesce096832011-06-20 17:50:41 -0700100 if (topIndex == allocEntries) {
Andy McFadden734155e2009-07-16 18:11:22 -0700101 /* reached end of allocated space; did we hit buffer max? */
Elliott Hughesce096832011-06-20 17:50:41 -0700102 if (topIndex == maxEntries) {
Elliott Hughes24c57f12011-06-29 11:10:21 -0700103 LOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)",
Elliott Hughesce096832011-06-20 17:50:41 -0700104 indirectRefKindToString(kind), maxEntries);
Elliott Hughes24c57f12011-06-29 11:10:21 -0700105 dump(indirectRefKindToString(kind));
106 dvmAbort();
Andy McFadden734155e2009-07-16 18:11:22 -0700107 }
108
Elliott Hughesce096832011-06-20 17:50:41 -0700109 size_t newSize = allocEntries * 2;
110 if (newSize > maxEntries) {
111 newSize = maxEntries;
112 }
113 assert(newSize > allocEntries);
Andy McFadden734155e2009-07-16 18:11:22 -0700114
Elliott Hughesce096832011-06-20 17:50:41 -0700115 Object** newTable = (Object**) realloc(table, newSize * sizeof(Object*));
Andy McFadden734155e2009-07-16 18:11:22 -0700116 if (newTable == NULL) {
Elliott Hughes24c57f12011-06-29 11:10:21 -0700117 LOGE("JNI ERROR (app bug): unable to expand %s reference table (from %d to %d, max=%d)",
118 indirectRefKindToString(kind),
119 allocEntries, newSize, maxEntries);
120 dump(indirectRefKindToString(kind));
121 dvmAbort();
Andy McFadden734155e2009-07-16 18:11:22 -0700122 }
Andy McFadden734155e2009-07-16 18:11:22 -0700123
124 /* update entries; adjust "nextEntry" in case memory moved */
Elliott Hughesce096832011-06-20 17:50:41 -0700125 table = newTable;
126 allocEntries = newSize;
Andy McFadden734155e2009-07-16 18:11:22 -0700127 }
128
129 IndirectRef result;
130
131 /*
132 * We know there's enough room in the table. Now we just need to find
133 * the right spot. If there's a hole, find it and fill it; otherwise,
134 * add to the end of the list.
135 */
Elliott Hughesce096832011-06-20 17:50:41 -0700136 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
Andy McFadden734155e2009-07-16 18:11:22 -0700137 if (numHoles > 0) {
138 assert(topIndex > 1);
139 /* find the first hole; likely to be near the end of the list */
Elliott Hughesce096832011-06-20 17:50:41 -0700140 Object** pScan = &table[topIndex - 1];
Andy McFadden734155e2009-07-16 18:11:22 -0700141 assert(*pScan != NULL);
142 while (*--pScan != NULL) {
Elliott Hughesce096832011-06-20 17:50:41 -0700143 assert(pScan >= table + prevState.parts.topIndex);
Andy McFadden734155e2009-07-16 18:11:22 -0700144 }
Elliott Hughesce096832011-06-20 17:50:41 -0700145 updateSlotAdd(obj, pScan - table);
146 result = toIndirectRef(obj, pScan - table);
Andy McFadden734155e2009-07-16 18:11:22 -0700147 *pScan = obj;
Elliott Hughesce096832011-06-20 17:50:41 -0700148 segmentState.parts.numHoles--;
Andy McFadden734155e2009-07-16 18:11:22 -0700149 } else {
150 /* add to the end */
Elliott Hughesce096832011-06-20 17:50:41 -0700151 updateSlotAdd(obj, topIndex);
152 result = toIndirectRef(obj, topIndex);
153 table[topIndex++] = obj;
154 segmentState.parts.topIndex = topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700155 }
156
157 assert(result != NULL);
158 return result;
159}
160
161/*
162 * Verify that the indirect table lookup is valid.
163 *
164 * Returns "false" if something looks bad.
165 */
Elliott Hughesce096832011-06-20 17:50:41 -0700166bool IndirectRefTable::getChecked(IndirectRef iref) const
Andy McFadden734155e2009-07-16 18:11:22 -0700167{
Andy McFadden734155e2009-07-16 18:11:22 -0700168 if (iref == NULL) {
Elliott Hughesce096832011-06-20 17:50:41 -0700169 LOGW("Attempt to look up NULL %s reference",
170 indirectRefKindToString(kind));
Andy McFadden734155e2009-07-16 18:11:22 -0700171 return false;
172 }
Elliott Hughesce096832011-06-20 17:50:41 -0700173 if (indirectRefKind(iref) == kIndirectKindInvalid) {
Elliott Hughes24c57f12011-06-29 11:10:21 -0700174 LOGE("JNI ERROR (app bug): invalid %s reference (%p)",
Elliott Hughesce096832011-06-20 17:50:41 -0700175 indirectRefKindToString(kind), iref);
Elliott Hughes24c57f12011-06-29 11:10:21 -0700176 abortMaybe();
Elliott Hughesce096832011-06-20 17:50:41 -0700177 return false;
178 }
179
180 int topIndex = segmentState.parts.topIndex;
181 int idx = extractIndex(iref);
Andy McFadden734155e2009-07-16 18:11:22 -0700182 if (idx >= topIndex) {
183 /* bad -- stale reference? */
Elliott Hughes24c57f12011-06-29 11:10:21 -0700184 LOGE("JNI ERROR (app bug): accessed stale %s reference at index %d (top=%d)",
185 indirectRefKindToString(kind), idx, topIndex);
186 abortMaybe();
Andy McFadden734155e2009-07-16 18:11:22 -0700187 return false;
188 }
189
Elliott Hughesddbd6f42011-07-06 14:22:18 -0700190 if (!checkEntry("use", iref, idx)) {
Andy McFadden734155e2009-07-16 18:11:22 -0700191 return false;
Elliott Hughesce096832011-06-20 17:50:41 -0700192 }
Andy McFadden734155e2009-07-16 18:11:22 -0700193
194 return true;
195}
196
197/*
198 * Remove "obj" from "pRef". We extract the table offset bits from "iref"
199 * and zap the corresponding entry, leaving a hole if it's not at the top.
200 *
201 * If the entry is not between the current top index and the bottom index
202 * specified by the cookie, we don't remove anything. This is the behavior
203 * required by JNI's DeleteLocalRef function.
204 *
Andy McFadden5d599602009-08-31 16:39:23 -0700205 * Note this is NOT called when a local frame is popped. This is only used
Elliott Hughesddbd6f42011-07-06 14:22:18 -0700206 * for explicit single removals.
Andy McFadden5d599602009-08-31 16:39:23 -0700207 *
Andy McFadden734155e2009-07-16 18:11:22 -0700208 * Returns "false" if nothing was removed.
209 */
Elliott Hughesce096832011-06-20 17:50:41 -0700210bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
Andy McFadden734155e2009-07-16 18:11:22 -0700211{
212 IRTSegmentState prevState;
213 prevState.all = cookie;
Elliott Hughesce096832011-06-20 17:50:41 -0700214 int topIndex = segmentState.parts.topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700215 int bottomIndex = prevState.parts.topIndex;
216
Elliott Hughesce096832011-06-20 17:50:41 -0700217 assert(table != NULL);
218 assert(allocEntries <= maxEntries);
219 assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -0700220
Elliott Hughesce096832011-06-20 17:50:41 -0700221 int idx = extractIndex(iref);
Andy McFadden734155e2009-07-16 18:11:22 -0700222 if (idx < bottomIndex) {
223 /* wrong segment */
Dan Bornstein60fc8062011-05-26 10:11:58 -0700224 LOGV("Attempt to remove index outside index area (%d vs %d-%d)",
Andy McFadden734155e2009-07-16 18:11:22 -0700225 idx, bottomIndex, topIndex);
226 return false;
227 }
228 if (idx >= topIndex) {
229 /* bad -- stale reference? */
Dan Bornstein60fc8062011-05-26 10:11:58 -0700230 LOGD("Attempt to remove invalid index %d (bottom=%d top=%d)",
Andy McFadden734155e2009-07-16 18:11:22 -0700231 idx, bottomIndex, topIndex);
232 return false;
233 }
234
235 if (idx == topIndex-1) {
236 /*
237 * Top-most entry. Scan up and consume holes. No need to NULL
238 * out the entry, since the test vs. topIndex will catch it.
239 */
Elliott Hughesddbd6f42011-07-06 14:22:18 -0700240 if (!checkEntry("remove", iref, idx)) {
Andy McFadden734155e2009-07-16 18:11:22 -0700241 return false;
Elliott Hughesce096832011-06-20 17:50:41 -0700242 }
243 updateSlotRemove(idx);
Andy McFadden734155e2009-07-16 18:11:22 -0700244
245#ifndef NDEBUG
Elliott Hughesce096832011-06-20 17:50:41 -0700246 table[idx] = (Object*)0xd3d3d3d3;
Andy McFadden734155e2009-07-16 18:11:22 -0700247#endif
248
249 int numHoles =
Elliott Hughesce096832011-06-20 17:50:41 -0700250 segmentState.parts.numHoles - prevState.parts.numHoles;
Andy McFadden734155e2009-07-16 18:11:22 -0700251 if (numHoles != 0) {
252 while (--topIndex > bottomIndex && numHoles != 0) {
Dan Bornstein60fc8062011-05-26 10:11:58 -0700253 LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
Elliott Hughesce096832011-06-20 17:50:41 -0700254 topIndex-1, cookie, table[topIndex-1]);
255 if (table[topIndex-1] != NULL)
Andy McFadden734155e2009-07-16 18:11:22 -0700256 break;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700257 LOGV("+++ ate hole at %d", topIndex-1);
Andy McFadden734155e2009-07-16 18:11:22 -0700258 numHoles--;
259 }
Elliott Hughesce096832011-06-20 17:50:41 -0700260 segmentState.parts.numHoles =
Andy McFadden734155e2009-07-16 18:11:22 -0700261 numHoles + prevState.parts.numHoles;
Elliott Hughesce096832011-06-20 17:50:41 -0700262 segmentState.parts.topIndex = topIndex;
Andy McFadden734155e2009-07-16 18:11:22 -0700263 } else {
Elliott Hughesce096832011-06-20 17:50:41 -0700264 segmentState.parts.topIndex = topIndex-1;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700265 LOGV("+++ ate last entry %d", topIndex-1);
Andy McFadden734155e2009-07-16 18:11:22 -0700266 }
267 } else {
268 /*
269 * Not the top-most entry. This creates a hole. We NULL out the
270 * entry to prevent somebody from deleting it twice and screwing up
271 * the hole count.
272 */
Elliott Hughesce096832011-06-20 17:50:41 -0700273 if (table[idx] == NULL) {
Dan Bornstein60fc8062011-05-26 10:11:58 -0700274 LOGV("--- WEIRD: removing null entry %d", idx);
Andy McFadden734155e2009-07-16 18:11:22 -0700275 return false;
276 }
Elliott Hughesddbd6f42011-07-06 14:22:18 -0700277 if (!checkEntry("remove", iref, idx)) {
Andy McFadden734155e2009-07-16 18:11:22 -0700278 return false;
Elliott Hughesce096832011-06-20 17:50:41 -0700279 }
280 updateSlotRemove(idx);
Andy McFadden734155e2009-07-16 18:11:22 -0700281
Elliott Hughesce096832011-06-20 17:50:41 -0700282 table[idx] = NULL;
283 segmentState.parts.numHoles++;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700284 LOGV("+++ left hole at %d, holes=%d",
Elliott Hughesce096832011-06-20 17:50:41 -0700285 idx, segmentState.parts.numHoles);
Andy McFadden734155e2009-07-16 18:11:22 -0700286 }
287
288 return true;
289}
290
Elliott Hughesce096832011-06-20 17:50:41 -0700291const char* indirectRefKindToString(IndirectRefKind kind)
Andy McFaddenc759b9f2011-04-07 10:36:56 -0700292{
Elliott Hughesce096832011-06-20 17:50:41 -0700293 switch (kind) {
Andy McFaddenc759b9f2011-04-07 10:36:56 -0700294 case kIndirectKindInvalid: return "invalid";
295 case kIndirectKindLocal: return "local";
296 case kIndirectKindGlobal: return "global";
297 case kIndirectKindWeakGlobal: return "weak global";
298 default: return "UNKNOWN";
299 }
300}
301
Elliott Hughesce096832011-06-20 17:50:41 -0700302void IndirectRefTable::dump(const char* descr) const
Andy McFadden734155e2009-07-16 18:11:22 -0700303{
Elliott Hughesce096832011-06-20 17:50:41 -0700304 dvmDumpReferenceTableContents(table, capacity(), descr);
Andy McFadden734155e2009-07-16 18:11:22 -0700305}