--- old/hotspot/make/test/JtregNative.gmk 2016-08-19 12:32:29.494470431 +0200 +++ new/hotspot/make/test/JtregNative.gmk 2016-08-19 12:32:29.350467208 +0200 @@ -44,6 +44,7 @@ $(HOTSPOT_TOPDIR)/test/native_sanity \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8025979 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \ + $(HOTSPOT_TOPDIR)/test/runtime/jni/8164086 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/ToStringInInterfaceTest \ $(HOTSPOT_TOPDIR)/test/runtime/modules/getModuleJNI \ $(HOTSPOT_TOPDIR)/test/runtime/SameObject \ --- old/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp 2016-08-19 12:32:29.986481442 +0200 +++ new/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp 2016-08-19 12:32:29.834478040 +0200 @@ -2041,6 +2041,11 @@ __ verify_oop(r0); } + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ str(zr, Address(rthread, JavaThread::pending_jni_exception_check_fn_offset()); + } + if (!is_critical_native) { // reset handle block __ ldr(r2, Address(rthread, JavaThread::active_handles_offset())); --- old/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp 2016-08-19 12:32:30.526493527 +0200 +++ new/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp 2016-08-19 12:32:30.378490215 +0200 @@ -1355,6 +1355,11 @@ // reset_last_Java_frame __ reset_last_Java_frame(true, true); + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ str(zr, Address(rthread, JavaThread::pending_jni_exception_check_fn_offset()); + } + // reset handle block __ ldr(t, Address(rthread, JavaThread::active_handles_offset())); __ str(zr, Address(t, JNIHandleBlock::top_offset_in_bytes())); --- old/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp 2016-08-19 12:32:31.014504449 +0200 +++ new/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp 2016-08-19 12:32:30.866501137 +0200 @@ -2765,6 +2765,11 @@ __ verify_oop(I0); } + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ st_ptr(G0, G2_thread, JavaThread::pending_jni_exception_check_fn_offset()); + } + if (!is_critical_native) { // reset handle block __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), L5); --- old/hotspot/src/cpu/sparc/vm/templateInterpreterGenerator_sparc.cpp 2016-08-19 12:32:31.530515998 +0200 +++ new/hotspot/src/cpu/sparc/vm/templateInterpreterGenerator_sparc.cpp 2016-08-19 12:32:31.382512686 +0200 @@ -1487,6 +1487,11 @@ __ set(_thread_in_Java, G3_scratch); __ st(G3_scratch, thread_state); + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ st_ptr(G0, G2_thread, JavaThread::pending_jni_exception_check_fn_offset()); + } + // reset handle block __ ld_ptr(G2_thread, JavaThread::active_handles_offset(), G3_scratch); __ st(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); --- old/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp 2016-08-19 12:32:32.042527458 +0200 +++ new/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp 2016-08-19 12:32:31.898524235 +0200 @@ -2236,6 +2236,11 @@ __ verify_oop(rax); } + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ movptr(Address(thread, JavaThread::pending_jni_exception_check_fn_offset()), NULL_WORD); + } + if (!is_critical_native) { // reset handle block __ movptr(rcx, Address(thread, JavaThread::active_handles_offset())); --- old/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp 2016-08-19 12:32:32.574539366 +0200 +++ new/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp 2016-08-19 12:32:32.426536053 +0200 @@ -2589,6 +2589,11 @@ __ verify_oop(rax); } + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ movptr(Address(r15_thread, JavaThread::pending_jni_exception_check_fn_offset()), NULL_WORD); + } + if (!is_critical_native) { // reset handle block __ movptr(rcx, Address(r15_thread, JavaThread::active_handles_offset())); --- old/hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp 2016-08-19 12:32:33.070550468 +0200 +++ new/hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp 2016-08-19 12:32:32.958547961 +0200 @@ -1169,6 +1169,11 @@ // reset_last_Java_frame __ reset_last_Java_frame(thread, true, true); + if (CheckJNICalls) { + // clear_pending_jni_exception_check + __ movptr(Address(thread, JavaThread::pending_jni_exception_check_fn_offset()), NULL_WORD); + } + // reset handle block __ movptr(t, Address(thread, JavaThread::active_handles_offset())); __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); --- old/hotspot/src/share/vm/runtime/thread.hpp 2016-08-19 12:32:33.570561660 +0200 +++ new/hotspot/src/share/vm/runtime/thread.hpp 2016-08-19 12:32:33.426558436 +0200 @@ -1542,6 +1542,9 @@ static ByteSize jmp_ring_offset() { return byte_offset_of(JavaThread, _jmp_ring); } #endif // PRODUCT static ByteSize jni_environment_offset() { return byte_offset_of(JavaThread, _jni_environment); } + static ByteSize pending_jni_exception_check_fn_offset() { + return byte_offset_of(JavaThread, _pending_jni_exception_check_fn); + } static ByteSize last_Java_sp_offset() { return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_sp_offset(); } --- old/jdk/src/java.base/share/native/libjli/java.c 2016-08-19 12:32:34.182575359 +0200 +++ new/jdk/src/java.base/share/native/libjli/java.c 2016-08-19 12:32:33.970570613 +0200 @@ -376,6 +376,12 @@ } \ } while (JNI_FALSE) +#define CHECK_EXCEPTION_PRINT() \ + do { \ + if ((*env)->ExceptionOccurred(env)) { \ + JLI_ReportExceptionDescription(env); \ + } \ + } while (JNI_FALSE) int JNICALL JavaMain(void * _args) @@ -1529,6 +1535,7 @@ str = (*env)->CallStaticObjectMethod(env, cls, makePlatformStringMID, USE_STDERR, ary); (*env)->DeleteLocalRef(env, ary); + CHECK_EXCEPTION_PRINT(); return str; } } @@ -1580,6 +1587,7 @@ NULL_CHECK0(str = NewPlatformString(env, name)); NULL_CHECK0(result = (*env)->CallStaticObjectMethod(env, cls, mid, USE_STDERR, mode, str)); + CHECK_EXCEPTION_PRINT(); if (JLI_IsTraceLauncher()) { end = CounterGet(); @@ -1601,7 +1609,9 @@ "getApplicationClass", "()Ljava/lang/Class;")); - return (*env)->CallStaticObjectMethod(env, cls, mid); + cls = (*env)->CallStaticObjectMethod(env, cls, mid); + CHECK_EXCEPTION_PRINT(); + return cls; } /* @@ -1812,6 +1822,7 @@ ); (*env)->CallStaticVoidMethod(env, ver, print); + CHECK_EXCEPTION_PRINT(); } /* @@ -1834,6 +1845,7 @@ (jlong)maxHeapSize, (jlong)threadStackSize, ServerClassMachine()); + CHECK_EXCEPTION_PRINT(); } /** @@ -1852,6 +1864,7 @@ (*env)->CallStaticVoidMethod(env, cls, listModulesID, USE_STDERR, joptString); + CHECK_EXCEPTION_PRINT(); } /* @@ -1869,6 +1882,7 @@ NULL_CHECK(printXUsageMessage = (*env)->GetStaticMethodID(env, cls, "printXUsageMessage", "(Z)V")); (*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, USE_STDERR); + CHECK_EXCEPTION_PRINT(); } else { NULL_CHECK(initHelp = (*env)->GetStaticMethodID(env, cls, "initHelpMessage", "(Ljava/lang/String;)V")); @@ -1933,6 +1947,7 @@ /* Complete the usage message and print to stderr*/ (*env)->CallStaticVoidMethod(env, cls, printHelp, USE_STDERR); + CHECK_EXCEPTION_RETURN(); } return; } --- /dev/null 2016-08-16 08:36:37.092052523 +0200 +++ new/hotspot/test/runtime/jni/8164086/TestCheckedJniExceptionCheck.java 2016-08-19 12:32:34.546583507 +0200 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2016, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8164086 + * @summary regression tests for 8164086, verify correct warning from checked JNI + * @library /testlibrary + * @modules java.base/jdk.internal.misc + * java.management + * @run main/native TestCheckedJniExceptionCheck launch + */ + +import java.util.List; +import jdk.test.lib.*; + +public class TestCheckedJniExceptionCheck { + + static { + System.loadLibrary("TestCheckedJniExceptionCheck"); + } + + int callableMethodInvokeCount = 0; + + static final String EXPECT_WARNING_START = "EXPECT_WARNING_START"; + static final String EXPECT_WARNING_END = "EXPECT_WARNING_END"; + + static final String JNI_CHECK_EXCEPTION = "WARNING in native method: JNI call made without checking exceptions when required to from"; + + static void printExpectWarningStart(int count) { + System.out.println(EXPECT_WARNING_START + " " + count); + } + + static void printExpectWarningEnd() { + System.out.println(EXPECT_WARNING_END); + } + + public TestCheckedJniExceptionCheck() { + initMethodIds("callableMethod", "()V", + "callableNestedMethod", "(IZ)V"); + } + + public void test() { + testSingleCallNoCheck(); + testSingleCallCheck(); + testSingleCallNoCheckMultipleTimes(); + + testMultipleCallsNoCheck(); + testMultipleCallsCheck(); + + testNestedSingleCallsNoCheck(); + testNestedSingleCallsCheck(); + testNestedMultipleCallsNoCheck(); + testNestedMultipleCallsCheck(); + } + + public void testSingleCallNoCheck() { + System.out.println("testSingleCallNoCheck start"); + callJavaFromNative(1, false); + System.out.println("testSingleCallNoCheck end"); + } + + public void testSingleCallCheck() { + System.out.println("testSingleCallCheck start"); + callJavaFromNative(1, true); + System.out.println("testSingleCallCheck end"); + } + + public void testSingleCallNoCheckMultipleTimes() { + System.out.println("testSingleCallNoCheckMultipleTimes start"); + callJavaFromNative(1, false); + callJavaFromNative(1, false); + System.out.println("testSingleCallNoCheckMultipleTimes end"); + } + + public void testMultipleCallsNoCheck() { + System.out.println("testMultipleCallsNoCheck start"); + printExpectWarningStart(1); + callJavaFromNative(2, false); + printExpectWarningEnd(); + System.out.println("testMultipleCallsNoCheck end"); + } + + public void testMultipleCallsCheck() { + System.out.println("testMultipleCallsCheck start"); + callJavaFromNative(2, true); + System.out.println("testMultipleCallsCheck end"); + } + + public void testNestedSingleCallsNoCheck() { + System.out.println("testNestedSingleCallsNoCheck start"); + callNestedJavaFromNative(1, false); + System.out.println("testNestedSingleCallsNoCheck end"); + } + + public void testNestedSingleCallsCheck() { + System.out.println("testNestedSingleCallsCheck start"); + callNestedJavaFromNative(1, true); + System.out.println("testNestedSingleCallsCheck end"); + } + + public void testNestedMultipleCallsNoCheck() { + System.out.println("testNestedMultipleCallsNoCheck start"); + printExpectWarningStart(3); + callNestedJavaFromNative(2, false); + printExpectWarningEnd(); + System.out.println("testNestedMultipleCallsNoCheck end"); + } + + public void testNestedMultipleCallsCheck() { + System.out.println("testNestedMultipleCallsCheck start"); + callNestedJavaFromNative(2, true); + System.out.println("testNestedMultipleCallsCheck end"); + } + + public void callableMethod() { + callableMethodInvokeCount++; + } + + public void callableNestedMethod(int nofCalls, boolean withExceptionChecks) { + callJavaFromNative(nofCalls, withExceptionChecks); + } + + public native void callJavaFromNative(int nofCalls, boolean withExceptionChecks); + + public native void callNestedJavaFromNative(int nofCalls, boolean withExceptionChecks); + + private native void initMethodIds(String callableMethodName, + String callableMethodSig, + String callableNestedMethodName, + String callableNestedMethodSig); + + + // Check warnings appear where they should, with start/end statements in output... + static void checkOuputForCorrectWarnings(OutputAnalyzer oa) throws RuntimeException { + List lines = oa.asLines(); + int expectedWarnings = 0; + int warningCount = 0; + int lineNo = 0; + for (String line : lines) { + lineNo++; + if (line.startsWith(JNI_CHECK_EXCEPTION)) { + if (expectedWarnings == 0) { + oa.reportDiagnosticSummary(); + throw new RuntimeException("Unexpected warning at line " + lineNo); + } + warningCount++; + if (warningCount > expectedWarnings) { + oa.reportDiagnosticSummary(); + throw new RuntimeException("Unexpected warning at line " + lineNo); + } + } + else if (line.startsWith(EXPECT_WARNING_START)) { + String countStr = line.substring(EXPECT_WARNING_START.length() + 1); + expectedWarnings = Integer.parseInt(countStr); + } + else if (line.startsWith(EXPECT_WARNING_END)) { + if (warningCount != expectedWarnings) { + oa.reportDiagnosticSummary(); + throw new RuntimeException("Missing warning at line " + lineNo); + } + warningCount = 0; + expectedWarnings = 0; + } + } + /* + System.out.println("Output looks good..."); + oa.reportDiagnosticSummary(); + */ + } + + public static void main(String[] args) throws Throwable { + if (args == null || args.length == 0) { + new TestCheckedJniExceptionCheck().test(); + return; + } + + // launch and check output + checkOuputForCorrectWarnings(ProcessTools.executeTestJvm("-Xcheck:jni", + "TestCheckedJniExceptionCheck")); + } + +} --- /dev/null 2016-08-16 08:36:37.092052523 +0200 +++ new/hotspot/test/runtime/jni/8164086/libTestCheckedJniExceptionCheck.c 2016-08-19 12:32:35.014593984 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +static jmethodID _callable_method_id; +static jmethodID _callable_nested_method_id; + +static jmethodID get_method_id(JNIEnv *env, jclass clz, jstring jname, jstring jsig) { + jmethodID mid; + const char *name, *sig; + name = (*env)->GetStringUTFChars(env, jname, NULL); + sig = (*env)->GetStringUTFChars(env, jsig, NULL); + mid = (*env)->GetMethodID(env, clz, name, sig); + (*env)->ReleaseStringUTFChars(env, jname, name); + (*env)->ReleaseStringUTFChars(env, jsig, sig); + return mid; +} + +JNIEXPORT void JNICALL +Java_TestCheckedJniExceptionCheck_initMethodIds(JNIEnv *env, + jobject obj, + jstring callable_method_name, + jstring callable_method_sig, + jstring callable_nested_method_name, + jstring callable_nested_method_sig) { + jclass clz = (*env)->GetObjectClass(env, obj); + + _callable_method_id = get_method_id(env, clz, + callable_method_name, + callable_method_sig); + + _callable_nested_method_id = get_method_id(env, clz, + callable_nested_method_name, + callable_nested_method_sig); +} + +JNIEXPORT void JNICALL +Java_TestCheckedJniExceptionCheck_callJavaFromNative(JNIEnv *env, + jobject obj, + jint nofCalls, + jboolean checkExceptions) { + int i; + for (i = 0; i < nofCalls; i++) { + (*env)->CallVoidMethod(env, obj, _callable_method_id); + if (checkExceptions == JNI_TRUE) { + (*env)->ExceptionCheck(env); + } + } +} + +JNIEXPORT void JNICALL +Java_TestCheckedJniExceptionCheck_callNestedJavaFromNative(JNIEnv *env, + jobject obj, + jint nofCalls, + jboolean checkExceptions) { + int i; + for (i = 0; i < nofCalls; i++) { + (*env)->CallVoidMethod(env, obj, _callable_nested_method_id, nofCalls, checkExceptions); + if (checkExceptions == JNI_TRUE) { + (*env)->ExceptionCheck(env); + } + } +}