From d5ab726b65d7271be261864c7e224fb90bfe06e0 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 25 Aug 2009 07:19:34 -0700 Subject: [PATCH] Another round of scary indirect ref changes. This change adds a not-really-working implementation to Jni.c, with various changes #ifdefed throughout the code. The ifdef is currently disabled, so the old behavior should continue. Eventually the old version will be stripped out and the ifdefs removed. This renames the stack's "localRefTop" field, which nudged a bunch of code. The name wasn't really right before (it's the *bottom* of the local references), and it's even less right now. This and one other mterp-visible constant were changed, which caused some ripples through mterp and the JIT, but the ifdeffing was limited to one in asm-constants.h (and the constant is the same both ways, so toggling the ifdef won't require rebuilding asm sources). Some comments and arg names in ReferenceTable were updated for the correct orientation of bottom vs. top. Some adjustments were made to the JNI code, e.g. dvmCallMethod now needs to understand if it needs to convert reference arguments from local/global refs to pointers (it's called from various places throughout the VM). --- vm/CheckJni.c | 5 +- vm/Debugger.c | 2 +- vm/Dvm.mk | 1 + vm/Globals.h | 5 + vm/Jni.c | 809 +++++++++++++-------- vm/JniInternal.h | 14 +- vm/ReferenceTable.c | 13 +- vm/ReferenceTable.h | 18 +- vm/SignalCatcher.c | 2 +- vm/Thread.c | 35 +- vm/Thread.h | 5 + .../armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S | 10 +- vm/compiler/template/armv5te/footer.S | 10 +- .../template/out/CompilerTemplateAsm-armv5te-vfp.S | 20 +- .../template/out/CompilerTemplateAsm-armv5te.S | 20 +- .../template/out/CompilerTemplateAsm-armv7-a.S | 20 +- vm/interp/Stack.c | 38 +- vm/interp/Stack.h | 20 +- vm/mterp/armv5te/footer.S | 8 +- vm/mterp/c/gotoTargets.c | 6 +- vm/mterp/common/asm-constants.h | 15 +- vm/mterp/out/InterpAsm-armv4t.S | 8 +- vm/mterp/out/InterpAsm-armv5te-vfp.S | 8 +- vm/mterp/out/InterpAsm-armv5te.S | 8 +- vm/mterp/out/InterpAsm-armv7-a.S | 8 +- vm/mterp/out/InterpAsm-x86.S | 8 +- vm/mterp/out/InterpC-allstubs.c | 6 +- vm/mterp/out/InterpC-portdbg.c | 6 +- vm/mterp/out/InterpC-portstd.c | 6 +- vm/mterp/out/InterpC-x86.c | 6 +- vm/mterp/x86/footer.S | 8 +- 31 files changed, 721 insertions(+), 427 deletions(-) diff --git a/vm/CheckJni.c b/vm/CheckJni.c index 31c35c323..a8bb1138a 100644 --- a/vm/CheckJni.c +++ b/vm/CheckJni.c @@ -1735,6 +1735,7 @@ static const jchar* Check_GetStringChars(JNIEnv* env, jstring string, 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) @@ -1790,6 +1791,7 @@ static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string, 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) @@ -2056,6 +2058,7 @@ static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string, 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) @@ -2153,7 +2156,7 @@ static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf) * 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; } diff --git a/vm/Debugger.c b/vm/Debugger.c index 3affa976f..4ddf25c4e 100644 --- a/vm/Debugger.c +++ b/vm/Debugger.c @@ -2814,7 +2814,7 @@ void dvmDbgExecuteMethod(DebugInvokeReq* pReq) 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); diff --git a/vm/Dvm.mk b/vm/Dvm.mk index 7aab98df7..66ada15bd 100644 --- a/vm/Dvm.mk +++ b/vm/Dvm.mk @@ -24,6 +24,7 @@ # 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. diff --git a/vm/Globals.h b/vm/Globals.h index a1a1b830f..e1a91de07 100644 --- a/vm/Globals.h +++ b/vm/Globals.h @@ -194,6 +194,7 @@ struct DvmGlobals { ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember; ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray; ClassObject* classOrgApacheHarmonyNioInternalDirectBuffer; + jclass jclassOrgApacheHarmonyNioInternalDirectBuffer; /* synthetic classes for arrays of primitives */ ClassObject* classArrayBoolean; @@ -409,7 +410,11 @@ struct DvmGlobals { /* * JNI global reference table. */ +#ifdef USE_INDIRECT_REF + IndirectRefTable jniGlobalRefTable; +#else ReferenceTable jniGlobalRefTable; +#endif pthread_mutex_t jniGlobalRefLock; int jniGlobalRefHiMark; int jniGlobalRefLoMark; diff --git a/vm/Jni.c b/vm/Jni.c index abd520a9b..f93a3329f 100644 --- a/vm/Jni.c +++ b/vm/Jni.c @@ -51,7 +51,9 @@ General notes on local/global reference tracking 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 @@ -71,36 +73,52 @@ with a simple store/increment operation; to avoid infinite growth in 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 @@ -173,14 +191,14 @@ all references. An exact GC will need to understand the actual layout. *** 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 @@ -218,15 +236,13 @@ Compared to #1, approach #2: /* 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) @@ -235,217 +251,6 @@ static void checkStackSum(Thread* self); /* * =========================================================================== - * 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 * =========================================================================== */ @@ -492,9 +297,16 @@ DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) */ 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; @@ -530,10 +342,11 @@ bool dvmJniStartup(void) 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 @@ -605,7 +418,11 @@ bool dvmJniStartup(void) */ void dvmJniShutdown(void) { +#ifdef USE_INDIRECT_REF + dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable); +#else dvmClearReferenceTable(&gDvm.jniGlobalRefTable); +#endif } @@ -714,11 +531,69 @@ void dvmDestroyJNIEnv(JNIEnv* env) * 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 @@ -737,6 +612,27 @@ static jobject addLocalReference(JNIEnv* env, Object* obj) 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)) { @@ -752,20 +648,10 @@ static jobject addLocalReference(JNIEnv* env, Object* 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; } /* @@ -774,9 +660,16 @@ Object* dvmDecodeIndirectRef(JNIEnv* env, jobject 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 } /* @@ -787,11 +680,27 @@ static void deleteLocalReference(JNIEnv* env, jobject jobj) 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 @@ -802,6 +711,7 @@ static void deleteLocalReference(JNIEnv* env, jobject jobj) LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n", jobj, dvmIsValidObject((Object*) jobj)); } +#endif } /* @@ -837,37 +747,73 @@ static jobject addGlobalReference(Object* obj) } 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, @@ -894,10 +840,11 @@ static jobject addGlobalReference(Object* obj) } } } +#endif bail: dvmUnlockMutex(&gDvm.jniGlobalRefLock); - return (jobject) obj; + return jobj; } /* @@ -914,6 +861,24 @@ static void deleteGlobalReference(jobject 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)) { @@ -930,6 +895,7 @@ static void deleteGlobalReference(jobject jobj) gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; } } +#endif bail: dvmUnlockMutex(&gDvm.jniGlobalRefLock); @@ -1010,14 +976,28 @@ bail: */ 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); @@ -1124,6 +1104,13 @@ static bool findInArgList(Thread* self, Object* obj) */ 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; @@ -1138,7 +1125,6 @@ jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) #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; @@ -1157,6 +1143,7 @@ jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) /* not found! */ return JNIInvalidRefType; +#endif } /* @@ -1421,6 +1408,217 @@ static void checkStackSum(Thread* self) /* * =========================================================================== + * 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 * =========================================================================== */ @@ -1885,7 +2083,8 @@ static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) 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); } } @@ -1904,7 +2103,7 @@ static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, 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(); @@ -1921,7 +2120,7 @@ static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, 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(); @@ -2256,9 +2455,9 @@ SET_TYPE_FIELD(jdouble, Double, false); 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; \ @@ -2275,8 +2474,8 @@ SET_TYPE_FIELD(jdouble, Double, false); 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; \ @@ -2293,8 +2492,8 @@ SET_TYPE_FIELD(jdouble, Double, false); 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; \ @@ -2334,8 +2533,8 @@ CALL_VIRTUAL(void, Void, , , false); 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(); \ @@ -2355,8 +2554,8 @@ CALL_VIRTUAL(void, Void, , , false); 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; \ @@ -2375,8 +2574,8 @@ CALL_VIRTUAL(void, Void, , , false); 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; \ @@ -2405,9 +2604,9 @@ CALL_NONVIRTUAL(void, Void, , , false); 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; \ @@ -2418,8 +2617,8 @@ CALL_NONVIRTUAL(void, Void, , , false); 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; \ @@ -2430,8 +2629,8 @@ CALL_NONVIRTUAL(void, Void, , , false); 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; \ @@ -2496,7 +2695,7 @@ static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) JNI_ENTER(); StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); - ArrayObject* strChars = dvmStringCharArray(jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); pinPrimitiveArray(strChars); @@ -2515,7 +2714,7 @@ static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) { JNI_ENTER(); StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); - ArrayObject* strChars = dvmStringCharArray(jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); unpinPrimitiveArray(strChars); JNI_EXIT(); } @@ -3220,7 +3419,7 @@ static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) 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; diff --git a/vm/JniInternal.h b/vm/JniInternal.h index f39583e8f..4620c5009 100644 --- a/vm/JniInternal.h +++ b/vm/JniInternal.h @@ -98,15 +98,17 @@ typedef enum DalvikJniReturnType { /* * 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 } /* diff --git a/vm/ReferenceTable.c b/vm/ReferenceTable.c index 089f2f287..a8010a251 100644 --- a/vm/ReferenceTable.c +++ b/vm/ReferenceTable.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Reference table management. */ @@ -96,13 +97,13 @@ bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj) /* * 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; } @@ -112,12 +113,12 @@ Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top, /* * 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; @@ -125,10 +126,10 @@ bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top, 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; diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h index f8f24610e..d6e2d705a 100644 --- a/vm/ReferenceTable.h +++ b/vm/ReferenceTable.h @@ -13,6 +13,7 @@ * 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. @@ -36,8 +37,8 @@ * 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 */ @@ -88,23 +89,24 @@ INLINE size_t dvmIsReferenceTableFull(const ReferenceTable* pRef) 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); /* diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c index 309ad5df5..e187aeb3c 100644 --- a/vm/SignalCatcher.c +++ b/vm/SignalCatcher.c @@ -246,7 +246,7 @@ loop: if (false) { dvmLockMutex(&gDvm.jniGlobalRefLock); - dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); + //dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); dvmUnlockMutex(&gDvm.jniGlobalRefLock); } diff --git a/vm/Thread.c b/vm/Thread.c index 3f63132ee..6a1e9aab8 100644 --- a/vm/Thread.c +++ b/vm/Thread.c @@ -980,15 +980,22 @@ static bool prepareThread(Thread* thread) /* * 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; @@ -1042,7 +1049,11 @@ static void freeThread(Thread* thread) #endif } +#ifdef USE_INDIRECT_REF + dvmClearIndirectRefTable(&thread->jniLocalRefTable); +#else dvmClearReferenceTable(&thread->jniLocalRefTable); +#endif dvmClearReferenceTable(&thread->internalLocalRefTable); if (&thread->jniMonitorRefTable.table != NULL) dvmClearReferenceTable(&thread->jniMonitorRefTable); @@ -3742,6 +3753,20 @@ static void gcScanReferenceTable(ReferenceTable *refTable) } } +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. */ @@ -3772,7 +3797,11 @@ static void gcScanThread(Thread *thread) 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); diff --git a/vm/Thread.h b/vm/Thread.h index 1725b1edb..50bf5a049 100644 --- a/vm/Thread.h +++ b/vm/Thread.h @@ -68,6 +68,7 @@ void dvmThreadShutdown(void); 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 */ @@ -152,7 +153,11 @@ typedef struct Thread { 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; diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S index 5e5999191..b20d56409 100644 --- a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S +++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S @@ -25,10 +25,10 @@ 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 @@ -41,11 +41,11 @@ @ 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 diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S index 29073c37d..3b0b14928 100644 --- a/vm/compiler/template/armv5te/footer.S +++ b/vm/compiler/template/armv5te/footer.S @@ -10,10 +10,10 @@ @ 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 @@ -26,11 +26,11 @@ @ 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 diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S index 2c669f8e0..0c433a11a 100644 --- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S +++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S @@ -394,10 +394,10 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: 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 @@ -410,11 +410,11 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: @ 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 @@ -1012,10 +1012,10 @@ dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP: @ 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 @@ -1028,11 +1028,11 @@ dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP: @ 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 diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S index b97369118..29dde7422 100644 --- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S +++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S @@ -394,10 +394,10 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: 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 @@ -410,11 +410,11 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: @ 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 @@ -746,10 +746,10 @@ dvmCompiler_TEMPLATE_USHR_LONG: @ 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 @@ -762,11 +762,11 @@ dvmCompiler_TEMPLATE_USHR_LONG: @ 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 diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S index 1faf7f649..9e97b7413 100644 --- a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S +++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S @@ -394,10 +394,10 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: 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 @@ -410,11 +410,11 @@ dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE: @ 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 @@ -1012,10 +1012,10 @@ dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP: @ 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 @@ -1028,11 +1028,11 @@ dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP: @ 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 diff --git a/vm/interp/Stack.c b/vm/interp/Stack.c index 838147fe1..5920cbc3a 100644 --- a/vm/interp/Stack.c +++ b/vm/interp/Stack.c @@ -105,7 +105,7 @@ static bool dvmPushInterpFrame(Thread* self, const Method* method) 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 @@ -182,11 +182,15 @@ bool dvmPushJNIFrame(Thread* self, const Method* method) 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", @@ -245,7 +249,11 @@ bool dvmPushLocalFrame(Thread* self, const Method* method) 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", @@ -320,9 +328,9 @@ static bool dvmPopFrame(Thread* self) 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); } @@ -424,7 +432,7 @@ void dvmCallMethod(Thread* self, const Method* method, Object* obj, va_list args; va_start(args, pResult); - dvmCallMethodV(self, method, obj, pResult, args); + dvmCallMethodV(self, method, obj, false, pResult, args); va_end(args); } @@ -438,7 +446,7 @@ void dvmCallMethod(Thread* self, const Method* method, Object* obj, * 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; @@ -483,7 +491,10 @@ void dvmCallMethodV(Thread* self, const Method* method, Object* obj, 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; } @@ -541,7 +552,7 @@ bail: * "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; @@ -572,7 +583,10 @@ void dvmCallMethodA(Thread* self, const Method* method, Object* obj, 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': diff --git a/vm/interp/Stack.h b/vm/interp/Stack.h index f2a481b29..22f066fb0 100644 --- a/vm/interp/Stack.h +++ b/vm/interp/Stack.h @@ -138,8 +138,12 @@ struct StackSaveArea { 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 */ @@ -191,16 +195,18 @@ bool dvmPushLocalFrame(Thread* thread, const Method* method); 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 diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S index 004ee136b..90e37503b 100644 --- a/vm/mterp/armv5te/footer.S +++ b/vm/mterp/armv5te/footer.S @@ -430,9 +430,9 @@ common_invokeMethodNoRange: @ 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 @@ -456,11 +456,11 @@ dalvik_mterp: @ 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 diff --git a/vm/mterp/c/gotoTargets.c b/vm/mterp/c/gotoTargets.c index 5997c3552..5b9358308 100644 --- a/vm/mterp/c/gotoTargets.c +++ b/vm/mterp/c/gotoTargets.c @@ -862,7 +862,11 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, 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; diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h index 716df3dbb..a69247eb8 100644 --- a/vm/mterp/common/asm-constants.h +++ b/vm/mterp/common/asm-constants.h @@ -154,7 +154,8 @@ MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 4) 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 @@ -162,7 +163,8 @@ MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 0) 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 @@ -198,8 +200,13 @@ MTERP_OFFSET(offInlineOperation_func, InlineOperation, func, 0) 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) diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S index b0aa69b15..2df532fc9 100644 --- a/vm/mterp/out/InterpAsm-armv4t.S +++ b/vm/mterp/out/InterpAsm-armv4t.S @@ -9902,9 +9902,9 @@ common_invokeMethodNoRange: @ 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 @@ -9928,11 +9928,11 @@ dalvik_mterp: @ 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 diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S index 5a8ae4c38..61b26971f 100644 --- a/vm/mterp/out/InterpAsm-armv5te-vfp.S +++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S @@ -9420,9 +9420,9 @@ common_invokeMethodNoRange: @ 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 @@ -9446,11 +9446,11 @@ dalvik_mterp: @ 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 diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index 3f2069cea..bafd44242 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -9896,9 +9896,9 @@ common_invokeMethodNoRange: @ 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 @@ -9922,11 +9922,11 @@ dalvik_mterp: @ 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 diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S index 0555a3074..8d018c118 100644 --- a/vm/mterp/out/InterpAsm-armv7-a.S +++ b/vm/mterp/out/InterpAsm-armv7-a.S @@ -9356,9 +9356,9 @@ common_invokeMethodNoRange: @ 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 @@ -9382,11 +9382,11 @@ dalvik_mterp: @ 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 diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S index 52007b0bd..79c98f696 100644 --- a/vm/mterp/out/InterpAsm-x86.S +++ b/vm/mterp/out/InterpAsm-x86.S @@ -8700,8 +8700,8 @@ common_invokeMethodNoRange: 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 @@ -8717,10 +8717,10 @@ common_invokeMethodNoRange: 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) diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c index bb1d0ea65..4e832d828 100644 --- a/vm/mterp/out/InterpC-allstubs.c +++ b/vm/mterp/out/InterpC-allstubs.c @@ -3895,7 +3895,11 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, 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; diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c index 90b2a75c6..4b9263977 100644 --- a/vm/mterp/out/InterpC-portdbg.c +++ b/vm/mterp/out/InterpC-portdbg.c @@ -4186,7 +4186,11 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, 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; diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c index c28bf5b55..1db6e879d 100644 --- a/vm/mterp/out/InterpC-portstd.c +++ b/vm/mterp/out/InterpC-portstd.c @@ -3900,7 +3900,11 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, 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; diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c index 9bb70d196..07536c4b1 100644 --- a/vm/mterp/out/InterpC-x86.c +++ b/vm/mterp/out/InterpC-x86.c @@ -2050,7 +2050,11 @@ GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, 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; diff --git a/vm/mterp/x86/footer.S b/vm/mterp/x86/footer.S index d86207a39..c39fa1645 100644 --- a/vm/mterp/x86/footer.S +++ b/vm/mterp/x86/footer.S @@ -200,8 +200,8 @@ common_invokeMethodNoRange: 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 @@ -217,10 +217,10 @@ common_invokeMethodNoRange: 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) -- 2.11.0