mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-04 05:00:31 +00:00
8378792: ObjectMethods.bootstrap missing getter validation
Reviewed-by: rriggs, jvernee
This commit is contained in:
parent
5606036793
commit
1fb608e1bc
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 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
|
||||
@ -363,9 +363,9 @@ public final class ObjectMethods {
|
||||
* @return the method handle
|
||||
*/
|
||||
private static MethodHandle makeToString(MethodHandles.Lookup lookup,
|
||||
Class<?> receiverClass,
|
||||
MethodHandle[] getters,
|
||||
List<String> names) {
|
||||
Class<?> receiverClass,
|
||||
MethodHandle[] getters,
|
||||
List<String> names) {
|
||||
assert getters.length == names.size();
|
||||
if (getters.length == 0) {
|
||||
// special case
|
||||
@ -516,8 +516,8 @@ public final class ObjectMethods {
|
||||
requireNonNull(type);
|
||||
requireNonNull(recordClass);
|
||||
requireNonNull(names);
|
||||
requireNonNull(getters);
|
||||
Arrays.stream(getters).forEach(Objects::requireNonNull);
|
||||
List<MethodHandle> getterList = List.of(getters); // deep null check
|
||||
|
||||
MethodType methodType;
|
||||
if (type instanceof MethodType mt)
|
||||
methodType = mt;
|
||||
@ -526,7 +526,14 @@ public final class ObjectMethods {
|
||||
if (!MethodHandle.class.equals(type))
|
||||
throw new IllegalArgumentException(type.toString());
|
||||
}
|
||||
List<MethodHandle> getterList = List.of(getters);
|
||||
|
||||
for (MethodHandle getter : getterList) {
|
||||
var getterType = getter.type();
|
||||
if (getterType.parameterCount() != 1 || getterType.returnType() == void.class || getterType.parameterType(0) != recordClass) {
|
||||
throw new IllegalArgumentException("Illegal getter type %s for recordClass %s".formatted(getterType, recordClass.getTypeName()));
|
||||
}
|
||||
}
|
||||
|
||||
MethodHandle handle = switch (methodName) {
|
||||
case "equals" -> {
|
||||
if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
|
||||
@ -541,7 +548,7 @@ public final class ObjectMethods {
|
||||
case "toString" -> {
|
||||
if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass)))
|
||||
throw new IllegalArgumentException("Bad method type: " + methodType);
|
||||
List<String> nameList = "".equals(names) ? List.of() : List.of(names.split(";"));
|
||||
List<String> nameList = names.isEmpty() ? List.of() : List.of(names.split(";"));
|
||||
if (nameList.size() != getterList.size())
|
||||
throw new IllegalArgumentException("Name list and accessor list do not match");
|
||||
yield makeToString(lookup, recordClass, getters, nameList);
|
||||
|
||||
@ -36,11 +36,11 @@ import java.lang.invoke.MethodType;
|
||||
import java.lang.runtime.ObjectMethods;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ObjectMethodsTest {
|
||||
|
||||
@ -144,8 +144,8 @@ public class ObjectMethodsTest {
|
||||
assertEquals("Empty[]", (String)handle.invokeExact(new Empty()));
|
||||
}
|
||||
|
||||
Class<NullPointerException> NPE = NullPointerException.class;
|
||||
Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
|
||||
private static final Class<NullPointerException> NPE = NullPointerException.class;
|
||||
private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
|
||||
|
||||
@Test
|
||||
public void exceptions() {
|
||||
@ -157,25 +157,60 @@ public class ObjectMethodsTest {
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC, C.class, "x;y", C.ACCESSORS));
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS));
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS));
|
||||
}
|
||||
|
||||
record NamePlusType(String mn, MethodType mt) {}
|
||||
List<NamePlusType> namePlusTypeList = List.of(
|
||||
record NamePlusType(String name, MethodType type) {}
|
||||
|
||||
static List<NamePlusType> namePlusTypeList() {
|
||||
return List.of(
|
||||
new NamePlusType("toString", C.TO_STRING_DESC),
|
||||
new NamePlusType("equals", C.EQUALS_DESC),
|
||||
new NamePlusType("hashCode", C.HASHCODE_DESC)
|
||||
);
|
||||
|
||||
for (NamePlusType npt : namePlusTypeList) {
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", null));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", new MethodHandle[]{null}));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, null, C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), null, "x;y", C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), null, C.class, "x;y", C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, npt.mt(), C.class, "x;y", C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(), npt.mt(), C.class, "x;y", C.ACCESSORS));
|
||||
}
|
||||
}
|
||||
|
||||
@MethodSource("namePlusTypeList")
|
||||
@ParameterizedTest
|
||||
void commonExceptions(NamePlusType npt) {
|
||||
String name = npt.name();
|
||||
MethodType type = npt.type();
|
||||
|
||||
// Null checks
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", null));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", new MethodHandle[]{null}));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, null, C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, null, "x;y", C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, null, C.class, "x;y", C.ACCESSORS));
|
||||
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));
|
||||
|
||||
// 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));
|
||||
|
||||
// Bad getter types
|
||||
var wrongReceiverGetter = assertDoesNotThrow(() -> MethodHandles.lookup().findGetter(this.getClass(), "y", int.class));
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y",
|
||||
new MethodHandle[]{
|
||||
C.ACCESSORS[0],
|
||||
wrongReceiverGetter,
|
||||
}));
|
||||
var extraArgGetter = MethodHandles.dropArguments(C.ACCESSORS[1], 1, int.class);
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y",
|
||||
new MethodHandle[]{
|
||||
C.ACCESSORS[0],
|
||||
extraArgGetter,
|
||||
}));
|
||||
var voidReturnGetter = MethodHandles.empty(MethodType.methodType(void.class, C.class));
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y",
|
||||
new MethodHandle[]{
|
||||
C.ACCESSORS[0],
|
||||
voidReturnGetter,
|
||||
}));
|
||||
}
|
||||
|
||||
// same field name and type as C::y, for wrongReceiverGetter
|
||||
private int y;
|
||||
|
||||
// Based on the ObjectMethods internal implementation
|
||||
private static int hashCombiner(int x, int y) {
|
||||
return x*31 + y;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user