static void threadExitUncaughtException(Env* env, Thread* thread) { Object* throwable = rvmExceptionClear(env); Object* handler = rvmCallObjectInstanceMethod(env, (Object*) thread->threadObj, getUncaughtExceptionHandlerMethod); // Ignore exception thrown by getUncaughtException() rvmExceptionClear(env); if (!handler) { handler = thread->threadObj->group; } if (handler) { rvmCallVoidInstanceMethod(env, handler, uncaughtExceptionMethod, (Object*) thread->threadObj, throwable); } else { rvmPrintStackTrace(env, throwable); } // Ignore exception thrown by uncaughtException() or rvmPrintStackTrace() rvmExceptionClear(env); }
void* lookupInterfaceMethod(Env* env, ClassInfoHeader* header, Object* thiz, char* name, char* desc) { initializeClass(env, header); if (rvmExceptionCheck(env)) return NULL; Class* ownerInterface = header->clazz; if (!rvmIsInstanceOf(env, thiz, ownerInterface)) { char message[256]; snprintf(message, 256, "Class %s does not implement the requested interface %s", rvmToBinaryClassName(env, thiz->clazz->name), rvmToBinaryClassName(env, ownerInterface->name)); rvmThrowIncompatibleClassChangeError(env, message); return NULL; } Method* method = rvmGetInstanceMethod(env, thiz->clazz, name, desc); Object* throwable = rvmExceptionClear(env); if (!method && throwable->clazz != java_lang_NoSuchMethodError) { rvmThrow(env, throwable); return NULL; } if (!method || METHOD_IS_ABSTRACT(method)) { rvmThrowAbstractMethodError(env, ""); // TODO: Message return NULL; } if (!METHOD_IS_PUBLIC(method)) { rvmThrowIllegalAccessError(env, ""); // TODO: Message return NULL; } return method->synchronizedImpl ? method->synchronizedImpl : method->impl; }
static void wrapClassNotFoundException(Env* env, const char* className) { Object* exception = rvmExceptionOccurred(env); if (exception && exception->clazz == java_lang_ClassNotFoundException) { // If ClassNotFoundException is thrown we have to wrap it in a NoClassDefFoundError exception = rvmExceptionClear(env); Method* constructor = rvmGetInstanceMethod(env, java_lang_NoClassDefFoundError, "<init>", "(Ljava/lang/String;)V"); if (!constructor) return; Object* message = rvmNewStringUTF(env, className, -1); if (!message) return; Object* wrappedException = rvmNewObject(env, java_lang_NoClassDefFoundError, constructor, message); if (!wrappedException) return; Class* java_lang_StackTraceElement = rvmFindClassUsingLoader(env, "java/lang/StackTraceElement", NULL); if (!java_lang_StackTraceElement) return; ObjectArray* stackTrace = rvmNewObjectArray(env, 0, java_lang_StackTraceElement, NULL, NULL); if (!stackTrace) return; Method* setStackTrace = rvmGetInstanceMethod(env, java_lang_Throwable, "setStackTrace", "([Ljava/lang/StackTraceElement;)V"); if (!setStackTrace) return; rvmCallVoidInstanceMethod(env, wrappedException, setStackTrace, stackTrace); if (rvmExceptionCheck(env)) return; Method* initCause = rvmGetInstanceMethod(env, java_lang_NoClassDefFoundError, "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); if (!initCause) return; rvmCallObjectInstanceMethod(env, wrappedException, initCause, exception); if (!rvmExceptionCheck(env)) rvmThrow(env, wrappedException); } }
Class* Java_java_lang_Class_getEnclosingClass(Env* env, Class* thiz) { Class* enclosingClass = rvmAttributeGetEnclosingClass(env, thiz); if (rvmExceptionCheck(env) && rvmExceptionOccurred(env)->clazz != java_lang_ClassNotFoundException) { return NULL; } if (!enclosingClass) { rvmExceptionClear(env); return rvmAttributeGetDeclaringClass(env, thiz); } return enclosingClass; }
static Class* findType(Env* env, const char* classDesc, Object* loader) { Class* c = rvmFindClassByDescriptor(env, classDesc, loader); if (!c) { if (rvmExceptionOccurred(env)->clazz == java_lang_ClassNotFoundException) { rvmExceptionClear(env); char* className = rvmCopyMemoryAtomicZ(env, classDesc); className[strlen(className)] = 0; rvmThrowNew(env, java_lang_TypeNotPresentException, rvmFromBinaryClassName(env, &className[1])); } } return c; }
void _bcHookInstrumented(DebugEnv* debugEnv, jint lineNumber, jint lineNumberOffset, jbyte* bptable, void* pc) { Env* env = (Env*) debugEnv; // Temporarily clear any exception that has been thrown. Could be the case if we are called from a catch block. Object* throwable = rvmExceptionClear(env); // We cannot use ENTER/LEAVEV as it will raise any exception thrown in here. We just want the code to // proceed normally after we return. rvmPushGatewayFrame(env); rvmHookInstrumented(debugEnv, lineNumber, lineNumberOffset, bptable, pc); rvmPopGatewayFrame(env); // Restore the exception if one had been thrown when this function was called. env->throwable = throwable; }
jboolean rvmUnboxDouble(Env* env, Object* arg, jvalue* value) { if (!arg) { rvmThrowNullPointerException(env); return FALSE; } if (arg->clazz != java_lang_Double) { if (rvmUnboxLong(env, arg, value)) { value->d = value->j; return TRUE; } else { rvmExceptionClear(env); } if (rvmUnboxFloat(env, arg, value)) { value->d = value->f; return TRUE; } else { rvmExceptionClear(env); } rvmThrowClassCastException(env, java_lang_Double, arg->clazz); return FALSE; } value->d = ((Double*) arg)->value; return TRUE; }
void throwInvocationTargetException(Env* env, Object* throwable) { rvmExceptionClear(env); if (!java_lang_reflect_InvocationTargetException) { java_lang_reflect_InvocationTargetException = rvmFindClassUsingLoader(env, "java/lang/reflect/InvocationTargetException", NULL); if (!java_lang_reflect_InvocationTargetException) return; } if (!java_lang_reflect_InvocationTargetException_init) { java_lang_reflect_InvocationTargetException_init = rvmGetMethod(env, java_lang_reflect_InvocationTargetException, "<init>", "(Ljava/lang/Throwable;)V"); if (!java_lang_reflect_InvocationTargetException_init) return; } jvalue initArgs[1]; initArgs[0].l = (jobject) throwable; Object* exception = rvmNewObjectA(env, java_lang_reflect_InvocationTargetException, java_lang_reflect_InvocationTargetException_init, initArgs); if (!exception) return; rvmThrow(env, exception); }
jvalue* validateAndUnwrapArgs(Env* env, ObjectArray* parameterTypes, ObjectArray* args) { jint length = args->length; jvalue* jvalueArgs = length > 0 ? (jvalue*) rvmAllocateMemory(env, sizeof(jvalue) * length) : emptyJValueArgs; if (!jvalueArgs) return NULL; jint i; for (i = 0; i < length; i++) { Object* arg = args->values[i]; Class* type = (Class*) parameterTypes->values[i]; if (CLASS_IS_PRIMITIVE(type)) { if (arg == NULL) { const char* typeName = rvmGetHumanReadableClassName(env, type); if (typeName) { rvmThrowNewf(env, java_lang_IllegalArgumentException, "argument %d should have type %s, got null", i + 1, typeName); } return NULL; } if (!rvmUnbox(env, arg, type, &jvalueArgs[i])) { if (rvmExceptionOccurred(env)->clazz == java_lang_ClassCastException) { rvmExceptionClear(env); const char* argTypeName = rvmGetHumanReadableClassName(env, arg->clazz); const char* typeName = argTypeName ? rvmGetHumanReadableClassName(env, type) : NULL; if (argTypeName && typeName) { rvmThrowNewf(env, java_lang_IllegalArgumentException, "argument %d should have type %s, got %s", i + 1, typeName, argTypeName); } } return NULL; } } else { if (arg && !rvmIsInstanceOf(env, arg, type)) { const char* argTypeName = rvmGetHumanReadableClassName(env, arg->clazz); const char* typeName = argTypeName ? rvmGetHumanReadableClassName(env, type) : NULL; if (argTypeName && typeName) { rvmThrowNewf(env, java_lang_IllegalArgumentException, "argument %d should have type %s, got %s", i + 1, typeName, argTypeName); } return NULL; } jvalueArgs[i].l = (jobject) arg; } } return jvalueArgs; }
jboolean rvmUnboxInt(Env* env, Object* arg, jvalue* value) { if (!arg) { rvmThrowNullPointerException(env); return FALSE; } if (arg->clazz != java_lang_Integer) { if (rvmUnboxShort(env, arg, value)) { value->i = value->s; return TRUE; } else { rvmExceptionClear(env); } rvmThrowClassCastException(env, java_lang_Integer, arg->clazz); return FALSE; } value->i = ((Integer*) arg)->value; return TRUE; }
jboolean rvmUnboxLong(Env* env, Object* arg, jvalue* value) { if (!arg) { rvmThrowNullPointerException(env); return FALSE; } if (arg->clazz != java_lang_Long) { if (rvmUnboxInt(env, arg, value)) { value->j = value->i; return TRUE; } else { rvmExceptionClear(env); } rvmThrowClassCastException(env, java_lang_Long, arg->clazz); return FALSE; } value->j = ((Long*) arg)->value; return TRUE; }
static void signalHandler_npe_so(int signum, siginfo_t* info, void* context) { // rvmGetEnv() uses pthread_getspecific() which isn't listed as // async-signal-safe. Others (e.g. mono) do this too so we assume it is safe // in practice. Env* env = rvmGetEnv(); if (env && rvmIsNonNativeFrame(env)) { // We now know the fault occurred in non-native code and not in our // native code or in any non-async-signal-safe system function. It // should be safe to do things here that would normally be unsafe to do // in a signal handler. void* faultAddr = info->si_addr; void* stackAddr = env->currentThread->stackAddr; Class* exClass = NULL; if (faultAddr < stackAddr && faultAddr >= (void*) (stackAddr - THREAD_STACK_GUARD_SIZE)) { // StackOverflowError exClass = java_lang_StackOverflowError; } else { // At least on Linux x86 it seems like si_addr isn't always 0x0 even // if a read of address 0x0 triggered SIGSEGV so we assume // everything that isn't a stack overflow is a read of address 0x0 // and throw NullPointerException. exClass = java_lang_NullPointerException; } if (exClass) { Object* throwable = rvmAllocateObject(env, exClass); if (!throwable) { throwable = rvmExceptionClear(env); } Frame fakeFrame; fakeFrame.prev = (Frame*) getFramePointer((ucontext_t*) context); fakeFrame.returnAddress = getPC((ucontext_t*) context); CallStack* callStack = captureCallStackFromFrame(env, &fakeFrame); rvmSetLongInstanceFieldValue(env, throwable, stackStateField, PTR_TO_LONG(callStack)); rvmRaiseException(env, throwable); } } struct sigaction sa; sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(signum, &sa, NULL); kill(0, signum); }
static void signalHandler_npe_so(int signum, siginfo_t* info, void* context) { // SIGSEGV/SIGBUS are synchronous signals so we shouldn't have to worry about only calling // async-signal-safe functions here. Env* env = rvmGetEnv(); if (env && rvmIsNonNativeFrame(env)) { // We now know the fault occurred in non-native code. void* faultAddr = info->si_addr; void* stackAddr = env->currentThread->stackAddr; Class* exClass = NULL; if (faultAddr < stackAddr && faultAddr >= (void*) (stackAddr - THREAD_STACK_GUARD_SIZE)) { // StackOverflowError exClass = java_lang_StackOverflowError; } else { // At least on Linux x86 it seems like si_addr isn't always 0x0 even // if a read of address 0x0 triggered SIGSEGV so we assume // everything that isn't a stack overflow is a read of address 0x0 // and throw NullPointerException. exClass = java_lang_NullPointerException; } if (exClass) { Frame fakeFrame; fakeFrame.prev = (Frame*) getFramePointer((ucontext_t*) context); fakeFrame.returnAddress = getPC((ucontext_t*) context); Object* throwable = NULL; CallStack* callStack = captureCallStackFromFrame(env, &fakeFrame); if (callStack) { throwable = rvmAllocateObject(env, exClass); if (throwable) { rvmCallVoidClassMethod(env, exClass, throwableInitMethod, throwable, PTR_TO_LONG(callStack)); if (rvmExceptionCheck(env)) { throwable = NULL; } } } if (!throwable) { throwable = rvmExceptionClear(env); } rvmRaiseException(env, throwable); // Never returns! } } }
static jint detachThread(Env* env, jboolean ignoreAttachCount) { env->attachCount--; if (!ignoreAttachCount && env->attachCount > 0) { return JNI_OK; } if (env->gatewayFrames) { rvmAbort("Cannot detach thread when there are non native frames on the call stack"); } // TODO: Release all monitors still held by this thread (should only be monitors acquired from JNI code) Thread* thread = env->currentThread; if (rvmExceptionOccurred(env)) { threadExitUncaughtException(env, thread); } if (thread->threadObj->group) { rvmCallVoidInstanceMethod(env, thread->threadObj->group, removeThreadMethod, thread->threadObj); rvmExceptionClear(env); } // Set threadPtr to null rvmAtomicStoreLong(&thread->threadObj->threadPtr, 0); // Notify anyone waiting on this thread (using Thread.join()) rvmLockObject(env, thread->threadObj->lock); rvmObjectNotifyAll(env, thread->threadObj->lock); rvmUnlockObject(env, thread->threadObj->lock); rvmLockThreadsList(); thread->status = THREAD_ZOMBIE; DL_DELETE(threads, thread); pthread_cond_broadcast(&threadsChangedCond); rvmTearDownSignals(env); env->currentThread = NULL; pthread_setspecific(tlsEnvKey, NULL); rvmUnlockThreadsList(); return JNI_OK; }
static jboolean getAnnotationValue(Env* env, void** attributes, Class* expectedAnnotationClass, Object* classLoader, jvalue* result, jboolean ignoreClassNotFound) { char* annotationTypeName = getString(attributes); if (expectedAnnotationClass && strncmp(&annotationTypeName[1], expectedAnnotationClass->name, strlen(expectedAnnotationClass->name))) { return throwFormatError(env, rvmFromBinaryClassName(env, expectedAnnotationClass->name)); } Class* annotationClass = expectedAnnotationClass; if (!annotationClass) { annotationClass = rvmFindClassByDescriptor(env, annotationTypeName, classLoader); if (!annotationClass) { if (ignoreClassNotFound && rvmExceptionOccurred(env)->clazz == java_lang_ClassNotFoundException) { rvmExceptionClear(env); jint length = getInt(attributes); for (jint i = 0; i < length; i++) { getString(attributes); skipElementValue(attributes); } } return FALSE; } } // Find the annotation impl class Class* annotationImplClass = findAnnotationImplClass(env, annotationClass, classLoader); if (rvmExceptionCheck(env)) return FALSE; jint length = getInt(attributes); if (length == 0) { // No member values specified. Use a singleton instance. Method* factoryMethod = rvmGetClassMethod(env, annotationImplClass, "$createSingleton", "()Ljava/lang/Object;"); if (rvmExceptionCheck(env)) return FALSE; Object* annotationObject = rvmCallObjectClassMethod(env, annotationImplClass, factoryMethod); if (rvmExceptionCheck(env)) return FALSE; result->l = (jobject) annotationObject; return TRUE; } // Call the annotation impl $create() method Method* factoryMethod = rvmGetClassMethod(env, annotationImplClass, "$create", "()Ljava/lang/Object;"); if (rvmExceptionCheck(env)) return FALSE; Object* annotationObject = rvmCallObjectClassMethod(env, annotationImplClass, factoryMethod); if (rvmExceptionCheck(env)) return FALSE; jint i = 0; for (i = 0; i < length; i++) { char* name = getString(attributes); Method* method = getAnnotationValueMethod(env, annotationClass, name); if (rvmExceptionCheck(env)) return FALSE; if (!method) { skipElementValue(attributes); } else { const char* memberDesc = rvmGetReturnType(method->desc); Class* type = findType(env, memberDesc, method->clazz->classLoader); Object* value = NULL; if (!type) { value = rvmExceptionClear(env); } else { jvalue v = {0}; if (!parseElementValue(env, attributes, type, classLoader, &v)) { value = rvmExceptionClear(env); } else { value = rvmBox(env, type, &v); } } InstanceField* field = getAnnotationMemberField(env, annotationImplClass, method->name); if (!field) return FALSE; rvmSetObjectInstanceFieldValue(env, annotationObject, field, value); if (rvmExceptionCheck(env)) return FALSE; } } result->l = (jobject) annotationObject; return TRUE; }
static void ExceptionClear(JNIEnv* env) { rvmExceptionClear((Env*) env); }
void rvmInitialize(Env* env, Class* clazz) { obtainClassLock(); // TODO: Throw java.lang.NoClassDefFoundError if state == CLASS_ERROR? if (CLASS_IS_STATE_ERROR(clazz)) { // TODO: Add the class' binary name in the message rvmThrowNew(env, java_lang_NoClassDefFoundError, "Could not initialize class ??"); releaseClassLock(); return; } if (!CLASS_IS_STATE_INITIALIZED(clazz) && !CLASS_IS_STATE_INITIALIZING(clazz)) { jint oldState = clazz->flags & CLASS_STATE_MASK; clazz->flags = (clazz->flags & (~CLASS_STATE_MASK)) | CLASS_STATE_INITIALIZING; if (clazz->superclass) { rvmInitialize(env, clazz->superclass); if (rvmExceptionOccurred(env)) { clazz->flags = (clazz->flags & (~CLASS_STATE_MASK)) | oldState; releaseClassLock(); return; } } TRACEF("Initializing class %s", clazz->name); void* initializer = clazz->initializer; if (!initializer) { if (!CLASS_IS_ARRAY(clazz) && !CLASS_IS_PROXY(clazz) && !CLASS_IS_PRIMITIVE(clazz)) { env->vm->options->classInitialized(env, clazz); } clazz->flags = (clazz->flags & (~CLASS_STATE_MASK)) | CLASS_STATE_INITIALIZED; releaseClassLock(); return; } CallInfo* callInfo = call0AllocateCallInfo(env, initializer, 1, 0, 0, 0, 0); call0AddPtr(callInfo, env); void (*f)(CallInfo*) = (void (*)(CallInfo*)) _call0; rvmPushGatewayFrame(env); TrycatchContext tc = {0}; tc.sel = CATCH_ALL_SEL; if (!rvmTrycatchEnter(env, &tc)) { f(callInfo); } rvmTrycatchLeave(env); rvmPopGatewayFrame(env); Object* exception = rvmExceptionClear(env); if (exception) { clazz->flags = (clazz->flags & (~CLASS_STATE_MASK)) | CLASS_STATE_ERROR; if (!rvmIsInstanceOf(env, exception, java_lang_Error)) { // If exception isn't an instance of java.lang.Error // we must wrap it in a java.lang.ExceptionInInitializerError Method* constructor = rvmGetInstanceMethod(env, java_lang_ExceptionInInitializerError, "<init>", "(Ljava/lang/Throwable;)V"); if (!constructor) return; Object* wrappedException = rvmNewObject(env, java_lang_ExceptionInInitializerError, constructor, exception); if (!wrappedException) return; exception = wrappedException; } rvmThrow(env, exception); releaseClassLock(); return; } if (!CLASS_IS_ARRAY(clazz) && !CLASS_IS_PROXY(clazz) && !CLASS_IS_PRIMITIVE(clazz)) { env->vm->options->classInitialized(env, clazz); } clazz->flags = (clazz->flags & (~CLASS_STATE_MASK)) | CLASS_STATE_INITIALIZED; } releaseClassLock(); }
jboolean rvmInitMemory(Env* env) { vm = env->vm; gcAddRoot(&referents); java_lang_ref_Reference_referent = rvmGetInstanceField(env, java_lang_ref_Reference, "referent", "Ljava/lang/Object;"); if (!java_lang_ref_Reference_referent) return FALSE; java_lang_ref_Reference_pendingNext = rvmGetInstanceField(env, java_lang_ref_Reference, "pendingNext", "Ljava/lang/ref/Reference;"); if (!java_lang_ref_Reference_pendingNext) return FALSE; java_lang_ref_Reference_queue = rvmGetInstanceField(env, java_lang_ref_Reference, "queue", "Ljava/lang/ref/ReferenceQueue;"); if (!java_lang_ref_Reference_queue) return FALSE; java_lang_ref_Reference_queueNext = rvmGetInstanceField(env, java_lang_ref_Reference, "queueNext", "Ljava/lang/ref/Reference;"); if (!java_lang_ref_Reference_queueNext) return FALSE; java_lang_ref_PhantomReference = rvmFindClassUsingLoader(env, "java/lang/ref/PhantomReference", NULL); if (!java_lang_ref_PhantomReference) return FALSE; java_lang_ref_WeakReference = rvmFindClassUsingLoader(env, "java/lang/ref/WeakReference", NULL); if (!java_lang_ref_WeakReference) return FALSE; java_lang_ref_SoftReference = rvmFindClassUsingLoader(env, "java/lang/ref/SoftReference", NULL); if (!java_lang_ref_SoftReference) return FALSE; java_lang_ref_FinalizerReference = rvmFindClassUsingLoader(env, "java/lang/ref/FinalizerReference", NULL); if (!java_lang_ref_FinalizerReference) return FALSE; java_lang_ref_FinalizerReference_add = rvmGetClassMethod(env, java_lang_ref_FinalizerReference, "add", "(Ljava/lang/Object;)V"); if (!java_lang_ref_FinalizerReference_add) return FALSE; java_lang_ref_FinalizerReference_zombie = rvmGetInstanceField(env, java_lang_ref_FinalizerReference, "zombie", "Ljava/lang/Object;"); if (!java_lang_ref_FinalizerReference_zombie) return FALSE; java_lang_ref_ReferenceQueue = rvmFindClassUsingLoader(env, "java/lang/ref/ReferenceQueue", NULL); if (!java_lang_ref_ReferenceQueue) return FALSE; java_lang_ref_ReferenceQueue_add = rvmGetClassMethod(env, java_lang_ref_ReferenceQueue, "add", "(Ljava/lang/ref/Reference;)V"); if (!java_lang_ref_ReferenceQueue_add) return FALSE; java_nio_ReadWriteDirectByteBuffer = rvmFindClassUsingLoader(env, "java/nio/ReadWriteDirectByteBuffer", NULL); if (!java_nio_ReadWriteDirectByteBuffer) return FALSE; java_nio_ReadWriteDirectByteBuffer_init = rvmGetInstanceMethod(env, java_nio_ReadWriteDirectByteBuffer, "<init>", "(II)V"); if (!java_nio_ReadWriteDirectByteBuffer_init) return FALSE; Class* java_nio_Buffer = rvmFindClassUsingLoader(env, "java/nio/Buffer", NULL); if (!java_nio_Buffer) return FALSE; java_nio_Buffer_effectiveDirectAddress = rvmGetInstanceField(env, java_nio_Buffer, "effectiveDirectAddress", "I"); if (!java_nio_Buffer_effectiveDirectAddress) return FALSE; java_nio_Buffer_capacity = rvmGetInstanceField(env, java_nio_Buffer, "capacity", "I"); if (!java_nio_Buffer_capacity) return FALSE; java_lang_Throwable_stackState = rvmGetInstanceField(env, java_lang_Throwable, "stackState", "J"); if (!java_lang_Throwable_stackState) return FALSE; org_robovm_rt_bro_Struct = rvmFindClassUsingLoader(env, "org/robovm/rt/bro/Struct", NULL); if (!org_robovm_rt_bro_Struct) { // We don't need Struct if it hasn't been compiled in rvmExceptionClear(env); } else { org_robovm_rt_bro_Struct_handle = rvmGetInstanceField(env, org_robovm_rt_bro_Struct, "handle", "J"); if (!org_robovm_rt_bro_Struct_handle) return FALSE; } java_nio_MemoryBlock = rvmFindClassUsingLoader(env, "java/nio/MemoryBlock", NULL); if (!java_nio_MemoryBlock) return FALSE; java_nio_MemoryBlock_address = rvmGetInstanceField(env, java_nio_MemoryBlock, "address", "I"); if (!java_nio_MemoryBlock_address) return FALSE; criticalOutOfMemoryError = rvmAllocateMemoryForObject(env, java_lang_OutOfMemoryError); if (!criticalOutOfMemoryError) return FALSE; criticalOutOfMemoryError->clazz = java_lang_OutOfMemoryError; if (!rvmAddObjectGCRoot(env, criticalOutOfMemoryError)) return FALSE; return TRUE; }
Object* _bcExceptionClear(Env* env) { return rvmExceptionClear(env); }
static void finalizeObject(Env* env, Object* obj) { // TRACEF("finalizeObject: %p (%s)\n", obj, obj->clazz->name); rvmLockMutex(&referentsLock); void* key = (void*) GC_HIDE_POINTER(obj); ReferentEntry* referentEntry; HASH_FIND_PTR(referents, &key, referentEntry); assert(referentEntry != NULL); if (referentEntry->references == NULL) { // The object is not referenced by any type of reference and can never be resurrected. HASH_DEL(referents, referentEntry); rvmUnlockMutex(&referentsLock); // Run all cleanup handlers registered for the object CleanupHandlerList* l = referentEntry->cleanupHandlers; while (l) { l->handler(env, obj); // Discard any exception thrown by the cleanup handler rvmExceptionClear(env); l = l->next; } return; } Object* softReferences = NULL; Object* weakReferences = NULL; Object* finalizerReferences = NULL; Object* phantomReferences = NULL; Object* clearedReferences = NULL; ReferenceList* refNode; while (referentEntry->references != NULL) { refNode = referentEntry->references; LL_DELETE(referentEntry->references, refNode); Object** list = NULL; Object* reference = refNode->reference; if (rvmIsSubClass(java_lang_ref_SoftReference, reference->clazz)) { list = &softReferences; } else if (rvmIsSubClass(java_lang_ref_WeakReference, reference->clazz)) { list = &weakReferences; } else if (rvmIsSubClass(java_lang_ref_FinalizerReference, reference->clazz)) { list = &finalizerReferences; } else if (rvmIsSubClass(java_lang_ref_PhantomReference, reference->clazz)) { list = &phantomReferences; } enqueuePendingReference(env, reference, list); } assert(referentEntry->references == NULL); clearAndEnqueueReferences(env, &softReferences, &clearedReferences); clearAndEnqueueReferences(env, &weakReferences, &clearedReferences); enqueueFinalizerReferences(env, &finalizerReferences, &clearedReferences); clearAndEnqueueReferences(env, &phantomReferences, &clearedReferences); // Reregister for finalization. If no new references have been added to the list of references for the referent the // next time it gets finalized we know it will never be resurrected. GC_REGISTER_FINALIZER_NO_ORDER(obj, _finalizeObject, NULL, NULL, NULL); rvmUnlockMutex(&referentsLock); if (clearedReferences != NULL) { rvmCallVoidClassMethod(env, java_lang_ref_ReferenceQueue, java_lang_ref_ReferenceQueue_add, clearedReferences); assert(rvmExceptionOccurred(env) == NULL); } }