diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index fd10f0723bff0..8465eec308bf3 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -1363,6 +1363,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/" @@ -1402,6 +1414,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. @@ -1409,9 +1455,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); @@ -1423,6 +1472,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)"); diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java index 652272ca8a8f4..b20e18d263edd 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java @@ -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.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 @@ static VarHandle handle() throws Exception { } @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();