Clean up IndirectRefTable a bit.

The main purpose here was to have slightly less unclear warnings for
JNI local reference abuse.

Change-Id: I2c6378dd0a94d8afb96a8e409f7460205e3cd315
diff --git a/vm/CheckJni.cpp b/vm/CheckJni.cpp
index d1ecd10..7f3e631 100644
--- a/vm/CheckJni.cpp
+++ b/vm/CheckJni.cpp
@@ -264,6 +264,11 @@
 #define kFlag_ExcepBad      0x0000      /* raised exceptions are bad */
 #define kFlag_ExcepOkay     0x0004      /* ...okay */
 
+static const char* indirectRefKindName(IndirectRef iref)
+{
+    return indirectRefKindToString(indirectRefKind(iref));
+}
+
 class ScopedCheck {
 public:
     explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName)
@@ -294,7 +299,8 @@
         Object* obj = dvmDecodeIndirectRef(mEnv, jarr);
 
         if (!dvmIsValidObject(obj)) {
-            LOGW("JNI WARNING: jarray is invalid %s ref (%p)", dvmIndirectRefTypeName(jarr), jarr);
+            LOGW("JNI WARNING: jarray is an invalid %s reference (%p)",
+                    indirectRefKindName(jarr), jarr);
             printWarn = true;
         } else if (obj->clazz->descriptor[0] != '[') {
             LOGW("JNI WARNING: jarray arg has wrong type (expected array, got %s)",
@@ -354,8 +360,8 @@
              * and valid.
              */
             if (obj != NULL && !dvmIsValidObject(obj)) {
-                LOGW("JNI WARNING: field operation on invalid %s ref (%p)",
-                        dvmIndirectRefTypeName(jobj), jobj);
+                LOGW("JNI WARNING: field operation on invalid %s reference (%p)",
+                        indirectRefKindName(jobj), jobj);
                 printWarn = true;
             } else {
                 ClassObject* fieldClass = dvmFindLoadedClass(field->signature);
@@ -458,8 +464,7 @@
 
         bool printWarn = false;
         if (dvmGetJNIRefType(mEnv, jobj) == JNIInvalidRefType) {
-            LOGW("JNI WARNING: %p is not a valid JNI reference (type=%s)",
-            jobj, dvmIndirectRefTypeName(jobj));
+            LOGW("JNI WARNING: %p is not a valid JNI reference", jobj);
             printWarn = true;
         } else {
             Object* obj = dvmDecodeIndirectRef(mEnv, jobj);
@@ -727,8 +732,8 @@
         Object* obj = dvmDecodeIndirectRef(mEnv, jobj);
 
         if (!dvmIsValidObject(obj)) {
-            LOGW("JNI WARNING: %s is invalid %s ref (%p)",
-                    argName, dvmIndirectRefTypeName(jobj), jobj);
+            LOGW("JNI WARNING: %s is an invalid %s reference (%p)",
+                    argName, indirectRefKindName(jobj), jobj);
             printWarn = true;
         } else if (obj->clazz != expectedClass) {
             LOGW("JNI WARNING: %s arg has wrong type (expected %s, got %s)",
diff --git a/vm/IndirectRefTable.cpp b/vm/IndirectRefTable.cpp
index 225d8c2..270727c 100644
--- a/vm/IndirectRefTable.cpp
+++ b/vm/IndirectRefTable.cpp
@@ -19,32 +19,31 @@
  */
 #include "Dalvik.h"
 
-/*
- * Initialize an IndirectRefTable structure.
- */
-bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
-    int maxCount, IndirectRefKind kind)
+bool IndirectRefTable::init(size_t initialCount,
+        size_t maxCount, IndirectRefKind desiredKind)
 {
     assert(initialCount > 0);
     assert(initialCount <= maxCount);
     assert(kind != kIndirectKindInvalid);
 
-    pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
-    if (pRef->table == NULL)
+    table = (Object**) malloc(initialCount * sizeof(Object*));
+    if (table == NULL) {
         return false;
+    }
 #ifndef NDEBUG
-    memset(pRef->table, 0xd1, initialCount * sizeof(Object*));
+    memset(table, 0xd1, initialCount * sizeof(Object*));
 #endif
 
-    pRef->slotData =
+    slotData =
         (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
-    if (pRef->slotData == NULL)
+    if (slotData == NULL) {
         return false;
+    }
 
-    pRef->segmentState.all = IRT_FIRST_SEGMENT;
-    pRef->allocEntries = initialCount;
-    pRef->maxEntries = maxCount;
-    pRef->kind = kind;
+    segmentState.all = IRT_FIRST_SEGMENT;
+    allocEntries = initialCount;
+    maxEntries = maxCount;
+    kind = desiredKind;
 
     return true;
 }
@@ -52,133 +51,69 @@
 /*
  * Clears out the contents of a IndirectRefTable, freeing allocated storage.
  */
-void dvmClearIndirectRefTable(IndirectRefTable* pRef)
+void IndirectRefTable::destroy()
 {
-    free(pRef->table);
-    free(pRef->slotData);
-    pRef->table = NULL;
-    pRef->allocEntries = pRef->maxEntries = -1;
-}
-
-/*
- * Remove one or more segments from the top.  The table entry identified
- * by "cookie" becomes the new top-most entry.
- *
- * Returns false if "cookie" is invalid or the table has only one segment.
- */
-bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie)
-{
-    IRTSegmentState sst;
-
-    /*
-     * The new value for "top" must be <= the current value.  Otherwise
-     * this would represent an expansion of the table.
-     */
-    sst.all = cookie;
-    if (sst.parts.topIndex > pRef->segmentState.parts.topIndex) {
-        LOGE("Attempt to expand table with segment pop (%d to %d)",
-            pRef->segmentState.parts.topIndex, sst.parts.topIndex);
-        return false;
-    }
-    if (sst.parts.numHoles >= sst.parts.topIndex) {
-        LOGE("Absurd numHoles in cookie (%d bi=%d)",
-            sst.parts.numHoles, sst.parts.topIndex);
-        return false;
-    }
-
-    LOGV("IRT %p[%d]: pop, top=%d holes=%d",
-        pRef, pRef->kind, sst.parts.topIndex, sst.parts.numHoles);
-
-    return true;
+    free(table);
+    free(slotData);
+    table = NULL;
+    allocEntries = maxEntries = -1;
 }
 
 /*
  * Make sure that the entry at "idx" is correctly paired with "iref".
  */
-static bool checkEntry(IndirectRefTable* pRef, IndirectRef iref, int idx)
+bool IndirectRefTable::checkEntry(IndirectRef iref, int idx) const
 {
-    Object* obj = pRef->table[idx];
-    IndirectRef checkRef = dvmObjectToIndirectRef(pRef, obj, idx, pRef->kind);
+    Object* obj = table[idx];
+    IndirectRef checkRef = toIndirectRef(obj, idx);
     if (checkRef != iref) {
-        LOGW("IRT %p[%d]: iref mismatch (req=%p vs cur=%p)",
-            pRef, pRef->kind, iref, checkRef);
+        LOGE("Attempt to use stale %s reference (req=%p vs cur=%p; table=%p)",
+                indirectRefKindToString(kind), iref, checkRef, this);
         return false;
     }
     return true;
 }
 
-/*
- * Update extended debug info when an entry is added.
- *
- * We advance the serial number, invalidating any outstanding references to
- * this slot.
- */
-static inline void updateSlotAdd(IndirectRefTable* pRef, Object* obj, int slot)
-{
-    if (pRef->slotData != NULL) {
-        IndirectRefSlot* pSlot = &pRef->slotData[slot];
-        pSlot->serial++;
-        //LOGI("+++ add [%d] slot %d (%p->%p), serial=%d",
-        //    pRef->kind, slot, obj, iref, pSlot->serial);
-        pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
-    }
-}
-
-/*
- * Update extended debug info when an entry is removed.
- */
-static inline void updateSlotRemove(IndirectRefTable* pRef, int slot)
-{
-    if (pRef->slotData != NULL) {
-        //IndirectRefSlot* pSlot = &pRef->slotData[slot];
-        //LOGI("+++ remove [%d] slot %d, serial now %d",
-        //    pRef->kind, slot, pSlot->serial);
-    }
-}
-
-/*
- * Add "obj" to "pRef".
- */
-IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
-    Object* obj)
+IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
 {
     IRTSegmentState prevState;
     prevState.all = cookie;
-    int topIndex = pRef->segmentState.parts.topIndex;
+    size_t topIndex = segmentState.parts.topIndex;
 
     assert(obj != NULL);
     assert(dvmIsValidObject(obj));
-    assert(pRef->table != NULL);
-    assert(pRef->allocEntries <= pRef->maxEntries);
-    assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+    assert(table != NULL);
+    assert(allocEntries <= maxEntries);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
 
-    if (topIndex == pRef->allocEntries) {
+    if (topIndex == allocEntries) {
         /* reached end of allocated space; did we hit buffer max? */
-        if (topIndex == pRef->maxEntries) {
-            LOGW("IndirectRefTable overflow (max=%d)", pRef->maxEntries);
+        if (topIndex == maxEntries) {
+            LOGW("%s reference table overflow (max=%d)",
+                    indirectRefKindToString(kind), maxEntries);
             return NULL;
         }
 
-        Object** newTable;
-        int newSize;
+        size_t newSize = allocEntries * 2;
+        if (newSize > maxEntries) {
+            newSize = maxEntries;
+        }
+        assert(newSize > allocEntries);
 
-        newSize = pRef->allocEntries * 2;
-        if (newSize > pRef->maxEntries)
-            newSize = pRef->maxEntries;
-        assert(newSize > pRef->allocEntries);
-
-        newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
+        Object** newTable = (Object**) realloc(table, newSize * sizeof(Object*));
         if (newTable == NULL) {
-            LOGE("Unable to expand iref table (from %d to %d, max=%d)",
-                pRef->allocEntries, newSize, pRef->maxEntries);
+            LOGE("Unable to expand %s reference table from %d to %d (max=%d)",
+                    indirectRefKindToString(kind), allocEntries,
+                    newSize, maxEntries);
             return false;
         }
-        LOGV("Growing ireftab %p from %d to %d (max=%d)",
-            pRef, pRef->allocEntries, newSize, pRef->maxEntries);
+        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 */
-        pRef->table = newTable;
-        pRef->allocEntries = newSize;
+        table = newTable;
+        allocEntries = newSize;
     }
 
     IndirectRef result;
@@ -188,26 +123,25 @@
      * the right spot.  If there's a hole, find it and fill it; otherwise,
      * add to the end of the list.
      */
-    int numHoles = pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
+    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 = &pRef->table[topIndex - 1];
+        Object** pScan = &table[topIndex - 1];
         assert(*pScan != NULL);
         while (*--pScan != NULL) {
-            assert(pScan >= pRef->table + prevState.parts.topIndex);
+            assert(pScan >= table + prevState.parts.topIndex);
         }
-        updateSlotAdd(pRef, obj, pScan - pRef->table);
-        result = dvmObjectToIndirectRef(pRef, obj, pScan - pRef->table,
-            pRef->kind);
+        updateSlotAdd(obj, pScan - table);
+        result = toIndirectRef(obj, pScan - table);
         *pScan = obj;
-        pRef->segmentState.parts.numHoles--;
+        segmentState.parts.numHoles--;
     } else {
         /* add to the end */
-        updateSlotAdd(pRef, obj, topIndex);
-        result = dvmObjectToIndirectRef(pRef, obj, topIndex, pRef->kind);
-        pRef->table[topIndex++] = obj;
-        pRef->segmentState.parts.topIndex = topIndex;
+        updateSlotAdd(obj, topIndex);
+        result = toIndirectRef(obj, topIndex);
+        table[topIndex++] = obj;
+        segmentState.parts.topIndex = topIndex;
     }
 
     assert(result != NULL);
@@ -219,34 +153,37 @@
  *
  * Returns "false" if something looks bad.
  */
-bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref)
+bool IndirectRefTable::getChecked(IndirectRef iref) const
 {
-    if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) {
-        LOGW("Invalid indirect reference 0x%08x", (u4) iref);
-        return false;
-    }
-
-    int topIndex = pRef->segmentState.parts.topIndex;
-    int idx = dvmIndirectRefToIndex(iref);
-
     if (iref == NULL) {
-        LOGD("Attempt to look up NULL iref");
+        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? */
-        LOGD("Attempt to access invalid index %d (top=%d)",
-            idx, topIndex);
+        LOGW("Attempt to access stale %s reference at index %d (top=%d)",
+            indirectRefKindToString(kind), idx, topIndex);
         return false;
     }
 
-    Object* obj = pRef->table[idx];
+    Object* obj = table[idx];
     if (obj == NULL) {
-        LOGD("Attempt to read from hole, iref=%p", iref);
+        LOGW("Attempt to access deleted %s reference (%p)",
+                indirectRefKindToString(kind), iref);
         return false;
     }
-    if (!checkEntry(pRef, iref, idx))
+    if (!checkEntry(iref, idx)) {
         return false;
+    }
 
     return true;
 }
@@ -264,19 +201,18 @@
  *
  * Returns "false" if nothing was removed.
  */
-bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
-    IndirectRef iref)
+bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
 {
     IRTSegmentState prevState;
     prevState.all = cookie;
-    int topIndex = pRef->segmentState.parts.topIndex;
+    int topIndex = segmentState.parts.topIndex;
     int bottomIndex = prevState.parts.topIndex;
 
-    assert(pRef->table != NULL);
-    assert(pRef->allocEntries <= pRef->maxEntries);
-    assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+    assert(table != NULL);
+    assert(allocEntries <= maxEntries);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
 
-    int idx = dvmIndirectRefToIndex(iref);
+    int idx = extractIndex(iref);
     if (idx < bottomIndex) {
         /* wrong segment */
         LOGV("Attempt to remove index outside index area (%d vs %d-%d)",
@@ -295,30 +231,31 @@
          * 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(pRef, iref, idx))
+        if (!checkEntry(iref, idx)) {
             return false;
-        updateSlotRemove(pRef, idx);
+        }
+        updateSlotRemove(idx);
 
 #ifndef NDEBUG
-        pRef->table[idx] = (Object*)0xd3d3d3d3;
+        table[idx] = (Object*)0xd3d3d3d3;
 #endif
 
         int numHoles =
-            pRef->segmentState.parts.numHoles - prevState.parts.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, pRef->table[topIndex-1]);
-                if (pRef->table[topIndex-1] != NULL)
+                    topIndex-1, cookie, table[topIndex-1]);
+                if (table[topIndex-1] != NULL)
                     break;
                 LOGV("+++ ate hole at %d", topIndex-1);
                 numHoles--;
             }
-            pRef->segmentState.parts.numHoles =
+            segmentState.parts.numHoles =
                 numHoles + prevState.parts.numHoles;
-            pRef->segmentState.parts.topIndex = topIndex;
+            segmentState.parts.topIndex = topIndex;
         } else {
-            pRef->segmentState.parts.topIndex = topIndex-1;
+            segmentState.parts.topIndex = topIndex-1;
             LOGV("+++ ate last entry %d", topIndex-1);
         }
     } else {
@@ -327,29 +264,27 @@
          * entry to prevent somebody from deleting it twice and screwing up
          * the hole count.
          */
-        if (pRef->table[idx] == NULL) {
+        if (table[idx] == NULL) {
             LOGV("--- WEIRD: removing null entry %d", idx);
             return false;
         }
-        if (!checkEntry(pRef, iref, idx))
+        if (!checkEntry(iref, idx)) {
             return false;
-        updateSlotRemove(pRef, idx);
+        }
+        updateSlotRemove(idx);
 
-        pRef->table[idx] = NULL;
-        pRef->segmentState.parts.numHoles++;
+        table[idx] = NULL;
+        segmentState.parts.numHoles++;
         LOGV("+++ left hole at %d, holes=%d",
-            idx, pRef->segmentState.parts.numHoles);
+            idx, segmentState.parts.numHoles);
     }
 
     return true;
 }
 
-/*
- * Return a type name, useful for debugging.
- */
-const char* dvmIndirectRefTypeName(IndirectRef iref)
+const char* indirectRefKindToString(IndirectRefKind kind)
 {
-    switch (dvmGetIndirectRefType(iref)) {
+    switch (kind) {
     case kIndirectKindInvalid:      return "invalid";
     case kIndirectKindLocal:        return "local";
     case kIndirectKindGlobal:       return "global";
@@ -358,11 +293,7 @@
     }
 }
 
-/*
- * Dump the contents of a IndirectRefTable to the log.
- */
-void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr)
+void IndirectRefTable::dump(const char* descr) const
 {
-    dvmDumpReferenceTableContents(pRef->table, dvmIndirectRefTableEntries(pRef),
-        descr);
+    dvmDumpReferenceTableContents(table, capacity(), descr);
 }
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
index 8b8f4d7..4b91c88 100644
--- a/vm/IndirectRefTable.h
+++ b/vm/IndirectRefTable.h
@@ -85,6 +85,9 @@
  */
 typedef void* IndirectRef;
 
+/* magic failure value; must not pass dvmIsValidObject() */
+#define kInvalidIndirectRefObject ((Object*)0xdead4321)
+
 /*
  * Indirect reference kind, used as the two low bits of IndirectRef.
  *
@@ -96,6 +99,15 @@
     kIndirectKindGlobal     = 2,
     kIndirectKindWeakGlobal = 3
 };
+const char* indirectRefKindToString(IndirectRefKind kind);
+
+/*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind indirectRefKind(IndirectRef iref)
+{
+    return (IndirectRefKind)((u4) iref & 0x03);
+}
 
 /*
  * Extended debugging structure.  We keep a parallel array of these, one
@@ -107,6 +119,9 @@
     Object*     previous[kIRTPrevCount];
 };
 
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT   0
+
 /*
  * Table definition.
  *
@@ -188,205 +203,137 @@
     /* semi-public - read-only during GC scan; pointer must not be kept */
     Object**        table;              /* bottom of the stack */
 
-    /* private */
-    IndirectRefSlot* slotData;          /* extended debugging info */
-    int             allocEntries;       /* #of entries we have space for */
-    int             maxEntries;         /* max #of entries allowed */
+    /*
+     * private:
+     *
+     * TODO: we can't make these private as long as the interpreter
+     * uses offsetof, since private member data makes us non-POD.
+     */
     IndirectRefKind kind;               /* bit mask, ORed into all irefs */
+    IndirectRefSlot* slotData;          /* extended debugging info */
+    size_t          allocEntries;       /* #of entries we have space for */
+    size_t          maxEntries;         /* max #of entries allowed */
 
     // TODO: want hole-filling stats (#of holes filled, total entries scanned)
     //       for performance evaluation.
-};
 
-/* use as initial value for "cookie", and when table has only one segment */
-#define IRT_FIRST_SEGMENT   0
+    /*
+     * Add a new entry.  "obj" must be a valid non-NULL object reference
+     * (though it's okay if it's not fully-formed, e.g. the result from
+     * dvmMalloc doesn't have obj->clazz set).
+     *
+     * Returns NULL if the table is full (max entries reached, or alloc
+     * failed during expansion).
+     */
+    IndirectRef add(u4 cookie, Object* obj);
 
-/*
- * (This is PRIVATE, but we want it inside other inlines in this header.)
- *
- * Indirectify the object.
- *
- * The object pointer itself is subject to relocation in some GC
- * implementations, so we shouldn't really be using it here.
- */
-INLINE IndirectRef dvmObjectToIndirectRef(IndirectRefTable* pRef,
-    Object* obj, u4 tableIndex, IndirectRefKind kind)
-{
-    assert(tableIndex < 65536);
-    //u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff;
-    //u4 uref = objChunk << 18 | (tableIndex << 2) | kind;
-    u4 serialChunk = pRef->slotData[tableIndex].serial;
-    u4 uref = serialChunk << 20 | (tableIndex << 2) | kind;
-    return (IndirectRef) uref;
-}
-
-/*
- * (This is PRIVATE, but we want it inside other inlines in this header.)
- *
- * Extract the table index from an indirect reference.
- */
-INLINE u4 dvmIndirectRefToIndex(IndirectRef iref)
-{
-    u4 uref = (u4) iref;
-    return (uref >> 2) & 0xffff;
-}
-
-/*
- * Determine what kind of indirect reference this is.
- */
-INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
-{
-    return (IndirectRefKind)((u4) iref & 0x03);
-}
-
-/*
- * Return a string constant describing the indirect ref type.
- */
-const char* dvmIndirectRefTypeName(IndirectRef iref);
-
-/*
- * Initialize an IndirectRefTable.
- *
- * If "initialCount" != "maxCount", the table will expand as required.
- *
- * "kind" should be Local or Global.  The Global table may also hold
- * WeakGlobal refs.
- *
- * Returns "false" if table allocation fails.
- */
-bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
-    int maxCount, IndirectRefKind kind);
-
-/*
- * Clear out the contents, freeing allocated storage.  Does not free "pRef".
- *
- * You must call dvmInitReferenceTable() before you can re-use this table.
- */
-void dvmClearIndirectRefTable(IndirectRefTable* pRef);
-
-/*
- * Start a new segment at the top of the table.
- *
- * Returns an opaque 32-bit value that must be provided when the segment
- * is to be removed.
- *
- * IMPORTANT: this is implemented as a single instruction in mterp, rather
- * than a call here.  You can add debugging aids for the C-language
- * interpreters, but the basic implementation may not change.
- */
-INLINE u4 dvmPushIndirectRefTableSegment(IndirectRefTable* pRef)
-{
-    return pRef->segmentState.all;
-}
-
-/* extra debugging checks */
-bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie);
-
-/*
- * Remove one or more segments from the top.  The table entry identified
- * by "cookie" becomes the new top-most entry.
- *
- * IMPORTANT: this is implemented as a single instruction in mterp, rather
- * than a call here.  You can add debugging aids for the C-language
- * interpreters, but the basic implementation must not change.
- */
-INLINE void dvmPopIndirectRefTableSegment(IndirectRefTable* pRef, u4 cookie)
-{
-    dvmPopIndirectRefTableSegmentCheck(pRef, cookie);
-    pRef->segmentState.all = cookie;
-}
-
-/*
- * Return the #of entries in the entire table.  This includes holes, and
- * so may be larger than the actual number of "live" entries.
- */
-INLINE size_t dvmIndirectRefTableEntries(const IndirectRefTable* pRef)
-{
-    return pRef->segmentState.parts.topIndex;
-}
-
-/*
- * Returns "true" if the table is full.  The table is considered full if
- * we would need to expand it to add another entry to the current segment.
- */
-INLINE size_t dvmIsIndirectRefTableFull(const IndirectRefTable* pRef)
-{
-    return dvmIndirectRefTableEntries(pRef) == (size_t)pRef->allocEntries;
-}
-
-/*
- * Add a new entry.  "obj" must be a valid non-NULL object reference
- * (though it's okay if it's not fully-formed, e.g. the result from
- * dvmMalloc doesn't have obj->clazz set).
- *
- * Returns NULL if the table is full (max entries reached, or alloc
- * failed during expansion).
- */
-IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
-    Object* obj);
-
-/*
- * Add a new entry at the end.  Similar to Add but does not usually attempt
- * to fill in holes.  This is only appropriate to use right after a new
- * segment has been pushed.
- *
- * (This is intended for use when calling into a native JNI method, so
- * performance is critical.)
- */
-INLINE IndirectRef dvmAppendToIndirectRefTable(IndirectRefTable* pRef,
-    u4 cookie, Object* obj)
-{
-    int topIndex = pRef->segmentState.parts.topIndex;
-    if (topIndex == pRef->allocEntries) {
-        /* up against alloc or max limit, call the fancy version */
-        return dvmAddToIndirectRefTable(pRef, cookie, obj);
-    } else {
-        IndirectRef result = dvmObjectToIndirectRef(pRef, obj, topIndex,
-            pRef->kind);
-        pRef->table[topIndex++] = obj;
-        pRef->segmentState.parts.topIndex = topIndex;
-        return result;
+    /*
+     * Given an IndirectRef in the table, return the Object it refers to.
+     *
+     * Returns kInvalidIndirectRefObject if iref is invalid.
+     */
+    Object* get(IndirectRef iref) const {
+        if (!getChecked(iref)) {
+            return kInvalidIndirectRefObject;
+        }
+        return table[extractIndex(iref)];
     }
-}
 
-/* extra debugging checks */
-bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref);
+    /*
+     * Remove an existing entry.
+     *
+     * 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.
+     *
+     * Returns "false" if nothing was removed.
+     */
+    bool remove(u4 cookie, IndirectRef iref);
 
-/* magic failure value; must not pass dvmIsValidObject() */
-#define kInvalidIndirectRefObject ((Object*)0xdead4321)
+    /*
+     * Initialize an IndirectRefTable.
+     *
+     * If "initialCount" != "maxCount", the table will expand as required.
+     *
+     * "kind" should be Local or Global.  The Global table may also hold
+     * WeakGlobal refs.
+     *
+     * Returns "false" if table allocation fails.
+     */
+    bool init(size_t initialCount, size_t maxCount, IndirectRefKind kind);
 
-/*
- * Given an IndirectRef in the table, return the Object it refers to.
- *
- * Returns kInvalidIndirectRefObject if iref is invalid.
- */
-INLINE Object* dvmGetFromIndirectRefTable(IndirectRefTable* pRef,
-    IndirectRef iref)
-{
-    if (!dvmGetFromIndirectRefTableCheck(pRef, iref))
-        return kInvalidIndirectRefObject;
+    /*
+     * Clear out the contents, freeing allocated storage.
+     *
+     * You must call dvmInitReferenceTable() before you can re-use this table.
+     *
+     * TODO: this should be a destructor.
+     */
+    void destroy();
 
-    int idx = dvmIndirectRefToIndex(iref);
-    return pRef->table[idx];
-}
+    /*
+     * Dump the contents of a reference table to the log file.
+     *
+     * The caller should lock any external sync before calling.
+     *
+     * TODO: we should name the table in a constructor and remove
+     * the argument here.
+     */
+    void dump(const char* descr) const;
 
-/*
- * Remove an existing entry.
- *
- * 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.
- *
- * Returns "false" if nothing was removed.
- */
-bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
-    IndirectRef iref);
+    /*
+     * Return the #of entries in the entire table.  This includes holes, and
+     * so may be larger than the actual number of "live" entries.
+     */
+    size_t capacity() const {
+        return segmentState.parts.topIndex;
+    }
 
-/*
- * Dump the contents of a reference table to the log file.
- *
- * The caller should lock any external sync before calling.
- */
-void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr);
+private:
+    /*
+     * Extract the table index from an indirect reference.
+     */
+    static u4 extractIndex(IndirectRef iref) {
+        u4 uref = (u4) iref;
+        return (uref >> 2) & 0xffff;
+    }
+
+    /*
+     * The object pointer itself is subject to relocation in some GC
+     * implementations, so we shouldn't really be using it here.
+     */
+    IndirectRef toIndirectRef(Object* obj, u4 tableIndex) const {
+        assert(tableIndex < 65536);
+        //u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff;
+        //u4 uref = objChunk << 18 | (tableIndex << 2) | kind;
+        u4 serialChunk = slotData[tableIndex].serial;
+        u4 uref = serialChunk << 20 | (tableIndex << 2) | kind;
+        return (IndirectRef) uref;
+    }
+
+    /*
+     * Update extended debug info when an entry is added.
+     *
+     * We advance the serial number, invalidating any outstanding references to
+     * this slot.
+     */
+    void updateSlotAdd(Object* obj, int slot) {
+        if (slotData != NULL) {
+            IndirectRefSlot* pSlot = &slotData[slot];
+            pSlot->serial++;
+            pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
+        }
+    }
+
+    /*
+     * Update extended debug info when an entry is removed.
+     */
+    void updateSlotRemove(int slot) {
+    }
+
+    /* extra debugging checks */
+    bool getChecked(IndirectRef) const;
+    bool checkEntry(IndirectRef, int) const;
+};
 
 #endif  // DALVIK_INDIRECTREFTABLE_H_
diff --git a/vm/Jni.cpp b/vm/Jni.cpp
index 49a2c8a..474dba7 100644
--- a/vm/Jni.cpp
+++ b/vm/Jni.cpp
@@ -241,14 +241,12 @@
 #define kPinComplainThreshold       10
 
 bool dvmJniStartup() {
-    if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
-                                 kGlobalRefsTableInitialSize,
+    if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
                                  kGlobalRefsTableMaxSize,
                                  kIndirectKindGlobal)) {
         return false;
     }
-    if (!dvmInitIndirectRefTable(&gDvm.jniWeakGlobalRefTable,
-                                 kWeakGlobalRefsTableInitialSize,
+    if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize,
                                  kGlobalRefsTableMaxSize,
                                  kIndirectKindWeakGlobal)) {
         return false;
@@ -269,8 +267,8 @@
 }
 
 void dvmJniShutdown() {
-    dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
-    dvmClearIndirectRefTable(&gDvm.jniWeakGlobalRefTable);
+    gDvm.jniGlobalRefTable.destroy();
+    gDvm.jniWeakGlobalRefTable.destroy();
     dvmClearReferenceTable(&gDvm.jniPinRefTable);
 }
 
@@ -311,30 +309,24 @@
         return NULL;
     }
 
-    Object* result;
-
-    switch (dvmGetIndirectRefType(jobj)) {
+    switch (indirectRefKind(jobj)) {
     case kIndirectKindLocal:
-        {
-            IndirectRefTable* pRefTable = getLocalRefTable(env);
-            result = dvmGetFromIndirectRefTable(pRefTable, jobj);
-        }
-        break;
+        return getLocalRefTable(env)->get(jobj);
     case kIndirectKindGlobal:
         {
             // TODO: find a way to avoid the mutex activity here
             IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
             ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
-            result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+            return pRefTable->get(jobj);
         }
-        break;
     case kIndirectKindWeakGlobal:
         {
-            // TODO: find a way to avoid the mutex activity here
-            IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable;
+            Object* result;
             {
+                // TODO: find a way to avoid the mutex activity here
+                IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable;
                 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
-                result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+                result = pRefTable->get(jobj);
             }
             /*
              * TODO: this is a temporary workaround for broken weak global
@@ -348,17 +340,14 @@
                 LOGW("Warning: used weak global ref hack");
                 result = NULL;
             }
+            return result;
         }
-        break;
     case kIndirectKindInvalid:
     default:
         LOGW("Invalid indirect reference %p in decodeIndirectRef", jobj);
         dvmAbort();
-        result = kInvalidIndirectRefObject;
-        break;
+        return kInvalidIndirectRefObject;
     }
-
-    return result;
 }
 
 /*
@@ -381,19 +370,19 @@
     IndirectRefTable* pRefTable = getLocalRefTable(env);
     void* curFrame = ((JNIEnvExt*)env)->self->interpSave.curFrame;
     u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
-    jobject jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
+    jobject jobj = (jobject) pRefTable->add(cookie, obj);
     if (jobj == NULL) {
-        dvmDumpIndirectRefTable(pRefTable, "JNI local");
-        LOGE("Failed adding to JNI local ref table (has %d entries)",
-            (int) dvmIndirectRefTableEntries(pRefTable));
+        pRefTable->dump("JNI local");
+        LOGE("Failed adding to JNI local ref table (has %zd entries)",
+                pRefTable->capacity());
         dvmDumpThread(dvmThreadSelf(), false);
         dvmAbort();     // spec says call FatalError; this is equivalent
     } else {
         if (false) {
-            LOGI("LREF add %p  (%s.%s) (ent=%d)", obj,
+            LOGI("LREF add %p  (%s.%s) (ent=%zd)", obj,
                     dvmGetCurrentJNIMethod()->clazz->descriptor,
                     dvmGetCurrentJNIMethod()->name,
-                    (int) dvmIndirectRefTableEntries(pRefTable));
+                    pRefTable->capacity());
         }
     }
 
@@ -406,7 +395,7 @@
  */
 static bool ensureLocalCapacity(JNIEnv* env, int capacity) {
     IndirectRefTable* pRefTable = getLocalRefTable(env);
-    int numEntries = dvmIndirectRefTableEntries(pRefTable);
+    int numEntries = pRefTable->capacity();
     // TODO: this isn't quite right, since "numEntries" includes holes
     return ((kJniLocalRefMax - numEntries) >= capacity);
 }
@@ -424,7 +413,7 @@
     u4 cookie =
         SAVEAREA_FROM_FP(self->interpSave.curFrame)->xtra.localRefCookie;
 
-    if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
+    if (!pRefTable->remove(cookie, jobj)) {
         /*
          * Attempting to delete a local reference that is not in the
          * topmost local reference frame is a no-op.  DeleteLocalRef returns
@@ -489,12 +478,11 @@
      * we're either leaking global ref table entries or we're going to
      * run out of space in the GC heap.
      */
-    jobject jobj = (jobject) dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
-            obj);
+    jobject jobj = (jobject) gDvm.jniGlobalRefTable.add(IRT_FIRST_SEGMENT, obj);
     if (jobj == NULL) {
-        dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
-        LOGE("Failed adding to JNI global ref table (%d entries)",
-            (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
+        gDvm.jniGlobalRefTable.dump("JNI global");
+        LOGE("Failed adding to JNI global ref table (%zd entries)",
+                gDvm.jniGlobalRefTable.capacity());
         dvmAbort();
     }
 
@@ -504,7 +492,7 @@
 
     /* GREF usage tracking; should probably be disabled for production env */
     if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
-        int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+        int count = gDvm.jniGlobalRefTable.capacity();
         // TODO: adjust for "holes"
         if (count > gDvm.jniGlobalRefHiMark) {
             LOGD("GREF has increased to %d", count);
@@ -516,7 +504,7 @@
                 if (gDvmJni.warnOnly) {
                     LOGW("Excessive JNI global references (%d)", count);
                 } else {
-                    dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+                    gDvm.jniGlobalRefTable.dump("JNI global");
                     LOGE("Excessive JNI global references (%d)", count);
                     dvmAbort();
                 }
@@ -533,11 +521,11 @@
 
     ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
     IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
-    jobject jobj = (jobject) dvmAddToIndirectRefTable(table, IRT_FIRST_SEGMENT, obj);
+    jobject jobj = (jobject) table->add(IRT_FIRST_SEGMENT, obj);
     if (jobj == NULL) {
-        dvmDumpIndirectRefTable(table, "JNI weak global");
+        table->dump("JNI weak global");
         LOGE("Failed adding to JNI weak global ref table (%zd entries)",
-             dvmIndirectRefTableEntries(table));
+                table->capacity());
     }
     return jobj;
 }
@@ -549,7 +537,7 @@
 
     ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
     IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
-    if (!dvmRemoveFromIndirectRefTable(table, IRT_FIRST_SEGMENT, jobj)) {
+    if (!table->remove(IRT_FIRST_SEGMENT, jobj)) {
         LOGW("JNI: DeleteWeakGlobalRef(%p) failed to find entry", jobj);
     }
 }
@@ -567,13 +555,13 @@
     }
 
     ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
-    if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT, jobj)) {
+    if (!gDvm.jniGlobalRefTable.remove(IRT_FIRST_SEGMENT, jobj)) {
         LOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj);
         return;
     }
 
     if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
-        int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+        int count = gDvm.jniGlobalRefTable.capacity();
         // TODO: not quite right, need to subtract holes
         if (count < gDvm.jniGlobalRefLoMark) {
             LOGD("GREF has decreased to %d", count);
@@ -656,8 +644,8 @@
     Thread* self = dvmThreadSelf();
     JNIEnv* env = self->jniEnv;
     IndirectRefTable* pLocalRefs = getLocalRefTable(env);
-    dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
-    dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+    pLocalRefs->dump("JNI local");
+    gDvm.jniGlobalRefTable.dump("JNI global");
     dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
 }
 
@@ -686,7 +674,7 @@
     if (obj == kInvalidIndirectRefObject) {
         return JNIInvalidRefType;
     } else {
-        return (jobjectRefType) dvmGetIndirectRefType(jobj);
+        return (jobjectRefType) indirectRefKind(jobj);
     }
 }
 
diff --git a/vm/Thread.cpp b/vm/Thread.cpp
index 3844b25..5122adf 100644
--- a/vm/Thread.cpp
+++ b/vm/Thread.cpp
@@ -922,9 +922,10 @@
      * Most threads won't use jniMonitorRefTable, so we clear out the
      * structure but don't call the init function (which allocs storage).
      */
-    if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable,
-            kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal))
+    if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
+            kJniLocalRefMax, kIndirectKindLocal)) {
         return false;
+    }
     if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
             kInternalRefDefault, kInternalRefMax))
         return false;
@@ -984,7 +985,7 @@
 #endif
     }
 
-    dvmClearIndirectRefTable(&thread->jniLocalRefTable);
+    thread->jniLocalRefTable.destroy();
     dvmClearReferenceTable(&thread->internalLocalRefTable);
     if (&thread->jniMonitorRefTable.table != NULL)
         dvmClearReferenceTable(&thread->jniMonitorRefTable);
diff --git a/vm/alloc/MarkSweep.cpp b/vm/alloc/MarkSweep.cpp
index 309bd03..19fadc1 100644
--- a/vm/alloc/MarkSweep.cpp
+++ b/vm/alloc/MarkSweep.cpp
@@ -1001,7 +1001,7 @@
     IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
     Object **entry = table->table;
     GcMarkContext *ctx = &gDvm.gcHeap->markContext;
-    int numEntries = dvmIndirectRefTableEntries(table);
+    int numEntries = table->capacity();
     for (int i = 0; i < numEntries; ++i) {
         if (entry[i] != NULL && !isMarked(entry[i], ctx)) {
             entry[i] = NULL;
diff --git a/vm/alloc/Visit.cpp b/vm/alloc/Visit.cpp
index 7f949f6..a869418 100644
--- a/vm/alloc/Visit.cpp
+++ b/vm/alloc/Visit.cpp
@@ -71,7 +71,7 @@
     assert(visitor != NULL);
     assert(table != NULL);
     Object **entry = table->table;
-    int numEntries = dvmIndirectRefTableEntries(table);
+    int numEntries = table->capacity();
     for (int i = 0; i < numEntries; ++i) {
         (*visitor)(&entry[i], threadId, type, arg);
     }
diff --git a/vm/test/TestIndirectRefTable.cpp b/vm/test/TestIndirectRefTable.cpp
index de68c20..1e462e7 100644
--- a/vm/test/TestIndirectRefTable.cpp
+++ b/vm/test/TestIndirectRefTable.cpp
@@ -23,7 +23,7 @@
 
 #ifndef NDEBUG
 
-#define DBUG_MSG    LOGV
+#define DBUG_MSG    LOGI
 
 /*
  * Basic add/get/delete tests in an unsegmented table.
@@ -42,14 +42,12 @@
     const u4 cookie = IRT_FIRST_SEGMENT;
     bool result = false;
 
-    if (!dvmInitIndirectRefTable(&irt, kTableMax/2, kTableMax,
-            kIndirectKindGlobal))
-    {
+    if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) {
         return false;
     }
 
     iref0 = (IndirectRef) 0x11110;
-    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+    if (irt.remove(cookie, iref0)) {
         LOGE("unexpectedly successful removal");
         goto bail;
     }
@@ -58,44 +56,41 @@
      * Add three, check, remove in the order in which they were added.
      */
     DBUG_MSG("+++ START fifo\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
     if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
         LOGE("trivial add1 failed");
         goto bail;
     }
 
-    if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
-        dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
-        dvmGetFromIndirectRefTable(&irt, iref2) != obj2)
-    {
+    if (irt.get(iref0) != obj0 ||
+            irt.get(iref1) != obj1 ||
+            irt.get(iref2) != obj2) {
         LOGE("objects don't match expected values %p %p %p vs. %p %p %p",
-            dvmGetFromIndirectRefTable(&irt, iref0),
-            dvmGetFromIndirectRefTable(&irt, iref1),
-            dvmGetFromIndirectRefTable(&irt, iref2),
-            obj0, obj1, obj2);
+                irt.get(iref0), irt.get(iref1), irt.get(iref2),
+                obj0, obj1, obj2);
         goto bail;
     } else {
         DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
     }
 
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref2))
+    if (!irt.remove(cookie, iref0) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref2))
     {
         LOGE("fifo deletion failed");
         goto bail;
     }
 
     /* table should be empty now */
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("fifo del not empty");
         goto bail;
     }
 
     /* get invalid entry (off the end of the list) */
-    if (dvmGetFromIndirectRefTable(&irt, iref0) != kInvalidIndirectRefObject) {
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
         LOGE("stale entry get succeeded unexpectedly");
         goto bail;
     }
@@ -104,24 +99,24 @@
      * Add three, remove in the opposite order.
      */
     DBUG_MSG("+++ START lifo\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
     if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
         LOGE("trivial add2 failed");
         goto bail;
     }
 
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+    if (!irt.remove(cookie, iref2) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref0))
     {
         LOGE("lifo deletion failed");
         goto bail;
     }
 
     /* table should be empty now */
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("lifo del not empty");
         goto bail;
     }
@@ -131,42 +126,37 @@
      * to remove middle should fail.)
      */
     DBUG_MSG("+++ START unorder\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
     if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
         LOGE("trivial add3 failed");
         goto bail;
     }
 
-    if (dvmIndirectRefTableEntries(&irt) != 3) {
-        LOGE("expected 3 entries, found %d",
-            dvmIndirectRefTableEntries(&irt));
+    if (irt.capacity() != 3) {
+        LOGE("expected 3 entries, found %d", irt.capacity());
         goto bail;
     }
 
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
-        dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
-    {
+    if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) {
         LOGE("unorder deletion1 failed");
         goto bail;
     }
 
     /* get invalid entry (from hole) */
-    if (dvmGetFromIndirectRefTable(&irt, iref1) != kInvalidIndirectRefObject) {
+    if (irt.get(iref1) != kInvalidIndirectRefObject) {
         LOGE("hole get succeeded unexpectedly");
         goto bail;
     }
 
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
-    {
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
         LOGE("unorder deletion2 failed");
         goto bail;
     }
 
     /* table should be empty now */
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("unorder del not empty");
         goto bail;
     }
@@ -177,40 +167,36 @@
      * that we delete one and don't hole-compact the other.
      */
     DBUG_MSG("+++ START hole fill\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
-    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    iref3 = irt.add(cookie, obj3);
     if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
         LOGE("trivial add4 failed");
         goto bail;
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+    if (!irt.remove(cookie, iref1)) {
         LOGE("remove 1 of 4 failed");
         goto bail;
     }
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    if (dvmIndirectRefTableEntries(&irt) != 4) {
+    iref1 = irt.add(cookie, obj1);
+    if (irt.capacity() != 4) {
         LOGE("hole not filled");
         goto bail;
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
-    {
+    if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) {
         LOGE("remove 1/3 failed");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != 3) {
+    if (irt.capacity() != 3) {
         LOGE("should be 3 after two deletions");
         goto bail;
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
-    {
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
         LOGE("remove 2/0 failed");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("not empty after split remove");
         goto bail;
     }
@@ -221,18 +207,18 @@
      * With the extended checks in place, this should fail.
      */
     DBUG_MSG("+++ START switched\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj1);
+    if (irt.remove(cookie, iref0)) {
         LOGE("mismatched del succeeded (%p vs %p)", iref0, iref1);
         goto bail;
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+    if (!irt.remove(cookie, iref1)) {
         LOGE("switched del failed");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("switching del not empty");
         goto bail;
     }
@@ -241,63 +227,77 @@
      * Same as above, but with the same object.  A more rigorous checker
      * (e.g. with slot serialization) will catch this.
      */
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    DBUG_MSG("+++ START switched same object\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj0);
     if (iref0 != iref1) {
         /* try 0, should not work */
-        if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+        if (irt.remove(cookie, iref0)) {
             LOGE("temporal del succeeded (%p vs %p)", iref0, iref1);
             goto bail;
         }
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+    if (!irt.remove(cookie, iref1)) {
         LOGE("temporal cleanup failed");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("temporal del not empty");
         goto bail;
     }
 
+    DBUG_MSG("+++ START null lookup\n");
+    if (irt.get(NULL) != kInvalidIndirectRefObject) {
+        LOGE("null lookup succeeded");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ START stale lookup\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
+        LOGE("stale lookup succeeded");
+        goto bail;
+    }
+
     /*
      * Test table overflow.
      */
     DBUG_MSG("+++ START overflow\n");
     int i;
     for (i = 0; i < kTableMax; i++) {
-        manyRefs[i] = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+        manyRefs[i] = irt.add(cookie, obj0);
         if (manyRefs[i] == NULL) {
             LOGE("Failed adding %d of %d", i, kTableMax);
             goto bail;
         }
     }
-    if (dvmAddToIndirectRefTable(&irt, cookie, obj0) != NULL) {
+    if (irt.add(cookie, obj0) != NULL) {
         LOGE("Table overflow succeeded");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
-        LOGE("Expected %d entries, found %d",
-            kTableMax, dvmIndirectRefTableEntries(&irt));
+    if (irt.capacity() != (size_t)kTableMax) {
+        LOGE("Expected %d entries, found %d", kTableMax, irt.capacity());
         goto bail;
     }
     for (i = 0; i < kTableMax-1; i++) {
-        if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[i])) {
+        if (!irt.remove(cookie, manyRefs[i])) {
             LOGE("multi-remove failed at %d", i);
             goto bail;
         }
     }
     /* because of removal order, should have 20 entries, 19 of them holes */
-    if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
+    if (irt.capacity() != (size_t)kTableMax) {
         LOGE("Expected %d entries (with holes), found %d",
-            kTableMax, dvmIndirectRefTableEntries(&irt));
+                kTableMax, irt.capacity());
         goto bail;
     }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[kTableMax-1])) {
+    if (!irt.remove(cookie, manyRefs[kTableMax-1])) {
         LOGE("multi-remove final failed");
         goto bail;
     }
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
+    if (irt.capacity() != 0) {
         LOGE("multi-del not empty");
         goto bail;
     }
@@ -306,169 +306,11 @@
     result = true;
 
 bail:
-    dvmClearIndirectRefTable(&irt);
+    irt.destroy();
     return result;
 }
 
 /*
- * Test operations on a segmented table.
- */
-static bool segmentTest()
-{
-    static const int kTableMax = 20;
-    IndirectRefTable irt;
-    IndirectRef iref0, iref1, iref2, iref3;
-    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
-    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-    u4 cookie;
-    u4 segmentState[4];
-    bool result = false;
-
-    if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax,
-            kIndirectKindLocal))
-    {
-        return false;
-    }
-    cookie = segmentState[0] = IRT_FIRST_SEGMENT;
-    DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3);
-
-    /*
-     * Push two, create new segment, push two more, try to get all four,
-     * try to delete all 4.  All four should be accessible, but only the
-     * last two should be deletable.
-     */
-    DBUG_MSG("+++ START basic segment\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
-    DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
-    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
-
-    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
-        dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
-    {
-        LOGE("removed values from earlier segment");
-        goto bail;
-    }
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
-    {
-        LOGE("unable to remove values from current segment");
-        goto bail;
-    }
-    if (dvmIndirectRefTableEntries(&irt) != 2) {
-        LOGE("wrong total entries");
-        goto bail;
-    }
-    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
-    cookie = segmentState[0];
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
-        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
-    {
-        LOGE("unable to remove values from first segment");
-        goto bail;
-    }
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
-        LOGE("basic push/pop not empty");
-        goto bail;
-    }
-
-    /*
-     * Push two, delete first, segment, push two more, pop segment, verify
-     * the last two are no longer present and hole count is right.  The
-     * adds after the segment pop should not be filling in the hole.
-     */
-    DBUG_MSG("+++ START segment pop\n");
-    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
-    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
-    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
-    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
-    cookie = segmentState[0];
-    if (dvmIndirectRefTableEntries(&irt) != 2) {
-        LOGE("wrong total entries after pop");
-        goto bail;
-    }
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
-        LOGE("not back to zero after pop + del");
-        goto bail;
-    }
-
-    /*
-     * Multiple segments, some empty.
-     */
-    DBUG_MSG("+++ START multiseg\n");
-    iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0);
-    iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1);
-    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
-    cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt);
-    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
-    iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2);
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
-    cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt);
-    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
-
-    if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
-        dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
-        dvmGetFromIndirectRefTable(&irt, iref2) != obj2 ||
-        dvmGetFromIndirectRefTable(&irt, iref3) != obj3)
-    {
-        LOGE("Unable to retrieve all multiseg objects");
-        goto bail;
-    }
-
-    dvmDumpIndirectRefTable(&irt, "test");
-
-    //int i;
-    //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) {
-    //    DBUG_MSG("+++  segment %d = 0x%08x\n", i, segmentState[i]);
-    //}
-
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
-    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
-        LOGE("multiseg del2 worked");
-        goto bail;
-    }
-    dvmPopIndirectRefTableSegment(&irt, segmentState[3]);
-    cookie = segmentState[2];
-    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
-        LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)", cookie, iref2);
-        goto bail;
-    }
-    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
-
-    /* pop two off at once */
-    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
-    cookie = segmentState[0];
-
-    if (dvmIndirectRefTableEntries(&irt) != 2) {
-        LOGE("Unexpected entry count in multiseg");
-        goto bail;
-    }
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
-    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
-    if (dvmIndirectRefTableEntries(&irt) != 0) {
-        LOGE("Unexpected entry count at multiseg end");
-        goto bail;
-    }
-
-    DBUG_MSG("+++ segment test complete\n");
-    result = true;
-
-bail:
-    dvmClearIndirectRefTable(&irt);
-    return result;
-}
-
-
-/*
  * Some quick tests.
  */
 bool dvmTestIndirectRefTable()
@@ -477,10 +319,6 @@
         LOGE("IRT basic test failed");
         return false;
     }
-    if (!segmentTest()) {
-        LOGE("IRT segment test failed");
-        return false;
-    }
 
     return true;
 }