Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
Original file line number Diff line number Diff line change
Expand Up @@ -479,12 +479,7 @@ private static List<List<MethodHandle>> split(MethodHandle[] getters) {
* {@link java.lang.Record#toString()}.
*
*
* @param lookup Every bootstrap method is expected to have a {@code lookup}
* which usually represents a lookup context with the
* accessibility privileges of the caller. This is because
* {@code invokedynamic} call sites always provide a {@code lookup}
* to the corresponding bootstrap method, but this method just
* ignores the {@code lookup} parameter
* @param lookup the full-privilege lookup context of the caller
* @param methodName the name of the method to generate, which must be one of
* {@code "equals"}, {@code "hashCode"}, or {@code "toString"}
* @param type a {@link MethodType} corresponding the descriptor type
Expand All @@ -503,8 +498,6 @@ private static List<List<MethodHandle>> split(MethodHandle[] getters) {
* if invoked by a condy
* @throws IllegalArgumentException if the bootstrap arguments are invalid
* or inconsistent
* @throws NullPointerException if any argument is {@code null} or if any element
* in the {@code getters} array is {@code null}
* @throws Throwable if any exception is thrown during call site construction
*/
public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
Expand All @@ -518,6 +511,9 @@ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, T
requireNonNull(names);
List<MethodHandle> getterList = List.of(getters); // deep null check

if (!lookup.hasFullPrivilegeAccess())
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));

MethodType methodType;
if (type instanceof MethodType mt)
methodType = mt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,13 @@ private static class StaticHolders {
* the length of the {@code labels} array (inclusive),
* both or an {@link IndexOutOfBoundsException} is thrown.
*
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
* @param lookup the full-privilege lookup context of the caller
* @param invocationName unused, {@code null} is permitted
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
* a target type, an {@code int}, and {@code int} as a return type.
* @param labels case labels as described above
* @return a {@code CallSite} returning the first matching element as described above
*
* @throws NullPointerException if any argument is {@code null}, unless noted otherwise
* @throws IllegalArgumentException if any element in the labels array is null
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a target type,
* second parameter of type {@code int} and with {@code int} as its return type
Expand All @@ -198,6 +195,9 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup,
requireNonNull(invocationType);
requireNonNull(labels);

if (!lookup.hasFullPrivilegeAccess())
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));

Class<?> selectorType = invocationType.parameterType(0);
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
Expand Down Expand Up @@ -275,17 +275,14 @@ private static void verifyLabel(Object label, Class<?> selectorType) {
* @apiNote It is permissible for the {@code labels} array to contain {@code String}
* values that do not represent any enum constants at runtime.
*
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
* @param lookup the full-privilege lookup context of the caller
* @param invocationName unused, {@code null} is permitted
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
* an enum type, an {@code int}, and {@code int} as a return type.
* @param labels case labels - {@code String} constants and {@code Class} instances,
* in any combination
* @return a {@code CallSite} returning the first matching element as described above
*
* @throws NullPointerException if any argument is {@code null}, unless noted otherwise
* @throws IllegalArgumentException if any element in the labels array is null
* @throws IllegalArgumentException if any element in the labels array is an empty {@code String}
* @throws IllegalArgumentException if the invocation type is not a method type
Expand All @@ -305,6 +302,9 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup,
requireNonNull(invocationType);
requireNonNull(labels);

if (!lookup.hasFullPrivilegeAccess())
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));

if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| invocationType.parameterType(0).isPrimitive()
Expand Down
14 changes: 13 additions & 1 deletion src/java.base/share/classes/java/lang/runtime/package-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
Expand All @@ -26,8 +26,20 @@
/**
* The {@code java.lang.runtime} package provides low-level runtime support
* for the Java language.
* <p>
* Unless otherwise specified: <ul>
* <li>Methods and constructors in this package throw a {@link
* NullPointerException} when they are called with {@code null} or an array
* that contains {@code null} as an argument.
* <li>{@linkplain java.lang.invoke##bsm Bootstrap methods} in this package
* throw an {@link IllegalArgumentException} when they are called with a
* {@link Lookup Lookup} that does not have {@linkplain
* Lookup#hasFullPrivilegeAccess() full privilege access}.
* </ul>
*
* @since 14
*/

package java.lang.runtime;

import java.lang.invoke.MethodHandles.Lookup;
4 changes: 4 additions & 0 deletions test/jdk/java/lang/runtime/ObjectMethodsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static class Empty {
}

static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
static final MethodHandles.Lookup UNPRIVILEGED_LOOKUP = LOOKUP.dropLookupMode(MethodHandles.Lookup.PRIVATE);

@Test
public void testEqualsC() throws Throwable {
Expand Down Expand Up @@ -184,6 +185,9 @@ void commonExceptions(NamePlusType npt) {
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS));
assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS));

// Unprivileged lookup
assertThrows(IAE, () -> ObjectMethods.bootstrap(UNPRIVILEGED_LOOKUP, name, type, C.class, "x;y", C.ACCESSORS));

// Bad indy call receiver type - change C to this test class
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS));

Expand Down
75 changes: 38 additions & 37 deletions test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,40 +42,25 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
/*
* @test
* @bug 8318144
* @enablePreview
* @compile SwitchBootstrapsTest.java
* @run junit/othervm SwitchBootstrapsTest
* @run junit SwitchBootstrapsTest
*/
public class SwitchBootstrapsTest {

public static final MethodHandle BSM_TYPE_SWITCH;
public static final MethodHandle BSM_ENUM_SWITCH;

static {
try {
BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch",
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
}
catch (ReflectiveOperationException e) {
throw new AssertionError("Should not happen", e);
}
}

private void testType(Object target, int start, int result, Object... labels) throws Throwable {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
assertEquals(result, (int) indy.invoke(target, start));
assertEquals(-1, (int) indy.invoke(null, start));
}

private void testPrimitiveType(Object target, Class<?> targetType, int start, int result, Object... labels) throws Throwable {
MethodType switchType = MethodType.methodType(int.class, targetType, int.class);
MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
assertEquals(result, (int) indy.invoke(target, start));
}

Expand All @@ -85,7 +70,7 @@ private void testEnum(Enum<?> target, int start, int result, Object... labels) t

private void testEnum(Class<?> targetClass, Enum<?> target, int start, int result, Object... labels) throws Throwable {
MethodType switchType = MethodType.methodType(int.class, targetClass, int.class);
MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
assertEquals(result, (int) indy.invoke(target, start));
assertEquals(-1, (int) indy.invoke(null, start));
}
Expand Down Expand Up @@ -188,7 +173,7 @@ public void testEnums() throws Throwable {

//null invocation name:
MethodType switchType = MethodType.methodType(int.class, E1.class, int.class);
MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker();
MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), null, switchType).dynamicInvoker();
assertEquals(0, (int) indy.invoke(E1.A, 0));
}

Expand Down Expand Up @@ -229,7 +214,7 @@ public void testWrongSwitchTypes() throws Throwable {
};
for (MethodType switchType : switchTypes) {
assertThrows(IllegalArgumentException.class, () ->
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType)
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType)
);
}
MethodType[] enumSwitchTypes = new MethodType[] {
Expand All @@ -240,7 +225,7 @@ public void testWrongSwitchTypes() throws Throwable {
};
for (MethodType enumSwitchType : enumSwitchTypes) {
assertThrows(IllegalArgumentException.class, () ->
BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType)
SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType)
);
}
}
Expand Down Expand Up @@ -270,23 +255,23 @@ enum E {A, B, C}
public void testNullLabels() throws Throwable {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
assertThrows(NullPointerException.class, () ->
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null)
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, (Object[]) null)
);
assertThrows(IllegalArgumentException.class, () ->
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType,
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType,
new Object[] {1, null, String.class})
);
MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class);
assertThrows(NullPointerException.class, () ->
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null)
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null)
);
assertThrows(IllegalArgumentException.class, () ->
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType,
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType,
new Object[] {1, null, String.class})
);
//null invocationName is OK:
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType,
new Object[] {Object.class});
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), null, switchType,
Object.class);
}

private static AtomicBoolean enumInitialized = new AtomicBoolean();
Expand All @@ -304,7 +289,7 @@ enum E {

MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class);

CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"});
CallSite invocation = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType, "A");
assertFalse(enumInitialized.get());
assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0));
assertFalse(enumInitialized.get());
Expand All @@ -330,7 +315,7 @@ enum E {
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
"test"
};
CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels);
CallSite invocation = (CallSite) SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels);
assertFalse(enumInitialized.get());
assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0));
assertFalse(enumInitialized.get());
Expand Down Expand Up @@ -402,21 +387,37 @@ private static byte[] createClass() {
}

@Test
public void testNullLookup() throws Throwable {
public void testNullLookup() {
assertThrows(NullPointerException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class);
SwitchBootstraps.typeSwitch(null, "", switchType, Object.class);
});
enum E {}
assertThrows(NullPointerException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
BSM_ENUM_SWITCH.invoke(null, "", switchType,
new Object[] {});
SwitchBootstraps.enumSwitch(null, "", switchType);
});
assertThrows(NullPointerException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
BSM_ENUM_SWITCH.invoke(null, "", switchType,
new Object[] {"A"});
SwitchBootstraps.enumSwitch(null, "", switchType, "A");
});
}

@Test
public void testUnprivilegedLookup() {
var lookup = MethodHandles.lookup().dropLookupMode(MethodHandles.Lookup.PRIVATE);
assertThrows(IllegalArgumentException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
SwitchBootstraps.typeSwitch(lookup, "", switchType, Object.class);
});
enum E {}
assertThrows(IllegalArgumentException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
SwitchBootstraps.enumSwitch(lookup, "", switchType);
});
assertThrows(IllegalArgumentException.class, () -> {
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
SwitchBootstraps.enumSwitch(lookup, "", switchType, "A");
});
}
}