Skip to content

Commit 2942787

Browse files
committed
Missing methods should throw NSME
1 parent 73501b2 commit 2942787

4 files changed

Lines changed: 52 additions & 15 deletions

File tree

src/java.base/share/classes/java/lang/Class.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -41,7 +41,6 @@
4141
import java.lang.reflect.Constructor;
4242
import java.lang.reflect.Executable;
4343
import java.lang.reflect.Field;
44-
import java.lang.reflect.GenericArrayType;
4544
import java.lang.reflect.GenericDeclaration;
4645
import java.lang.reflect.GenericSignatureFormatError;
4746
import java.lang.reflect.InvocationTargetException;
@@ -1471,7 +1470,7 @@ public Method getEnclosingMethod() {
14711470
}
14721471
}
14731472

1474-
throw new InternalError("Enclosing method not found");
1473+
throw new NoSuchMethodError(enclosingCandidate.getName() + "." + enclosingInfo.getName() + enclosingInfo.getDescriptor());
14751474
}
14761475
}
14771476

@@ -1544,12 +1543,6 @@ String getDescriptor() {
15441543
}
15451544
}
15461545

1547-
private static Class<?> toClass(Type o) {
1548-
if (o instanceof GenericArrayType gat)
1549-
return toClass(gat.getGenericComponentType()).arrayType();
1550-
return (Class<?>)o;
1551-
}
1552-
15531546
/**
15541547
* If this {@code Class} object represents a local or anonymous
15551548
* class within a constructor, returns a {@link
@@ -1593,7 +1586,7 @@ public Constructor<?> getEnclosingConstructor() {
15931586
}
15941587
}
15951588

1596-
throw new InternalError("Enclosing constructor not found");
1589+
throw new NoSuchMethodError(enclosingCandidate.getName() + "." + enclosingInfo.getName() + enclosingInfo.getDescriptor());
15971590
}
15981591
}
15991592

src/java.base/share/classes/java/lang/reflect/RecordComponent.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -168,6 +168,10 @@ public AnnotatedType getAnnotatedType() {
168168
* component
169169
*/
170170
public Method getAccessor() {
171+
if (accessor == null) {
172+
// Hotspot passes null for "not found" accessors
173+
throw new NoSuchMethodError(getDeclaringRecord().getName() + "." + getName());
174+
}
171175
return accessor;
172176
}
173177

test/jdk/java/lang/Class/getEnclosingMethod/BadEnclosingMethodTest.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8350704
26+
* @bug 8350704 8381377
2727
* @summary Test behaviors with various bad EnclosingMethod attribute
2828
* @library /test/lib
2929
* @run junit BadEnclosingMethodTest
@@ -111,7 +111,7 @@ void testMalformedTypes() throws Exception {
111111
* valid, but refers to a class or interface that cannot be found.
112112
*/
113113
@Test
114-
void testAbsentMethods() throws Exception {
114+
void testAbsentTypeInMethods() throws Exception {
115115
var absentMethodType = loadTestClass("methodName", "(Ldoes/not/Exist;)V");
116116
var ex = assertThrows(TypeNotPresentException.class,
117117
absentMethodType::getEnclosingMethod);
@@ -122,8 +122,29 @@ void testAbsentMethods() throws Exception {
122122
absentConstructorType::getEnclosingConstructor);
123123
assertEquals("does.not.Exist", ex.typeName());
124124
}
125+
126+
/**
127+
* Test reflective behaviors when the EnclosingMethod attribute uses valid
128+
* class and method names and types, but the method does not exist.
129+
*/
130+
@Test
131+
void testAbsentMethods() throws Exception {
132+
var absentMethodType = loadTestClass("work", "(I)V");
133+
var ex = assertThrows(NoSuchMethodError.class,
134+
absentMethodType::getEnclosingMethod);
135+
var message = ex.getMessage();
136+
assertTrue(message.contains("Encloser.work(I)V"));
137+
138+
var absentConstructorType = loadTestClass(INIT_NAME, "(I)V");
139+
ex = assertThrows(NoSuchMethodError.class,
140+
absentConstructorType::getEnclosingConstructor);
141+
message = ex.getMessage();
142+
assertTrue(message.contains("Encloser.<init>(I)V"));
143+
}
144+
125145
}
126146

147+
// These guys are compiled to serve as templates for bytecode transformation
127148
class Encloser {
128149
private static void work() {
129150
class Enclosed {

test/jdk/java/lang/reflect/records/IsRecordTest.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8255560
26+
* @bug 8255560 8381377
2727
* @summary Class::isRecord should check that the current class is final and not abstract
2828
* @library /test/lib
2929
* @run junit/othervm IsRecordTest
@@ -156,6 +156,25 @@ final class B { }
156156
assertNull(B.class.getRecordComponents());
157157
}
158158

159+
/** Tests record component behavior with missing accessors. */
160+
@Test
161+
public void testMissingAccessor() throws Exception {
162+
var missingAccessorBytes = generateClassBytes("MissingAccessor", true, false, "java/lang/Record", List.of(RecordComponentInfo.of("x", CD_int)));
163+
var missingAccessor = ByteCodeLoader.load("MissingAccessor", missingAccessorBytes);
164+
165+
assertTrue(missingAccessor.isRecord());
166+
assertEquals(1, missingAccessor.getRecordComponents().length);
167+
168+
var component = missingAccessor.getRecordComponents()[0];
169+
assertEquals("x", component.getName());
170+
assertSame(int.class, component.getType());
171+
assertSame(int.class, component.getGenericType());
172+
assertNull(component.getGenericSignature());
173+
var nsme = assertThrows(NoSuchMethodError.class, () -> component.getAccessor());
174+
var message = nsme.getMessage();
175+
assertTrue(message.contains("MissingAccessor.x"), message);
176+
}
177+
159178
// -- infra
160179

161180
// Generates a class with the given properties.

0 commit comments

Comments
 (0)