const jchar* result;
result = BASE_ENV(env)->GetStringChars(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
int len = dvmStringLen(string) * 2;
result = (const jchar*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
const char* result;
result = BASE_ENV(env)->GetStringUTFChars(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
int len = dvmStringUtf8ByteLen(string) + 1;
result = (const char*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
const jchar* result;
result = BASE_ENV(env)->GetStringCritical(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
int len = dvmStringLen(string) * 2;
result = (const jchar*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
* interfaces. Note this does not guarantee that it's a direct buffer.
*/
if (JNI_FALSE == (*env)->IsInstanceOf(env, buf,
- (jclass) gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
+ gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer))
{
goto bail;
}
free(desc);
}
- dvmCallMethodA(self, meth, pReq->obj, &pReq->resultValue,
+ dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
(jvalue*)pReq->argArray);
pReq->exceptObj = objectToObjectId(dvmGetException(self));
pReq->resultTag = resultTagFromSignature(meth);
# Compiler defines.
#
LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps
+#LOCAL_CFLAGS += -DUSE_INDIRECT_REF
#
# Optional features. These may impact the size or performance of the VM.
ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember;
ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray;
ClassObject* classOrgApacheHarmonyNioInternalDirectBuffer;
+ jclass jclassOrgApacheHarmonyNioInternalDirectBuffer;
/* synthetic classes for arrays of primitives */
ClassObject* classArrayBoolean;
/*
* JNI global reference table.
*/
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable jniGlobalRefTable;
+#else
ReferenceTable jniGlobalRefTable;
+#endif
pthread_mutex_t jniGlobalRefLock;
int jniGlobalRefHiMark;
int jniGlobalRefLoMark;
JNI provides explicit control over natively-held references that the GC
needs to know about. These can be local, in which case they're released
when the native method returns into the VM, or global, which are held
-until explicitly released.
+until explicitly released. (There are also weak-global references,
+which have the lifespan and visibility of global references, but the
+object they refer to may be collected.)
The references can be created with explicit JNI NewLocalRef / NewGlobalRef
calls. The former is very unusual, the latter is reasonably common
pathological situations, we need to reclaim the space used by deleted
entries.
-The simplest implementation is an expanding append-only array that compacts
-when objects are deleted. In typical situations, e.g. running through
-an array of objects, we will be deleting one of the most recently added
-entries, so we can minimize the number of elements moved (or avoid having
-to move any).
+If we just want to maintain a list for the GC root set, we can use an
+expanding append-only array that compacts when objects are deleted.
+In typical situations, e.g. running through an array of objects, we will
+be deleting one of the most recently added entries, so we can minimize
+the number of elements moved (or avoid having to move any).
+
+If we want to conceal the pointer values from native code, which is
+necessary to allow the GC to move JNI-referenced objects around, then we
+have to use a more complicated indirection mechanism.
The spec says, "Local references are only valid in the thread in which
they are created. The native code must not pass local references from
one thread to another."
+Pinned objects
+
+For some large chunks of data, notably primitive arrays and String data,
+JNI allows the VM to choose whether it wants to pin the array object or
+make a copy. We currently pin the memory for better execution performance.
+
+TODO: we're using simple root set references to pin primitive array data,
+because they have the property we need (i.e. the pointer we return is
+guaranteed valid until we explicitly release it). However, if we have a
+compacting GC and don't want to pin all memory held by all global refs,
+we need to treat these differently.
+
+
Global reference tracking
There should be a small "active" set centered around the most-recently
-added items. We can use an append-only, compacting array like we do for
-local refs.
+added items.
-Because it's global, access to it has to be synchronized.
+Because it's global, access to it has to be synchronized. Additions and
+removals require grabbing a mutex. If the table serves as an indirection
+mechanism (i.e. it's not just a list for the benefit of the garbage
+collector), reference lookups may also require grabbing a mutex.
The JNI spec does not define any sort of limit, so the list must be able
-to expand. It may be useful to log significant increases in usage to
-help identify resource leaks.
+to expand to a reasonable size. It may be useful to log significant
+increases in usage to help identify resource leaks.
+
-TODO: we currently use global references on strings and primitive array
-data, because they have the property we need (i.e. the pointer we return
-is guaranteed valid until we explicitly release it). However, if we have
-a compacting GC and don't want to pin all memory held by all global refs,
-we actually want to treat these differently. Either we need a way to
-tell the GC that specific global references are pinned, or we have to
-make a copy of the data and return that instead (something JNI supports).
+Weak-global reference tracking
+
+[TBD]
Local reference tracking
*** Approach #2: use a parallel stack
-Each Thread/JNIEnv points to a ReferenceTable struct. The struct
-has a system-heap-allocated array of references and a pointer to the
+Each Thread/JNIEnv points to a reference table. The struct has
+a system-heap-allocated array of references and a pointer to the
next-available entry ("nextEntry").
-Each stack frame has a pointer to what it sees as the "top" element in the
-array (we can double-up the "currentPc" field). This is set to "nextEntry"
-when the frame is pushed on. As local references are added or removed,
-"nextEntry" is updated.
+Each stack frame has a pointer to what it sees as the "bottom" element
+in the array (we can double-up the "currentPc" field). This is set to
+"nextEntry" when the frame is pushed on. As local references are added
+or removed, "nextEntry" is updated.
We implement Push/PopLocalFrame with actual stack frames. Before a JNI
frame gets popped, we set "nextEntry" to the "top" pointer of the current
/* fwd */
static const struct JNINativeInterface gNativeInterface;
-static jobject addLocalReference(JNIEnv* env, Object* obj);
static jobject addGlobalReference(Object* obj);
-
#ifdef WITH_JNI_STACK_CHECK
# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
# define CHECK_STACK_SUM(_self) checkStackSum(_self);
-static void computeStackSum(Thread* self);
-static void checkStackSum(Thread* self);
+//static void computeStackSum(Thread* self);
+//static void checkStackSum(Thread* self);
#else
# define COMPUTE_STACK_SUM(_self) ((void)0)
# define CHECK_STACK_SUM(_self) ((void)0)
/*
* ===========================================================================
- * JNI call bridge
- * ===========================================================================
- */
-
-/*
- * The functions here form a bridge between interpreted code and JNI native
- * functions. The basic task is to convert an array of primitives and
- * references into C-style function arguments. This is architecture-specific
- * and usually requires help from assembly code.
- *
- * The bridge takes four arguments: the array of parameters, a place to
- * store the function result (if any), the method to call, and a pointer
- * to the current thread.
- *
- * These functions aren't called directly from elsewhere in the VM.
- * A pointer in the Method struct points to one of these, and when a native
- * method is invoked the interpreter jumps to it.
- *
- * (The "internal native" methods are invoked the same way, but instead
- * of calling through a bridge, the target method is called directly.)
- *
- * The "args" array should not be modified, but we do so anyway for
- * performance reasons. We know that it points to the "outs" area on
- * the current method's interpreted stack. This area is ignored by the
- * precise GC, because there is no register map for a native method (for
- * an interpreted method the args would be listed in the argument set).
- * We know all of the values exist elsewhere on the interpreted stack,
- * because the method call setup copies them right before making the call,
- * so we don't have to worry about concealing stuff from the GC.
- *
- * If we don't want to modify "args", we either have to create a local
- * copy and modify it before calling dvmPlatformInvoke, or we have to do
- * the local reference replacement within dvmPlatformInvoke. The latter
- * has some performance advantages, though if we can inline the local
- * reference adds we may win when there's a lot of reference args (unless
- * we want to code up some local ref table manipulation in assembly.
- */
-
-/*
- * General form, handles all cases.
- */
-void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
- const Method* method, Thread* self)
-{
- int oldStatus;
- u4* modArgs = (u4*) args;
-
- assert(method->insns != NULL);
-
- //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
- // method->clazz->descriptor, method->name, method->shorty);
-
- /*
- * Walk the argument list, creating local references for appropriate
- * arguments.
- */
- JNIEnv* env = self->jniEnv;
- jclass staticMethodClass;
- int idx = 0;
- if (dvmIsStaticMethod(method)) {
- /* add the class object we pass in */
- staticMethodClass = addLocalReference(env, (Object*) method->clazz);
- if (staticMethodClass == NULL) {
- assert(dvmCheckException(self));
- return;
- }
- } else {
- /* add "this" */
- staticMethodClass = NULL;
- jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
- if (thisObj == NULL) {
- assert(dvmCheckException(self));
- return;
- }
- modArgs[idx] = (u4) thisObj;
- idx = 1;
- }
-
- const char* shorty = &method->shorty[1]; /* skip return type */
- while (*shorty != '\0') {
- switch (*shorty++) {
- case 'L':
- //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
- if (modArgs[idx] != 0) {
- //if (!dvmIsValidObject((Object*) modArgs[idx]))
- // dvmAbort();
- jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
- if (argObj == NULL) {
- assert(dvmCheckException(self));
- return;
- }
- modArgs[idx] = (u4) argObj;
- }
- break;
- case 'D':
- case 'J':
- idx++;
- break;
- default:
- /* Z B C S I -- do nothing */
- break;
- }
-
- idx++;
- }
-
- oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
-
- COMPUTE_STACK_SUM(self);
- dvmPlatformInvoke(self->jniEnv, staticMethodClass,
- method->jniArgInfo, method->insSize, modArgs, method->shorty,
- (void*)method->insns, pResult);
- CHECK_STACK_SUM(self);
-
- dvmChangeStatus(self, oldStatus);
-}
-
-/*
- * Handler for the unusual case of a synchronized native method.
- *
- * Lock the object, then call through the general function.
- */
-void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
- const Method* method, Thread* self)
-{
- Object* lockObj;
-
- assert(dvmIsSynchronizedMethod(method));
-
- if (dvmIsStaticMethod(method))
- lockObj = (Object*) method->clazz;
- else
- lockObj = (Object*) args[0];
-
- LOGVV("Calling %s.%s: locking %p (%s)\n",
- method->clazz->descriptor, method->name,
- lockObj, lockObj->clazz->descriptor);
-
- dvmLockObject(self, lockObj);
- dvmCallJNIMethod_general(args, pResult, method, self);
- dvmUnlockObject(self, lockObj);
-}
-
-/*
- * Virtual method call, no reference arguments.
- *
- * We need to local-ref the "this" argument, found in args[0].
- */
-void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
- const Method* method, Thread* self)
-{
- u4* modArgs = (u4*) args;
- int oldStatus;
-
- jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
- if (thisObj == NULL) {
- assert(dvmCheckException(self));
- return;
- }
- modArgs[0] = (u4) thisObj;
-
- oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
-
- COMPUTE_STACK_SUM(self);
- dvmPlatformInvoke(self->jniEnv, NULL,
- method->jniArgInfo, method->insSize, modArgs, method->shorty,
- (void*)method->insns, pResult);
- CHECK_STACK_SUM(self);
-
- dvmChangeStatus(self, oldStatus);
-}
-
-/*
- * Static method call, no reference arguments.
- *
- * We need to local-ref the class reference.
- */
-void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
- const Method* method, Thread* self)
-{
- jclass staticMethodClass;
- int oldStatus;
-
- staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
- if (staticMethodClass == NULL) {
- assert(dvmCheckException(self));
- return;
- }
-
- oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
-
- COMPUTE_STACK_SUM(self);
- dvmPlatformInvoke(self->jniEnv, staticMethodClass,
- method->jniArgInfo, method->insSize, args, method->shorty,
- (void*)method->insns, pResult);
- CHECK_STACK_SUM(self);
-
- dvmChangeStatus(self, oldStatus);
-}
-
-/*
- * Extract the return type enum from the "jniArgInfo" field.
- */
-DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
-{
- return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
-}
-
-
-/*
- * ===========================================================================
* Utility functions
* ===========================================================================
*/
*/
bool dvmJniStartup(void)
{
+#ifdef USE_INDIRECT_REF
+ if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
+ kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
+ kIndirectKindGlobal))
+ return false;
+#else
if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
return false;
+#endif
dvmInitMutex(&gDvm.jniGlobalRefLock);
gDvm.jniGlobalRefLoMark = 0;
LOGE("Unable to find internal direct buffer classes\n");
return false;
}
- /* needs to be a global ref so CheckJNI thinks we're allowed to see it */
- gDvm.classOrgApacheHarmonyNioInternalDirectBuffer =
- addGlobalReference((Object*) directBufferClass);
gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
+ gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
+ /* need a global reference for extended CheckJNI tests */
+ gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
+ addGlobalReference((Object*) directBufferClass);
/*
* We need a Method* here rather than a vtable offset, because
*/
void dvmJniShutdown(void)
{
+#ifdef USE_INDIRECT_REF
+ dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
+#else
dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
+#endif
}
* Going through "env" rather than dvmThreadSelf() is faster but will
* get weird if the JNI code is passing the wrong JNIEnv around.
*/
+#ifdef USE_INDIRECT_REF
+static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
+{
+ return &((JNIEnvExt*)env)->self->jniLocalRefTable;
+}
+#else
static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
{
//return &dvmThreadSelf()->jniLocalRefTable;
return &((JNIEnvExt*)env)->self->jniLocalRefTable;
}
+#endif
+
+/*
+ * Convert an indirect reference to an Object reference. The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
+ */
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
+{
+#ifdef USE_INDIRECT_REF
+ if (jobj == NULL)
+ return NULL;
+
+ Object* result;
+
+ switch (dvmGetIndirectRefType(jobj)) {
+ case kIndirectKindLocal:
+ {
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+ }
+ break;
+ case kIndirectKindGlobal:
+ {
+ // TODO: find a way to avoid the mutex activity here
+ IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ }
+ break;
+ case kIndirectKindWeakGlobal:
+ {
+ LOGE("weak-global not yet supported\n");
+ result = NULL;
+ dvmAbort();
+ }
+ break;
+ case kIndirectKindInvalid:
+ default:
+ LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
+ dvmAbort();
+ result = NULL;
+ break;
+ }
+
+ return result;
+#else
+ return (Object*) jobj;
+#endif
+}
/*
* Add a local reference for an object to the current stack frame. When
if (obj == NULL)
return NULL;
+ jobject jobj;
+
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
+ u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+
+ jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
+ if (jobj == NULL) {
+ dvmDumpIndirectRefTable(pRefTable, "JNI local");
+ LOGE("Failed adding to JNI local ref table (has %d entries)\n",
+ (int) dvmIndirectRefTableEntries(pRefTable));
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort(); // spec says call FatalError; this is equivalent
+ } else {
+ LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name,
+ (int) dvmReferenceTableEntries(pRefTable));
+ }
+#else
ReferenceTable* pRefTable = getLocalRefTable(env);
if (!dvmAddToReferenceTable(pRefTable, obj)) {
(int) dvmReferenceTableEntries(pRefTable));
}
- return obj;
-}
+ jobj = (jobject) obj;
+#endif
-/*
- * Convert an indirect reference to an Object reference. The indirect
- * reference may be local, global, or weak-global.
- *
- * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
- *
- * [ this is currently a no-op ]
- */
-Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
-{
- return (Object*) jobj;
+ return jobj;
}
/*
*/
static bool ensureLocalCapacity(JNIEnv* env, int capacity)
{
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ // TODO: this isn't quite right, since "numEntries" includes holes
+ return ((kJniLocalRefMax - numEntries) >= capacity);
+#else
ReferenceTable* pRefTable = getLocalRefTable(env);
return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
+#endif
}
/*
if (jobj == NULL)
return;
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ Thread* self = ((JNIEnvExt*)env)->self;
+ u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
+
+ if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
+ /*
+ * Attempting to delete a local reference that is not in the
+ * topmost local reference frame is a no-op. DeleteLocalRef returns
+ * void and doesn't throw any exceptions, but we should probably
+ * complain about it so the user will notice that things aren't
+ * going quite the way they expect.
+ */
+ LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
+ }
+#else
ReferenceTable* pRefTable = getLocalRefTable(env);
- Thread* self = dvmThreadSelf();
- Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
+ Thread* self = ((JNIEnvExt*)env)->self;
+ Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
- if (!dvmRemoveFromReferenceTable(pRefTable, top, (Object*) jobj)) {
+ if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
/*
* Attempting to delete a local reference that is not in the
* topmost local reference frame is a no-op. DeleteLocalRef returns
LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
jobj, dvmIsValidObject((Object*) jobj));
}
+#endif
}
/*
}
if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
ArrayObject* arrayObj = (ArrayObject*) obj;
- if (arrayObj->length == 8192 &&
- dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400)
+ if (arrayObj->length == 8192 /*&&
+ dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
{
LOGI("Adding global ref on byte array %p (len=%d)\n",
arrayObj, arrayObj->length);
dvmDumpThread(dvmThreadSelf(), false);
}
}
-
- dvmLockMutex(&gDvm.jniGlobalRefLock);
-
- /*
- * Expanding the table should happen rarely, so I'm not overly
- * concerned about the performance impact of copying the old list
- * over. We shouldn't see one-time activity spikes, so freeing
- * up storage shouldn't be required.
- *
- * Throwing an exception on failure is problematic, because JNI code
- * may not be expecting an exception, and things sort of cascade. We
- * want to have a hard limit to catch leaks during debugging, but this
- * otherwise needs to expand until memory is consumed. As a practical
- * matter, if we have many thousands of global references, chances are
- * we're either leaking global ref table entries or we're going to
- * run out of space in the GC heap.
- */
- if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) {
+
+ jobject jobj;
+
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+ /*
+ * Throwing an exception on failure is problematic, because JNI code
+ * may not be expecting an exception, and things sort of cascade. We
+ * want to have a hard limit to catch leaks during debugging, but this
+ * otherwise needs to expand until memory is consumed. As a practical
+ * matter, if we have many thousands of global references, chances are
+ * we're either leaking global ref table entries or we're going to
+ * run out of space in the GC heap.
+ */
+#ifdef USE_INDIRECT_REF
+ jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
+ obj);
+ if (jobj == NULL) {
+ dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+ LOGE("Failed adding to JNI global ref table (%d entries)\n",
+ (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
+ dvmAbort();
+ }
+
+ LOGVV("GREF add %p (%s.%s)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name);
+
+ /* GREF usage tracking; should probably be disabled for production env */
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+ // TODO: adjust for "holes"
+ if (count > gDvm.jniGlobalRefHiMark) {
+ LOGD("GREF has increased to %d\n", count);
+ gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+ /* watch for "excessive" use; not generally appropriate */
+ if (count >= gDvm.jniGrefLimit) {
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ if (vm->warnError) {
+ dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
+ "JNI global");
+ LOGE("Excessive JNI global references (%d)\n", count);
+ dvmAbort();
+ } else {
+ LOGW("Excessive JNI global references (%d)\n", count);
+ }
+ }
+ }
+ }
+#else
+ if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
LOGE("Failed adding to JNI global ref table (%d entries)\n",
(int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
dvmAbort();
}
+ jobj = (jobject) obj;
LOGVV("GREF add %p (%s.%s)\n", obj,
dvmGetCurrentJNIMethod()->clazz->descriptor,
}
}
}
+#endif
bail:
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
- return (jobject) obj;
+ return jobj;
}
/*
dvmLockMutex(&gDvm.jniGlobalRefLock);
+#ifdef USE_INDIRECT_REF
+ if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
+ IRT_FIRST_SEGMENT, jobj))
+ {
+ LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
+ goto bail;
+ }
+
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+ // TODO: not quite right, need to subtract holes
+ if (count < gDvm.jniGlobalRefLoMark) {
+ LOGD("GREF has decreased to %d\n", count);
+ gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+ }
+ }
+#else
if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
gDvm.jniGlobalRefTable.table, jobj))
{
gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
}
}
+#endif
bail:
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
*/
void dvmGcMarkJniGlobalRefs()
{
- Object **op;
+ Object** op;
dvmLockMutex(&gDvm.jniGlobalRefLock);
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+ op = pRefTable->table;
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ int i;
+
+ for (i = 0; i < numEntries; i++) {
+ Object* obj = *op;
+ if (obj != NULL)
+ dvmMarkObjectNonNull(obj);
+ op++;
+ }
+#else
op = gDvm.jniGlobalRefTable.table;
while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
dvmMarkObjectNonNull(*(op++));
}
+#endif
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
*/
jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
{
+#ifdef USE_INDIRECT_REF
+ /*
+ * IndirectRefKind is currently defined as an exact match of
+ * jobjectRefType, so this is easy.
+ */
+ return (jobjectRefType) dvmIndirectRefToIndex(jobj);
+#else
ReferenceTable* pRefTable = getLocalRefTable(env);
Thread* self = dvmThreadSelf();
//Object** top;
#endif
/* check locals */
- //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
//LOGI("--- REF found %p in locals\n", jobj);
return JNILocalRefType;
/* not found! */
return JNIInvalidRefType;
+#endif
}
/*
/*
* ===========================================================================
+ * JNI call bridge
+ * ===========================================================================
+ */
+
+/*
+ * The functions here form a bridge between interpreted code and JNI native
+ * functions. The basic task is to convert an array of primitives and
+ * references into C-style function arguments. This is architecture-specific
+ * and usually requires help from assembly code.
+ *
+ * The bridge takes four arguments: the array of parameters, a place to
+ * store the function result (if any), the method to call, and a pointer
+ * to the current thread.
+ *
+ * These functions aren't called directly from elsewhere in the VM.
+ * A pointer in the Method struct points to one of these, and when a native
+ * method is invoked the interpreter jumps to it.
+ *
+ * (The "internal native" methods are invoked the same way, but instead
+ * of calling through a bridge, the target method is called directly.)
+ *
+ * The "args" array should not be modified, but we do so anyway for
+ * performance reasons. We know that it points to the "outs" area on
+ * the current method's interpreted stack. This area is ignored by the
+ * precise GC, because there is no register map for a native method (for
+ * an interpreted method the args would be listed in the argument set).
+ * We know all of the values exist elsewhere on the interpreted stack,
+ * because the method call setup copies them right before making the call,
+ * so we don't have to worry about concealing stuff from the GC.
+ *
+ * If we don't want to modify "args", we either have to create a local
+ * copy and modify it before calling dvmPlatformInvoke, or we have to do
+ * the local reference replacement within dvmPlatformInvoke. The latter
+ * has some performance advantages, though if we can inline the local
+ * reference adds we may win when there's a lot of reference args (unless
+ * we want to code up some local ref table manipulation in assembly.
+ */
+
+/*
+ * General form, handles all cases.
+ */
+void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ int oldStatus;
+ u4* modArgs = (u4*) args;
+
+ assert(method->insns != NULL);
+
+ //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
+ // method->clazz->descriptor, method->name, method->shorty);
+
+ /*
+ * Walk the argument list, creating local references for appropriate
+ * arguments.
+ */
+ JNIEnv* env = self->jniEnv;
+ jclass staticMethodClass;
+ int idx = 0;
+ if (dvmIsStaticMethod(method)) {
+ /* add the class object we pass in */
+ staticMethodClass = addLocalReference(env, (Object*) method->clazz);
+ if (staticMethodClass == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ } else {
+ /* add "this" */
+ staticMethodClass = NULL;
+ jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
+ if (thisObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[idx] = (u4) thisObj;
+ idx = 1;
+ }
+
+ const char* shorty = &method->shorty[1]; /* skip return type */
+ while (*shorty != '\0') {
+ switch (*shorty++) {
+ case 'L':
+ //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
+ if (modArgs[idx] != 0) {
+ //if (!dvmIsValidObject((Object*) modArgs[idx]))
+ // dvmAbort();
+ jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
+ if (argObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[idx] = (u4) argObj;
+ }
+ break;
+ case 'D':
+ case 'J':
+ idx++;
+ break;
+ default:
+ /* Z B C S I -- do nothing */
+ break;
+ }
+
+ idx++;
+ }
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(self->jniEnv, staticMethodClass,
+ method->jniArgInfo, method->insSize, modArgs, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+}
+
+/*
+ * Handler for the unusual case of a synchronized native method.
+ *
+ * Lock the object, then call through the general function.
+ */
+void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* lockObj;
+
+ assert(dvmIsSynchronizedMethod(method));
+
+ if (dvmIsStaticMethod(method))
+ lockObj = (Object*) method->clazz;
+ else
+ lockObj = (Object*) args[0];
+
+ LOGVV("Calling %s.%s: locking %p (%s)\n",
+ method->clazz->descriptor, method->name,
+ lockObj, lockObj->clazz->descriptor);
+
+ dvmLockObject(self, lockObj);
+ dvmCallJNIMethod_general(args, pResult, method, self);
+ dvmUnlockObject(self, lockObj);
+}
+
+/*
+ * Virtual method call, no reference arguments.
+ *
+ * We need to local-ref the "this" argument, found in args[0].
+ */
+void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ u4* modArgs = (u4*) args;
+ int oldStatus;
+
+ jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
+ if (thisObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[0] = (u4) thisObj;
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(self->jniEnv, NULL,
+ method->jniArgInfo, method->insSize, modArgs, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+}
+
+/*
+ * Static method call, no reference arguments.
+ *
+ * We need to local-ref the class reference.
+ */
+void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ jclass staticMethodClass;
+ int oldStatus;
+
+ staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
+ if (staticMethodClass == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(self->jniEnv, staticMethodClass,
+ method->jniArgInfo, method->insSize, args, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+}
+
+/*
+ * Extract the return type enum from the "jniArgInfo" field.
+ */
+DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
+{
+ return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
+}
+
+
+/*
+ * ===========================================================================
* JNI implementation
* ===========================================================================
*/
JValue unused;
va_list args;
va_start(args, methodID);
- dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
+ dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
+ args);
va_end(args);
}
}
result = addLocalReference(env, newObj);
if (newObj != NULL) {
JValue unused;
- dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
+ dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
}
JNI_EXIT();
result = addLocalReference(env, newObj);
if (newObj != NULL) {
JValue unused;
- dvmCallMethodA(_self, (Method*) methodID, newObj, &unused, args);
+ dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
}
JNI_EXIT();
return _retfail; \
} \
va_start(args, methodID); \
- dvmCallMethodV(_self, meth, obj, &result, args); \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
va_end(args); \
- if (_isref) \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
JNI_EXIT(); \
return _retfail; \
} \
- dvmCallMethodV(_self, meth, obj, &result, args); \
- if (_isref) \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
JNI_EXIT(); \
return _retfail; \
} \
- dvmCallMethodA(_self, meth, obj, &result, args); \
- if (_isref) \
+ dvmCallMethodA(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
return _retfail; \
} \
va_start(args, methodID); \
- dvmCallMethodV(_self, meth, obj, &result, args); \
- if (_isref) \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
va_end(args); \
JNI_EXIT(); \
JNI_EXIT(); \
return _retfail; \
} \
- dvmCallMethodV(_self, meth, obj, &result, args); \
- if (_isref) \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
JNI_EXIT(); \
return _retfail; \
} \
- dvmCallMethodA(_self, meth, obj, &result, args); \
- if (_isref) \
+ dvmCallMethodA(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
JValue result; \
va_list args; \
va_start(args, methodID); \
- dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
+ dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
va_end(args); \
- if (_isref) \
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
UNUSED_PARAMETER(jclazz); \
JNI_ENTER(); \
JValue result; \
- dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
- if (_isref) \
+ dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
UNUSED_PARAMETER(jclazz); \
JNI_ENTER(); \
JValue result; \
- dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \
- if (_isref) \
+ dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
+ if (_isref && !dvmCheckException(_self)) \
result.l = addLocalReference(env, result.l); \
JNI_EXIT(); \
return _retok; \
JNI_ENTER();
StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
- ArrayObject* strChars = dvmStringCharArray(jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
pinPrimitiveArray(strChars);
{
JNI_ENTER();
StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
- ArrayObject* strChars = dvmStringCharArray(jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
unpinPrimitiveArray(strChars);
JNI_EXIT();
}
JValue callResult;
const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
- dvmCallMethodA(self, meth, bufObj, &callResult, NULL);
+ dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
if (dvmGetException(self) != NULL) {
dvmClearException(self);
callResult.l = NULL;
/*
* Pop the JNI local stack when we return from a native method. "saveArea"
* points to the StackSaveArea for the method we're leaving.
+ *
+ * (This may be implemented directly in assembly in mterp, so changes here
+ * may only affect the portable interpreter.)
*/
INLINE void dvmPopJniLocals(Thread* self, StackSaveArea* saveArea)
{
- if (saveArea->xtra.localRefTop != self->jniLocalRefTable.nextEntry) {
- LOGVV("LREF: popped %d entries (%d remain)\n",
- (int)(self->jniLocalRefTable.nextEntry-saveArea->xtra.localRefTop),
- (int)(saveArea->xtra.localRefTop - self->jniLocalRefTable.table));
- }
- self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefTop;
+#ifdef USE_INDIRECT_REF
+ self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie;
+#else
+ self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefCookie;
+#endif
}
/*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Reference table management.
*/
/*
* Returns NULL if not found.
*/
-Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top,
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
Object* obj)
{
Object** ptr;
ptr = pRef->nextEntry;
- while (--ptr >= top) {
+ while (--ptr >= bottom) {
if (*ptr == obj)
return ptr;
}
/*
* Remove "obj" from "pRef". We start at the end of the list (where the
* most-recently-added element is), and stop searching for a match after
- * examining the element at "top".
+ * examining the element at "bottom".
*
* Most of the time "obj" is at or near the end of the list. If not, we
* compact it down.
*/
-bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top,
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
Object* obj)
{
Object** ptr;
assert(pRef->table != NULL);
/*
- * Scan from the most-recently-added entry up to the top entry for
+ * Scan from the most-recently-added entry up to the bottom entry for
* this frame.
*/
- ptr = dvmFindInReferenceTable(pRef, top, obj);
+ ptr = dvmFindInReferenceTable(pRef, bottom, obj);
if (ptr == NULL)
return false;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Maintain a table of references. Used for internal local references,
* JNI locals, JNI globals, and GC heap references.
* table/nextEntry is allowed.)
*/
typedef struct ReferenceTable {
- Object** table; /* top of the list */
- Object** nextEntry; /* bottom of the list */
+ Object** nextEntry; /* top of the list */
+ Object** table; /* bottom of the list */
int allocEntries; /* #of entries we have space for */
int maxEntries; /* max #of entries allowed */
bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj);
/*
- * Determine if "obj" is present in "pRef". Stops searching when we hit "top".
- * To include the entire table, pass in "pRef->table" as the top.
+ * Determine if "obj" is present in "pRef". Stops searching when we hit
+ * "bottom". To include the entire table, pass in "pRef->table" as the
+ * bottom.
*
* Returns NULL if "obj" was not found.
*/
-Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top,
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
Object* obj);
/*
* Remove an existing entry.
*
- * We stop searching for a match after examining the element at "top". This
- * is useful when entries are associated with a stack frame.
+ * We stop searching for a match after examining the element at "bottom".
+ * This is useful when entries are associated with a stack frame.
*
* Returns "false" if the entry was not found.
*/
-bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top,
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
Object* obj);
/*
if (false) {
dvmLockMutex(&gDvm.jniGlobalRefLock);
- dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+ //dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
}
/*
* Initialize our reference tracking tables.
*
- * The JNI local ref table *must* be fixed-size because we keep pointers
- * into the table in our stack frames.
- *
* Most threads won't use jniMonitorRefTable, so we clear out the
* structure but don't call the init function (which allocs storage).
*/
+#ifdef USE_INDIRECT_REF
+ if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable,
+ kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal))
+ return false;
+#else
+ /*
+ * The JNI local ref table *must* be fixed-size because we keep pointers
+ * into the table in our stack frames.
+ */
if (!dvmInitReferenceTable(&thread->jniLocalRefTable,
kJniLocalRefMax, kJniLocalRefMax))
return false;
+#endif
if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
kInternalRefDefault, kInternalRefMax))
return false;
#endif
}
+#ifdef USE_INDIRECT_REF
+ dvmClearIndirectRefTable(&thread->jniLocalRefTable);
+#else
dvmClearReferenceTable(&thread->jniLocalRefTable);
+#endif
dvmClearReferenceTable(&thread->internalLocalRefTable);
if (&thread->jniMonitorRefTable.table != NULL)
dvmClearReferenceTable(&thread->jniMonitorRefTable);
}
}
+static void gcScanIndirectRefTable(IndirectRefTable* pRefTable)
+{
+ Object** op = pRefTable->table;
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ int i;
+
+ for (i = 0; i < numEntries; i++) {
+ Object* obj = *op;
+ if (obj != NULL)
+ dvmMarkObjectNonNull(obj);
+ op++;
+ }
+}
+
/*
* Scan a Thread and mark any objects it references.
*/
HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_LOCAL, thread->threadId);
+#ifdef USE_INDIRECT_REF
+ gcScanIndirectRefTable(&thread->jniLocalRefTable);
+#else
gcScanReferenceTable(&thread->jniLocalRefTable);
+#endif
if (thread->jniMonitorRefTable.table != NULL) {
HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_MONITOR, thread->threadId);
void dvmSlayDaemons(void);
+#define kJniLocalRefMin 32
#define kJniLocalRefMax 512 /* arbitrary; should be plenty */
#define kInternalRefDefault 32 /* equally arbitrary */
#define kInternalRefMax 4096 /* mainly a sanity check */
ReferenceTable internalLocalRefTable;
/* JNI local reference tracking */
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable jniLocalRefTable;
+#else
ReferenceTable jniLocalRefTable;
+#endif
/* JNI native monitor reference tracking (initialized on first use) */
ReferenceTable jniMonitorRefTable;
bxne lr @ bail to the interpreter
@ go ahead and transfer control to the native code
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
@ Prep for the native call
@ r1 = newFP, r0 = methodToCall
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
bxne lr @ bail to the interpreter
@ go ahead and transfer control to the native code
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
@ Prep for the native call
@ r1 = newFP, r0 = methodToCall
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
bxne lr @ bail to the interpreter
@ go ahead and transfer control to the native code
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
@ Prep for the native call
@ r1 = newFP, r0 = methodToCall
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
bxne lr @ bail to the interpreter
@ go ahead and transfer control to the native code
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
@ Prep for the native call
@ r1 = newFP, r0 = methodToCall
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)]
- @ newFp->localRefTop=refNext
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne .LhandleException @ no, handle exception
bx r2
breakSaveBlock->prevFrame = self->curFrame;
breakSaveBlock->savedPc = NULL; // not required
- breakSaveBlock->xtra.localRefTop = NULL; // not required
+ breakSaveBlock->xtra.localRefCookie = 0; // not required
breakSaveBlock->method = NULL;
saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
saveBlock->savedPc = NULL; // not required
breakSaveBlock->prevFrame = self->curFrame;
breakSaveBlock->savedPc = NULL; // not required
- breakSaveBlock->xtra.localRefTop = NULL; // not required
+ breakSaveBlock->xtra.localRefCookie = 0; // not required
breakSaveBlock->method = NULL;
saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
saveBlock->savedPc = NULL; // not required
- saveBlock->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
saveBlock->method = method;
LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)\n",
saveBlock->prevFrame = self->curFrame;
saveBlock->savedPc = NULL; // not required
- saveBlock->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
saveBlock->method = method;
LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)\n",
saveBlock->method->name,
(SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
"" : " (JNI local)");
- assert(saveBlock->xtra.localRefTop != NULL);
- assert(saveBlock->xtra.localRefTop >=self->jniLocalRefTable.table &&
- saveBlock->xtra.localRefTop <=self->jniLocalRefTable.nextEntry);
+ assert(saveBlock->xtra.localRefCookie != 0);
+ //assert(saveBlock->xtra.localRefCookie >= self->jniLocalRefTable.table &&
+ // saveBlock->xtra.localRefCookie <=self->jniLocalRefTable.nextEntry);
dvmPopJniLocals(self, saveBlock);
}
va_list args;
va_start(args, pResult);
- dvmCallMethodV(self, method, obj, pResult, args);
+ dvmCallMethodV(self, method, obj, false, pResult, args);
va_end(args);
}
* we don't need to worry about static synchronized methods.
*/
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
- JValue* pResult, va_list args)
+ bool fromJni, JValue* pResult, va_list args)
{
const char* desc = &(method->shorty[1]); // [0] is the return type.
int verifyCount = 0;
case 'L': { /* 'shorty' descr uses L for all refs, incl array */
void* argObj = va_arg(args, void*);
assert(obj == NULL || dvmIsValidObject(obj));
- *ins++ = (u4) dvmDecodeIndirectRef(env, argObj);
+ if (fromJni)
+ *ins++ = (u4) dvmDecodeIndirectRef(env, argObj);
+ else
+ *ins++ = (u4) argObj;
verifyCount++;
break;
}
* "args" may be NULL if the method has no arguments.
*/
void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
- JValue* pResult, const jvalue* args)
+ bool fromJni, JValue* pResult, const jvalue* args)
{
const char* desc = &(method->shorty[1]); // [0] is the return type.
int verifyCount = 0;
verifyCount++; /* this needs an extra push */
break;
case 'L': /* includes array refs */
- *ins++ = (u4) dvmDecodeIndirectRef(env, args->l);
+ if (fromJni)
+ *ins++ = (u4) dvmDecodeIndirectRef(env, args->l);
+ else
+ *ins++ = (u4) args->l;
break;
case 'F':
case 'I':
const Method* method;
union {
- /* for JNI native methods: top of local reference storage */
- Object** localRefTop;
+ /* for JNI native methods: bottom of local reference segment */
+#ifdef USE_INDIRECT_REF
+ u4 localRefCookie;
+#else
+ Object** localRefCookie;
+#endif
/* for interpreted methods: saved current PC, for exception stack
* traces and debugger traces */
bool dvmPopLocalFrame(Thread* thread);
/*
- * Call an interpreted method from native code.
+ * Call an interpreted method from native code. If this is being called
+ * from a JNI function, references in the argument list will be converted
+ * back to pointers.
*
* "obj" should be NULL for "direct" methods.
*/
-void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
- JValue* pResult, va_list args);
-void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
- JValue* pResult, const jvalue* args);
void dvmCallMethod(Thread* self, const Method* method, Object* obj,
JValue* pResult, ...);
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, va_list args);
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, const jvalue* args);
/*
* Invoke a method, using the specified arguments and return type, through
@ Prep for the native call
@ r0=methodToCall, r1=newFp, r10=newSaveArea
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r10, #offStackSaveArea_localRefTop] @newFp->localRefTop=refNext
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
mov r2, r0 @ r2<- methodToCall
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
- newSaveArea->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
self->curFrame = newFp;
MTERP_OFFSET(offStackSaveArea_savedPc, StackSaveArea, savedPc, 8)
MTERP_OFFSET(offStackSaveArea_method, StackSaveArea, method, 12)
MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 16)
-MTERP_OFFSET(offStackSaveArea_localRefTop, StackSaveArea, xtra.localRefTop, 16)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+ StackSaveArea, xtra.localRefCookie, 16)
MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 20)
MTERP_SIZEOF(sizeofStackSaveArea, StackSaveArea, 24)
#else
MTERP_OFFSET(offStackSaveArea_savedPc, StackSaveArea, savedPc, 4)
MTERP_OFFSET(offStackSaveArea_method, StackSaveArea, method, 8)
MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 12)
-MTERP_OFFSET(offStackSaveArea_localRefTop, StackSaveArea, xtra.localRefTop, 12)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+ StackSaveArea, xtra.localRefCookie, 12)
MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 16)
MTERP_SIZEOF(sizeofStackSaveArea, StackSaveArea, 20)
#endif
MTERP_OFFSET(offThread_stackOverflowed, Thread, stackOverflowed, 40)
MTERP_OFFSET(offThread_curFrame, Thread, curFrame, 44)
MTERP_OFFSET(offThread_exception, Thread, exception, 48)
-MTERP_OFFSET(offThread_jniLocal_nextEntry, \
- Thread, jniLocalRefTable.nextEntry, 80)
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.segmentState.all, 76)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.nextEntry, 76)
+#endif
/* Object fields */
MTERP_OFFSET(offObject_clazz, Object, clazz, 0)
@ Prep for the native call
@ r0=methodToCall, r1=newFp, r10=newSaveArea
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r10, #offStackSaveArea_localRefTop] @newFp->localRefTop=refNext
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
mov r2, r0 @ r2<- methodToCall
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
@ Prep for the native call
@ r0=methodToCall, r1=newFp, r10=newSaveArea
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r10, #offStackSaveArea_localRefTop] @newFp->localRefTop=refNext
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
mov r2, r0 @ r2<- methodToCall
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
@ Prep for the native call
@ r0=methodToCall, r1=newFp, r10=newSaveArea
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r10, #offStackSaveArea_localRefTop] @newFp->localRefTop=refNext
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
mov r2, r0 @ r2<- methodToCall
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
@ Prep for the native call
@ r0=methodToCall, r1=newFp, r10=newSaveArea
ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
- ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
- str r9, [r10, #offStackSaveArea_localRefTop] @newFp->localRefTop=refNext
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
mov r9, r3 @ r9<- glue->self (preserve)
mov r2, r0 @ r2<- methodToCall
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
- ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
ldr r1, [r9, #offThread_exception] @ check for exception
str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
cmp r1, #0 @ null?
- str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
bne common_exceptionThrown @ no, handle exception
FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
GET_GLUE(%ecx) # %ecx<- pMterpGlue
movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
- movl offThread_jniLocal_nextEntry(%ecx), %eax # %eax<- glue->self->thread->refNext
- movl %eax, offStackSaveArea_localRefTop(%edx) # newSaveArea->localRefTop<- refNext
+ movl offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+ movl %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
movl %edx, OUT_ARG4(%esp) # save newSaveArea
movl LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
movl %edx, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
lea 4(%esp), %esp
movl OUT_ARG4(%esp), %ecx # %ecx<- newSaveArea
movl OUT_ARG3(%esp), %eax # %eax<- glue->self
- movl offStackSaveArea_localRefTop(%ecx), %edx # %edx<- newSaveArea->localRefTop
+ movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
cmp $0, offThread_exception(%eax) # check for exception
movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
- movl %edx, offThread_jniLocal_nextEntry(%eax) # glue->self<- newSaveArea->localRefTop
+ movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
UNSPILL(rPC)
jne common_exceptionThrown # handle exception
FETCH_INST_WORD(3)
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
- newSaveArea->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
self->curFrame = newFp;
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
- newSaveArea->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
self->curFrame = newFp;
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
- newSaveArea->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
self->curFrame = newFp;
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
- newSaveArea->xtra.localRefTop = self->jniLocalRefTable.nextEntry;
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
self->curFrame = newFp;
GET_GLUE(%ecx) # %ecx<- pMterpGlue
movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
- movl offThread_jniLocal_nextEntry(%ecx), %eax # %eax<- glue->self->thread->refNext
- movl %eax, offStackSaveArea_localRefTop(%edx) # newSaveArea->localRefTop<- refNext
+ movl offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+ movl %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
movl %edx, OUT_ARG4(%esp) # save newSaveArea
movl LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
movl %edx, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
lea 4(%esp), %esp
movl OUT_ARG4(%esp), %ecx # %ecx<- newSaveArea
movl OUT_ARG3(%esp), %eax # %eax<- glue->self
- movl offStackSaveArea_localRefTop(%ecx), %edx # %edx<- newSaveArea->localRefTop
+ movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
cmp $$0, offThread_exception(%eax) # check for exception
movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
- movl %edx, offThread_jniLocal_nextEntry(%eax) # glue->self<- newSaveArea->localRefTop
+ movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
UNSPILL(rPC)
jne common_exceptionThrown # handle exception
FETCH_INST_WORD(3)