diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/ModuleReferenceImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/ModuleReferenceImpl.c index 25d076889f0..3ff27b89d63 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/ModuleReferenceImpl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/ModuleReferenceImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ static jclass jlM(JNIEnv *env) { - return findClass(env, "Ljava/lang/Module;"); + return findClass(env, "java/lang/Module"); } static jboolean @@ -50,6 +50,11 @@ getName(PacketInputStream *in, PacketOutputStream *out) return JNI_TRUE; } namestr = (jstring)JNI_FUNC_PTR(env, CallObjectMethod) (env, module, method); + if (JNI_FUNC_PTR(env,ExceptionCheck)(env)) { + JNI_FUNC_PTR(env,ExceptionClear)(env); // keep -Xcheck:jni happy + ERROR_MESSAGE(("JNI Exception occurred calling Module.getName()")); + EXIT_ERROR(AGENT_ERROR_JNI_EXCEPTION, NULL); + } if (namestr != NULL) { name = (char*)JNI_FUNC_PTR(env, GetStringUTFChars)(env, namestr, NULL); } else { @@ -78,6 +83,11 @@ getClassLoader(PacketInputStream *in, PacketOutputStream *out) return JNI_TRUE; } loader = JNI_FUNC_PTR(env, CallObjectMethod) (env, module, method); + if (JNI_FUNC_PTR(env,ExceptionCheck)(env)) { + JNI_FUNC_PTR(env,ExceptionClear)(env); // keep -Xcheck:jni happy + ERROR_MESSAGE(("JNI Exception occurred calling ClassLoader.getClassLoader()")); + EXIT_ERROR(AGENT_ERROR_JNI_EXCEPTION, NULL); + } (void)outStream_writeObjectRef(env, out, loader); return JNI_TRUE; diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index e4a9bd79943..3968b1de6b6 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -681,23 +681,14 @@ event_callback(JNIEnv *env, EventInfo *evinfo) bagDestroyBag(eventBag); } - /* Always restore any exception that was set beforehand. If - * there is a pending async exception, StopThread will be - * called from threadControl_onEventHandlerExit immediately - * below. Depending on VM implementation and state, the async - * exception might immediately overwrite the currentException, - * or it might be delayed until later. */ - if (currentException != NULL) { - JNI_FUNC_PTR(env,Throw)(env, currentException); - } else { - JNI_FUNC_PTR(env,ExceptionClear)(env); - } + /* Clear any exception thrown while handling the event. */ + JNI_FUNC_PTR(env,ExceptionClear)(env); /* * Release thread resources and perform any delayed operations. */ if (thread != NULL) { - threadControl_onEventHandlerExit(evinfo->ei, thread, eventBag); + threadControl_onEventHandlerExit(evinfo->ei, thread, eventBag, currentException); } } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c index 4b32430ad7b..1dcb73500d7 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c @@ -338,6 +338,22 @@ invoker_requestInvoke(jbyte invokeType, jbyte options, jint id, return error; } +static void +saveGlobalRefHelper(JNIEnv *env, jobject obj, jobject *pobj) +{ + // In order to keep -Xcheck:jni happy, we have to clear any pending + // exception before calling saveGlobalRef(). We also need to restore + // it for the caller of this function. + jthrowable exception = JNI_FUNC_PTR(env,ExceptionOccurred)(env); + if (exception != NULL) { + JNI_FUNC_PTR(env,ExceptionClear)(env); + } + saveGlobalRef(env, obj, pobj); + if (exception != NULL) { + JNI_FUNC_PTR(env,Throw)(env, exception); + } +} + static void invokeConstructor(JNIEnv *env, InvokeRequest *request) { @@ -349,7 +365,7 @@ invokeConstructor(JNIEnv *env, InvokeRequest *request) request->arguments); request->returnValue.l = NULL; if (object != NULL) { - saveGlobalRef(env, object, &(request->returnValue.l)); + saveGlobalRefHelper(env, object, &(request->returnValue.l)); } } @@ -367,7 +383,7 @@ invokeStatic(JNIEnv *env, InvokeRequest *request) request->arguments); request->returnValue.l = NULL; if (object != NULL) { - saveGlobalRef(env, object, &(request->returnValue.l)); + saveGlobalRefHelper(env, object, &(request->returnValue.l)); } return; } @@ -455,7 +471,7 @@ invokeVirtual(JNIEnv *env, InvokeRequest *request) request->arguments); request->returnValue.l = NULL; if (object != NULL) { - saveGlobalRef(env, object, &(request->returnValue.l)); + saveGlobalRefHelper(env, object, &(request->returnValue.l)); } return; } @@ -545,7 +561,7 @@ invokeNonvirtual(JNIEnv *env, InvokeRequest *request) request->arguments); request->returnValue.l = NULL; if (object != NULL) { - saveGlobalRef(env, object, &(request->returnValue.l)); + saveGlobalRefHelper(env, object, &(request->returnValue.l)); } return; } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c index 7e68e65e56c..4b037142d52 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c @@ -2141,7 +2141,7 @@ doPendingTasks(JNIEnv *env, jthread thread, int pendingInterrupt, jobject pendin void threadControl_onEventHandlerExit(EventIndex ei, jthread thread, - struct bag *eventBag) + struct bag *eventBag, jobject currentException) { ThreadNode *node; JNIEnv *env = getEnv(); @@ -2178,6 +2178,17 @@ threadControl_onEventHandlerExit(EventIndex ei, jthread thread, // locks when doing that. Thus we got all our node updates done first // and can now exit the threadLock. debugMonitorExit(threadLock); + if (currentException != NULL) { + // We need to rethrow the exception that was current when we received the + // JVMTI event. If there is a pending async exception, StopThread will be + // called from doPendingTasks() immediately below. Depending on the VM + // implementation and state, the async exception might immediately overwrite + // the currentException, or it might be delayed until later. + // + // Note in order the keep the JNI Checker happy, we had to delay doing this + // until now. Otherwise there are complaints when JNI IsVirtualThread is called. + JNI_FUNC_PTR(env,Throw)(env, currentException); + } doPendingTasks(env, thread, pendingInterrupt, pendingStop); if (pendingStop != NULL) { tossGlobalRef(env, &pendingStop); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.h b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.h index b7817d35981..f3e921948f4 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,8 +41,7 @@ void threadControl_onDisconnect(void); jvmtiError threadControl_popFrames(jthread thread, FrameNumber fnum); struct bag *threadControl_onEventHandlerEntry(jbyte sessionID, EventInfo *evinfo, jobject currentException); -void threadControl_onEventHandlerExit(EventIndex ei, jthread thread, struct bag *); - +void threadControl_onEventHandlerExit(EventIndex ei, jthread thread, struct bag *, jobject currentException); jvmtiError threadControl_suspendThread(jthread thread, jboolean deferred); jvmtiError threadControl_resumeThread(jthread thread, jboolean do_unblock); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c index a79545ed274..45de2ba7b7a 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c @@ -101,13 +101,10 @@ findClass(JNIEnv *env, const char * name) EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"findClass name"); } x = JNI_FUNC_PTR(env,FindClass)(env, name); - if (x == NULL) { - ERROR_MESSAGE(("JDWP Can't find class %s", name)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); - } if ( JNI_FUNC_PTR(env,ExceptionCheck)(env) ) { - ERROR_MESSAGE(("JDWP Exception occurred finding class %s", name)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); + JNI_FUNC_PTR(env,ExceptionClear)(env); // keep -Xcheck:jni happy + ERROR_MESSAGE(("JNI Exception occurred finding class %s", name)); + EXIT_ERROR(AGENT_ERROR_JNI_EXCEPTION,NULL); } return x; } @@ -130,15 +127,11 @@ getMethod(JNIEnv *env, jclass clazz, const char * name, const char *signature) EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getMethod signature"); } method = JNI_FUNC_PTR(env,GetMethodID)(env, clazz, name, signature); - if (method == NULL) { - ERROR_MESSAGE(("JDWP Can't find method %s with signature %s", - name, signature)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); - } if ( JNI_FUNC_PTR(env,ExceptionCheck)(env) ) { - ERROR_MESSAGE(("JDWP Exception occurred finding method %s with signature %s", - name, signature)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); + JNI_FUNC_PTR(env,ExceptionClear)(env); // keep -Xcheck:jni happy + ERROR_MESSAGE(("JNI Exception occurred finding method %s with signature %s", + name, signature)); + EXIT_ERROR(AGENT_ERROR_JNI_EXCEPTION,NULL); } return method; } @@ -161,15 +154,11 @@ getStaticMethod(JNIEnv *env, jclass clazz, const char * name, const char *signat EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getStaticMethod signature"); } method = JNI_FUNC_PTR(env,GetStaticMethodID)(env, clazz, name, signature); - if (method == NULL) { - ERROR_MESSAGE(("JDWP Can't find method %s with signature %s", - name, signature)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); - } if ( JNI_FUNC_PTR(env,ExceptionCheck)(env) ) { + JNI_FUNC_PTR(env,ExceptionClear)(env); // keep -Xcheck:jni happy ERROR_MESSAGE(("JDWP Exception occurred finding method %s with signature %s", - name, signature)); - EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL); + name, signature)); + EXIT_ERROR(AGENT_ERROR_JNI_EXCEPTION,NULL); } return method; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jni/JNIreferences.cpp b/test/hotspot/jtreg/vmTestbase/nsk/share/jni/JNIreferences.cpp index 21285d32825..2babf82e02e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jni/JNIreferences.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jni/JNIreferences.cpp @@ -116,6 +116,7 @@ Java_nsk_share_ReferringObject_createJNILocalReferenceNative(JNIEnv *env, env->ThrowNew( env->FindClass("nsk/share/TestJNIError"), "NewLocalRef return null"); + return; } klass = env->GetObjectClass(createWicket); @@ -123,8 +124,11 @@ Java_nsk_share_ReferringObject_createJNILocalReferenceNative(JNIEnv *env, // notify another thread that JNI local reference has been created env->CallVoidMethod(createWicket, env->GetMethodID(klass, "unlock", "()V")); + if (env->ExceptionCheck()) { + return; + } - // wait till JNI local reference can be released (it will heppen then we will leave the method) + // wait till JNI local reference can be released (it will happen then we will leave the method) env->CallVoidMethod(deleteWicket, env->GetMethodID(klass, "waitFor", "()V")); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/libNativeMethodsTestThread.cpp b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/libNativeMethodsTestThread.cpp index beb9e90a995..2c16e3b12b9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/libNativeMethodsTestThread.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/libNativeMethodsTestThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,10 @@ static void logMessage(JNIEnv *env, jobject thisObject, jstring message) env->CallVoidMethod(thisObject, env->GetMethodID(klass, "log", "(Ljava/lang/String;)V"), message); + if (env->ExceptionOccurred()) { + env->ExceptionDescribe(); + env->FatalError("ERROR: Failed to log message."); + } } JNIEXPORT void JNICALL diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/locks/JNIMonitorLocker.cpp b/test/hotspot/jtreg/vmTestbase/nsk/share/locks/JNIMonitorLocker.cpp index f54e15ff6cf..fee28595a43 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/locks/JNIMonitorLocker.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/locks/JNIMonitorLocker.cpp @@ -66,6 +66,7 @@ This method executes JNI analog for following Java code: env->ThrowNew( env->FindClass("nsk/share/TestJNIError"), "MonitorEnter return non-zero"); + return; } thisObjectClass = env->GetObjectClass(thisObject); @@ -78,6 +79,9 @@ This method executes JNI analog for following Java code: env->CallVoidMethod(wicketObject, env->GetMethodID(wicketClass, "unlockAll", "()V")); + if (env->ExceptionOccurred()) { + return; + } // step2.waitFor() field = env->GetFieldID(thisObjectClass, "step2", "Lnsk/share/Wicket;"); @@ -85,6 +89,9 @@ This method executes JNI analog for following Java code: env->CallVoidMethod(wicketObject, env->GetMethodID(wicketClass, "waitFor", "()V")); + if (env->ExceptionOccurred()) { + return; + } // readyWicket.unlock() field = env->GetFieldID(thisObjectClass, "readyWicket", "Lnsk/share/Wicket;"); @@ -92,6 +99,9 @@ This method executes JNI analog for following Java code: env->CallVoidMethod(wicketObject, env->GetMethodID(wicketClass, "unlock", "()V")); + if (env->ExceptionOccurred()) { + return; + } // inner.lock() field = env->GetFieldID(thisObjectClass, "inner", "Lnsk/share/locks/DeadlockLocker;"); @@ -100,8 +110,14 @@ This method executes JNI analog for following Java code: env->CallVoidMethod(innerObject, env->GetMethodID(deadlockLockerClass, "lock", "()V")); + if (env->ExceptionOccurred()) { + return; + } success = env->MonitorExit(thisObject); + if (env->ExceptionOccurred()) { + return; + } if (success != 0) {