8335638: Calling VarHandle.{access-mode} methods reflectively throws wrong exception

Reviewed-by: liach
This commit is contained in:
Hannes Greule 2024-08-01 14:41:25 +00:00 committed by Chen Liang
parent 27af80ef9e
commit 9fe6e2316a
2 changed files with 77 additions and 5 deletions

View File

@ -1360,6 +1360,18 @@ JVM_ENTRY(jobject, MH_invokeExact_UOE(JNIEnv* env, jobject mh, jobjectArray args
}
JVM_END
/**
* Throws a java/lang/UnsupportedOperationException unconditionally.
* This is required by the specification of VarHandle.{access-mode} if
* invoked directly.
*/
JVM_ENTRY(jobject, VH_UOE(JNIEnv* env, jobject vh, jobjectArray args)) {
THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "VarHandle access mode methods cannot be invoked reflectively");
return nullptr;
}
JVM_END
/// JVM_RegisterMethodHandleMethods
#define LANG "Ljava/lang/"
@ -1399,6 +1411,40 @@ static JNINativeMethod MH_methods[] = {
{CC "invoke", CC "([" OBJ ")" OBJ, FN_PTR(MH_invoke_UOE)},
{CC "invokeExact", CC "([" OBJ ")" OBJ, FN_PTR(MH_invokeExact_UOE)}
};
static JNINativeMethod VH_methods[] = {
// UnsupportedOperationException throwers
{CC "get", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "set", CC "([" OBJ ")V", FN_PTR(VH_UOE)},
{CC "getVolatile", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "setVolatile", CC "([" OBJ ")V", FN_PTR(VH_UOE)},
{CC "getAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "setRelease", CC "([" OBJ ")V", FN_PTR(VH_UOE)},
{CC "getOpaque", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "setOpaque", CC "([" OBJ ")V", FN_PTR(VH_UOE)},
{CC "compareAndSet", CC "([" OBJ ")Z", FN_PTR(VH_UOE)},
{CC "compareAndExchange", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "compareAndExchangeAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "compareAndExchangeRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "weakCompareAndSetPlain", CC "([" OBJ ")Z", FN_PTR(VH_UOE)},
{CC "weakCompareAndSet", CC "([" OBJ ")Z", FN_PTR(VH_UOE)},
{CC "weakCompareAndSetAcquire", CC "([" OBJ ")Z", FN_PTR(VH_UOE)},
{CC "weakCompareAndSetRelease", CC "([" OBJ ")Z", FN_PTR(VH_UOE)},
{CC "getAndSet", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndSetAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndSetRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndAdd", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndAddAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndAddRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseOr", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseOrAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseOrRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseAnd", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseAndAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseAndRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseXor", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseXorAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)},
{CC "getAndBitwiseXorRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}
};
/**
* This one function is exported, used by NativeLookup.
@ -1406,9 +1452,12 @@ static JNINativeMethod MH_methods[] = {
JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
assert(!MethodHandles::enabled(), "must not be enabled");
assert(vmClasses::MethodHandle_klass() != nullptr, "should be present");
assert(vmClasses::VarHandle_klass() != nullptr, "should be present");
oop mirror = vmClasses::MethodHandle_klass()->java_mirror();
jclass MH_class = (jclass) JNIHandles::make_local(THREAD, mirror);
oop mh_mirror = vmClasses::MethodHandle_klass()->java_mirror();
oop vh_mirror = vmClasses::VarHandle_klass()->java_mirror();
jclass MH_class = (jclass) JNIHandles::make_local(THREAD, mh_mirror);
jclass VH_class = (jclass) JNIHandles::make_local(THREAD, vh_mirror);
{
ThreadToNativeFromVM ttnfv(thread);
@ -1420,6 +1469,10 @@ JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class))
status = env->RegisterNatives(MH_class, MH_methods, sizeof(MH_methods)/sizeof(JNINativeMethod));
guarantee(status == JNI_OK && !env->ExceptionOccurred(),
"register java.lang.invoke.MethodHandle natives");
status = env->RegisterNatives(VH_class, VH_methods, sizeof(VH_methods)/sizeof(JNINativeMethod));
guarantee(status == JNI_OK && !env->ExceptionOccurred(),
"register java.lang.invoke.VarHandle natives");
}
log_debug(methodhandles, indy)("MethodHandle support loaded (using LambdaForms)");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 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,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleInfo;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.stream.Stream;
@ -52,15 +53,33 @@ public class VarHandleTestReflection extends VarHandleBaseTest {
}
@Test(dataProvider = "accessModesProvider", expectedExceptions = IllegalArgumentException.class)
public void methodInvocation(VarHandle.AccessMode accessMode) throws Exception {
public void methodInvocationArgumentMismatch(VarHandle.AccessMode accessMode) throws Exception {
VarHandle v = handle();
// Try a reflective invoke using a Method
// Try a reflective invoke using a Method, with no arguments
Method vhm = VarHandle.class.getMethod(accessMode.methodName(), Object[].class);
vhm.invoke(v, new Object[]{});
}
@Test(dataProvider = "accessModesProvider")
public void methodInvocationMatchingArguments(VarHandle.AccessMode accessMode) throws Exception {
VarHandle v = handle();
// Try a reflective invoke using a Method, with the minimal required arguments
Method vhm = VarHandle.class.getMethod(accessMode.methodName(), Object[].class);
Object arg = new Object[0];
try {
vhm.invoke(v, arg);
} catch (InvocationTargetException e) {
if (!(e.getCause() instanceof UnsupportedOperationException)) {
throw new RuntimeException("expected UnsupportedOperationException but got: "
+ e.getCause().getClass().getName(), e);
}
}
}
@Test(dataProvider = "accessModesProvider", expectedExceptions = UnsupportedOperationException.class)
public void methodHandleInvoke(VarHandle.AccessMode accessMode) throws Throwable {
VarHandle v = handle();