blob: 270727ce8f0870424b3036ea83ce5804506d012c [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Indirect reference table management.
*/
#include "Dalvik.h"
bool IndirectRefTable::init(size_t initialCount,
size_t maxCount, IndirectRefKind desiredKind)
{
assert(initialCount > 0);
assert(initialCount <= maxCount);
assert(kind != kIndirectKindInvalid);
table = (Object**) malloc(initialCount * sizeof(Object*));
if (table == NULL) {
return false;
}
#ifndef NDEBUG
memset(table, 0xd1, initialCount * sizeof(Object*));
#endif
slotData =
(IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
if (slotData == NULL) {
return false;
}
segmentState.all = IRT_FIRST_SEGMENT;
allocEntries = initialCount;
maxEntries = maxCount;
kind = desiredKind;
return true;
}
/*
* Clears out the contents of a IndirectRefTable, freeing allocated storage.
*/
void IndirectRefTable::destroy()
{
free(table);
free(slotData);
table = NULL;
allocEntries = maxEntries = -1;
}
/*
* Make sure that the entry at "idx" is correctly paired with "iref".
*/
bool IndirectRefTable::checkEntry(IndirectRef iref, int idx) const
{
Object* obj = table[idx];
IndirectRef checkRef = toIndirectRef(obj, idx);
if (checkRef != iref) {
LOGE("Attempt to use stale %s reference (req=%p vs cur=%p; table=%p)",
indirectRefKindToString(kind), iref, checkRef, this);
return false;
}
return true;
}
IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
{
IRTSegmentState prevState;
prevState.all = cookie;
size_t topIndex = segmentState.parts.topIndex;
assert(obj != NULL);
assert(dvmIsValidObject(obj));
assert(table != NULL);
assert(allocEntries <= maxEntries);
assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
if (topIndex == allocEntries) {
/* reached end of allocated space; did we hit buffer max? */
if (topIndex == maxEntries) {
LOGW("%s reference table overflow (max=%d)",
indirectRefKindToString(kind), maxEntries);
return NULL;
}
size_t newSize = allocEntries * 2;
if (newSize > maxEntries) {
newSize = maxEntries;
}
assert(newSize > allocEntries);
Object** newTable = (Object**) realloc(table, newSize * sizeof(Object*));
if (newTable == NULL) {
LOGE("Unable to expand %s reference table from %d to %d (max=%d)",
indirectRefKindToString(kind), allocEntries,
newSize, maxEntries);
return false;
}
LOGV("Growing %s reference table %p from %d to %d (max=%d)",
indirectRefKindToString(kind), this,
allocEntries, newSize, maxEntries);
/* update entries; adjust "nextEntry" in case memory moved */
table = newTable;
allocEntries = newSize;
}
IndirectRef result;
/*
* We know there's enough room in the table. Now we just need to find
* the right spot. If there's a hole, find it and fill it; otherwise,
* add to the end of the list.
*/
int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
if (numHoles > 0) {
assert(topIndex > 1);
/* find the first hole; likely to be near the end of the list */
Object** pScan = &table[topIndex - 1];
assert(*pScan != NULL);
while (*--pScan != NULL) {
assert(pScan >= table + prevState.parts.topIndex);
}
updateSlotAdd(obj, pScan - table);
result = toIndirectRef(obj, pScan - table);
*pScan = obj;
segmentState.parts.numHoles--;
} else {
/* add to the end */
updateSlotAdd(obj, topIndex);
result = toIndirectRef(obj, topIndex);
table[topIndex++] = obj;
segmentState.parts.topIndex = topIndex;
}
assert(result != NULL);
return result;
}
/*
* Verify that the indirect table lookup is valid.
*
* Returns "false" if something looks bad.
*/
bool IndirectRefTable::getChecked(IndirectRef iref) const
{
if (iref == NULL) {
LOGW("Attempt to look up NULL %s reference",
indirectRefKindToString(kind));
return false;
}
if (indirectRefKind(iref) == kIndirectKindInvalid) {
LOGW("Invalid %s reference %p",
indirectRefKindToString(kind), iref);
return false;
}
int topIndex = segmentState.parts.topIndex;
int idx = extractIndex(iref);
if (idx >= topIndex) {
/* bad -- stale reference? */
LOGW("Attempt to access stale %s reference at index %d (top=%d)",
indirectRefKindToString(kind), idx, topIndex);
return false;
}
Object* obj = table[idx];
if (obj == NULL) {
LOGW("Attempt to access deleted %s reference (%p)",
indirectRefKindToString(kind), iref);
return false;
}
if (!checkEntry(iref, idx)) {
return false;
}
return true;
}
/*
* Remove "obj" from "pRef". We extract the table offset bits from "iref"
* and zap the corresponding entry, leaving a hole if it's not at the top.
*
* If the entry is not between the current top index and the bottom index
* specified by the cookie, we don't remove anything. This is the behavior
* required by JNI's DeleteLocalRef function.
*
* Note this is NOT called when a local frame is popped. This is only used
* for explict single removals.
*
* Returns "false" if nothing was removed.
*/
bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
{
IRTSegmentState prevState;
prevState.all = cookie;
int topIndex = segmentState.parts.topIndex;
int bottomIndex = prevState.parts.topIndex;
assert(table != NULL);
assert(allocEntries <= maxEntries);
assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
int idx = extractIndex(iref);
if (idx < bottomIndex) {
/* wrong segment */
LOGV("Attempt to remove index outside index area (%d vs %d-%d)",
idx, bottomIndex, topIndex);
return false;
}
if (idx >= topIndex) {
/* bad -- stale reference? */
LOGD("Attempt to remove invalid index %d (bottom=%d top=%d)",
idx, bottomIndex, topIndex);
return false;
}
if (idx == topIndex-1) {
/*
* Top-most entry. Scan up and consume holes. No need to NULL
* out the entry, since the test vs. topIndex will catch it.
*/
if (!checkEntry(iref, idx)) {
return false;
}
updateSlotRemove(idx);
#ifndef NDEBUG
table[idx] = (Object*)0xd3d3d3d3;
#endif
int numHoles =
segmentState.parts.numHoles - prevState.parts.numHoles;
if (numHoles != 0) {
while (--topIndex > bottomIndex && numHoles != 0) {
LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
topIndex-1, cookie, table[topIndex-1]);
if (table[topIndex-1] != NULL)
break;
LOGV("+++ ate hole at %d", topIndex-1);
numHoles--;
}
segmentState.parts.numHoles =
numHoles + prevState.parts.numHoles;
segmentState.parts.topIndex = topIndex;
} else {
segmentState.parts.topIndex = topIndex-1;
LOGV("+++ ate last entry %d", topIndex-1);
}
} else {
/*
* Not the top-most entry. This creates a hole. We NULL out the
* entry to prevent somebody from deleting it twice and screwing up
* the hole count.
*/
if (table[idx] == NULL) {
LOGV("--- WEIRD: removing null entry %d", idx);
return false;
}
if (!checkEntry(iref, idx)) {
return false;
}
updateSlotRemove(idx);
table[idx] = NULL;
segmentState.parts.numHoles++;
LOGV("+++ left hole at %d, holes=%d",
idx, segmentState.parts.numHoles);
}
return true;
}
const char* indirectRefKindToString(IndirectRefKind kind)
{
switch (kind) {
case kIndirectKindInvalid: return "invalid";
case kIndirectKindLocal: return "local";
case kIndirectKindGlobal: return "global";
case kIndirectKindWeakGlobal: return "weak global";
default: return "UNKNOWN";
}
}
void IndirectRefTable::dump(const char* descr) const
{
dvmDumpReferenceTableContents(table, capacity(), descr);
}