IndirectRefTable.cpp revision ce0968340f9ddd54f20e38d4946bfd2ef8f1f343
1/*
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
22bool IndirectRefTable::init(size_t initialCount,
23        size_t maxCount, IndirectRefKind desiredKind)
24{
25    assert(initialCount > 0);
26    assert(initialCount <= maxCount);
27    assert(kind != kIndirectKindInvalid);
28
29    table = (Object**) malloc(initialCount * sizeof(Object*));
30    if (table == NULL) {
31        return false;
32    }
33#ifndef NDEBUG
34    memset(table, 0xd1, initialCount * sizeof(Object*));
35#endif
36
37    slotData =
38        (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
39    if (slotData == NULL) {
40        return false;
41    }
42
43    segmentState.all = IRT_FIRST_SEGMENT;
44    allocEntries = initialCount;
45    maxEntries = maxCount;
46    kind = desiredKind;
47
48    return true;
49}
50
51/*
52 * Clears out the contents of a IndirectRefTable, freeing allocated storage.
53 */
54void IndirectRefTable::destroy()
55{
56    free(table);
57    free(slotData);
58    table = NULL;
59    allocEntries = maxEntries = -1;
60}
61
62/*
63 * Make sure that the entry at "idx" is correctly paired with "iref".
64 */
65bool IndirectRefTable::checkEntry(IndirectRef iref, int idx) const
66{
67    Object* obj = table[idx];
68    IndirectRef checkRef = toIndirectRef(obj, idx);
69    if (checkRef != iref) {
70        LOGE("Attempt to use stale %s reference (req=%p vs cur=%p; table=%p)",
71                indirectRefKindToString(kind), iref, checkRef, this);
72        return false;
73    }
74    return true;
75}
76
77IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
78{
79    IRTSegmentState prevState;
80    prevState.all = cookie;
81    size_t topIndex = segmentState.parts.topIndex;
82
83    assert(obj != NULL);
84    assert(dvmIsValidObject(obj));
85    assert(table != NULL);
86    assert(allocEntries <= maxEntries);
87    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
88
89    if (topIndex == allocEntries) {
90        /* reached end of allocated space; did we hit buffer max? */
91        if (topIndex == maxEntries) {
92            LOGW("%s reference table overflow (max=%d)",
93                    indirectRefKindToString(kind), maxEntries);
94            return NULL;
95        }
96
97        size_t newSize = allocEntries * 2;
98        if (newSize > maxEntries) {
99            newSize = maxEntries;
100        }
101        assert(newSize > allocEntries);
102
103        Object** newTable = (Object**) realloc(table, newSize * sizeof(Object*));
104        if (newTable == NULL) {
105            LOGE("Unable to expand %s reference table from %d to %d (max=%d)",
106                    indirectRefKindToString(kind), allocEntries,
107                    newSize, maxEntries);
108            return false;
109        }
110        LOGV("Growing %s reference table %p from %d to %d (max=%d)",
111                indirectRefKindToString(kind), this,
112                allocEntries, newSize, maxEntries);
113
114        /* update entries; adjust "nextEntry" in case memory moved */
115        table = newTable;
116        allocEntries = newSize;
117    }
118
119    IndirectRef result;
120
121    /*
122     * We know there's enough room in the table.  Now we just need to find
123     * the right spot.  If there's a hole, find it and fill it; otherwise,
124     * add to the end of the list.
125     */
126    int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
127    if (numHoles > 0) {
128        assert(topIndex > 1);
129        /* find the first hole; likely to be near the end of the list */
130        Object** pScan = &table[topIndex - 1];
131        assert(*pScan != NULL);
132        while (*--pScan != NULL) {
133            assert(pScan >= table + prevState.parts.topIndex);
134        }
135        updateSlotAdd(obj, pScan - table);
136        result = toIndirectRef(obj, pScan - table);
137        *pScan = obj;
138        segmentState.parts.numHoles--;
139    } else {
140        /* add to the end */
141        updateSlotAdd(obj, topIndex);
142        result = toIndirectRef(obj, topIndex);
143        table[topIndex++] = obj;
144        segmentState.parts.topIndex = topIndex;
145    }
146
147    assert(result != NULL);
148    return result;
149}
150
151/*
152 * Verify that the indirect table lookup is valid.
153 *
154 * Returns "false" if something looks bad.
155 */
156bool IndirectRefTable::getChecked(IndirectRef iref) const
157{
158    if (iref == NULL) {
159        LOGW("Attempt to look up NULL %s reference",
160                indirectRefKindToString(kind));
161        return false;
162    }
163    if (indirectRefKind(iref) == kIndirectKindInvalid) {
164        LOGW("Invalid %s reference %p",
165                indirectRefKindToString(kind), iref);
166        return false;
167    }
168
169    int topIndex = segmentState.parts.topIndex;
170    int idx = extractIndex(iref);
171    if (idx >= topIndex) {
172        /* bad -- stale reference? */
173        LOGW("Attempt to access stale %s reference at index %d (top=%d)",
174            indirectRefKindToString(kind), idx, topIndex);
175        return false;
176    }
177
178    Object* obj = table[idx];
179    if (obj == NULL) {
180        LOGW("Attempt to access deleted %s reference (%p)",
181                indirectRefKindToString(kind), iref);
182        return false;
183    }
184    if (!checkEntry(iref, idx)) {
185        return false;
186    }
187
188    return true;
189}
190
191/*
192 * Remove "obj" from "pRef".  We extract the table offset bits from "iref"
193 * and zap the corresponding entry, leaving a hole if it's not at the top.
194 *
195 * If the entry is not between the current top index and the bottom index
196 * specified by the cookie, we don't remove anything.  This is the behavior
197 * required by JNI's DeleteLocalRef function.
198 *
199 * Note this is NOT called when a local frame is popped.  This is only used
200 * for explict single removals.
201 *
202 * Returns "false" if nothing was removed.
203 */
204bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
205{
206    IRTSegmentState prevState;
207    prevState.all = cookie;
208    int topIndex = segmentState.parts.topIndex;
209    int bottomIndex = prevState.parts.topIndex;
210
211    assert(table != NULL);
212    assert(allocEntries <= maxEntries);
213    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
214
215    int idx = extractIndex(iref);
216    if (idx < bottomIndex) {
217        /* wrong segment */
218        LOGV("Attempt to remove index outside index area (%d vs %d-%d)",
219            idx, bottomIndex, topIndex);
220        return false;
221    }
222    if (idx >= topIndex) {
223        /* bad -- stale reference? */
224        LOGD("Attempt to remove invalid index %d (bottom=%d top=%d)",
225            idx, bottomIndex, topIndex);
226        return false;
227    }
228
229    if (idx == topIndex-1) {
230        /*
231         * Top-most entry.  Scan up and consume holes.  No need to NULL
232         * out the entry, since the test vs. topIndex will catch it.
233         */
234        if (!checkEntry(iref, idx)) {
235            return false;
236        }
237        updateSlotRemove(idx);
238
239#ifndef NDEBUG
240        table[idx] = (Object*)0xd3d3d3d3;
241#endif
242
243        int numHoles =
244            segmentState.parts.numHoles - prevState.parts.numHoles;
245        if (numHoles != 0) {
246            while (--topIndex > bottomIndex && numHoles != 0) {
247                LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
248                    topIndex-1, cookie, table[topIndex-1]);
249                if (table[topIndex-1] != NULL)
250                    break;
251                LOGV("+++ ate hole at %d", topIndex-1);
252                numHoles--;
253            }
254            segmentState.parts.numHoles =
255                numHoles + prevState.parts.numHoles;
256            segmentState.parts.topIndex = topIndex;
257        } else {
258            segmentState.parts.topIndex = topIndex-1;
259            LOGV("+++ ate last entry %d", topIndex-1);
260        }
261    } else {
262        /*
263         * Not the top-most entry.  This creates a hole.  We NULL out the
264         * entry to prevent somebody from deleting it twice and screwing up
265         * the hole count.
266         */
267        if (table[idx] == NULL) {
268            LOGV("--- WEIRD: removing null entry %d", idx);
269            return false;
270        }
271        if (!checkEntry(iref, idx)) {
272            return false;
273        }
274        updateSlotRemove(idx);
275
276        table[idx] = NULL;
277        segmentState.parts.numHoles++;
278        LOGV("+++ left hole at %d, holes=%d",
279            idx, segmentState.parts.numHoles);
280    }
281
282    return true;
283}
284
285const char* indirectRefKindToString(IndirectRefKind kind)
286{
287    switch (kind) {
288    case kIndirectKindInvalid:      return "invalid";
289    case kIndirectKindLocal:        return "local";
290    case kIndirectKindGlobal:       return "global";
291    case kIndirectKindWeakGlobal:   return "weak global";
292    default:                        return "UNKNOWN";
293    }
294}
295
296void IndirectRefTable::dump(const char* descr) const
297{
298    dvmDumpReferenceTableContents(table, capacity(), descr);
299}
300