Skip to content

Commit 09d8083

Browse files
authored
A crude sketch for Lookup.unreflectDeconstructor (openjdk#7)
* A crude sketch of unreflect for deconstructors. * Reuse the same mangling everywhere. * Cleanup.
1 parent 84d94ff commit 09d8083

File tree

5 files changed

+117
-1
lines changed

5 files changed

+117
-1
lines changed

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
6969
import static java.lang.invoke.MethodHandleStatics.newInternalError;
7070
import static java.lang.invoke.MethodType.methodType;
71+
import java.lang.reflect.Deconstructor;
72+
import java.lang.runtime.Carriers;
7173

7274
/**
7375
* This class consists exclusively of static methods that operate on or return
@@ -3507,6 +3509,53 @@ public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessE
35073509
return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor);
35083510
}
35093511

3512+
/**
3513+
* Produces a method handle for a reflected deconstructor.
3514+
* TBD
3515+
* @param d the reflected deconstructor
3516+
* @return a method handle which can invoke the reflected deconstructor
3517+
* @throws IllegalAccessException if access checking fails
3518+
* @throws NullPointerException if the argument is null
3519+
*/
3520+
public MethodHandle unreflectDeconstructor(Deconstructor<?> d) throws IllegalAccessException {
3521+
try {
3522+
String mangled = SharedSecrets.getJavaLangReflectAccess().getMangledName(d);
3523+
Method deconstructorMethod = d.getDeclaringClass().getDeclaredMethod(mangled, d.getDeclaringClass());
3524+
MethodHandle deconstructorPhysicalHandle = unreflect(deconstructorMethod);
3525+
List<Class<?>> methodTypeTypes = new ArrayList<>();
3526+
methodTypeTypes.add(Object.class);
3527+
MethodType bindingMethodType = MethodType.methodType(Object.class, Arrays.stream(d.getPatternBindings())
3528+
.map(b -> b.getType())
3529+
.toArray(Class[]::new));
3530+
return MethodHandles.filterReturnValue(deconstructorPhysicalHandle, MethodHandles.insertArguments(CARRIER_TO_ARRAY, 1, Carriers.components(bindingMethodType)));
3531+
} catch (NoSuchMethodException | SecurityException ex) {
3532+
throw new InternalError(ex);
3533+
}
3534+
}
3535+
3536+
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
3537+
private static MethodHandle CARRIER_TO_ARRAY;
3538+
3539+
static {
3540+
try {
3541+
CARRIER_TO_ARRAY = LOOKUP.findStatic(Lookup.class, "carrier2Array",
3542+
MethodType.methodType(Object[].class, Object.class, List.class));
3543+
} catch (ReflectiveOperationException e) {
3544+
throw new ExceptionInInitializerError(e);
3545+
}
3546+
}
3547+
3548+
private static Object[] carrier2Array(Object carrier, List<MethodHandle> componentHandles) throws Throwable {
3549+
Object[] result = new Object[componentHandles.size()];
3550+
int i = 0;
3551+
3552+
for (MethodHandle componentAccessor : componentHandles) {
3553+
result[i++] = componentAccessor.invoke(carrier);
3554+
}
3555+
3556+
return result;
3557+
}
3558+
35103559
/*
35113560
* Produces a method handle that is capable of creating instances of the given class
35123561
* and instantiated by the given constructor. No security manager check.

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ public int getPatternFlags() {
423423
public Object[] invoke(Object matchCandidate)
424424
throws IllegalAccessException, MatchException
425425
{
426-
String underlyingName = mangle(this.getDeclaringClass(), Arrays.stream(getPatternBindings()).map(pb -> pb.getType()).toArray(Class[]::new));
426+
String underlyingName = getMangledName();
427427

428428
try {
429429
Method method = this.getDeclaringClass().getDeclaredMethod(underlyingName, matchCandidate.getClass());
@@ -472,4 +472,8 @@ public Annotation[] getDeclaredAnnotations() {
472472
public AnnotatedType getAnnotatedReturnType() {
473473
return null;
474474
}
475+
476+
String getMangledName() {
477+
return mangle(this.getDeclaringClass(), Arrays.stream(getPatternBindings()).map(pb -> pb.getType()).toArray(Class[]::new));
478+
}
475479
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,9 @@ public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
131131
{
132132
return ctor.newInstanceWithCaller(args, true, caller);
133133
}
134+
135+
@Override
136+
public String getMangledName(Deconstructor<?> d) {
137+
return d.getMangledName();
138+
}
134139
}

src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,6 @@ public void setConstructorAccessor(Constructor<?> c,
105105
/** Returns a new instance created by the given constructor with access check */
106106
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
107107
throws IllegalAccessException, InstantiationException, InvocationTargetException;
108+
109+
public String getMangledName(Deconstructor<?> d);
108110
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @summary Verify unreflect works for deconstructors
26+
* @enablePreview
27+
* @compile UnreflectPattern.java
28+
* @run main UnreflectPattern
29+
*/
30+
31+
import java.lang.invoke.MethodHandle;
32+
import java.lang.invoke.MethodHandles;
33+
import java.lang.reflect.Deconstructor;
34+
import java.util.Arrays;
35+
36+
public class UnreflectPattern {
37+
38+
public static void main(String... args) throws Throwable {
39+
Object[] expected = new Object[] {"correct", -1};
40+
Object instance = new UnreflectPattern();
41+
Deconstructor<?> deconstructor = UnreflectPattern.class.getDeclaredDeconstructor(String.class, int.class);
42+
Object[] result1 = deconstructor.invoke(instance);
43+
if (!Arrays.equals(expected, result1)) {
44+
throw new AssertionError("Unexpected result: " + Arrays.toString(result1));
45+
}
46+
MethodHandle deconstructorHandle = MethodHandles.lookup().unreflectDeconstructor(deconstructor);
47+
Object[] result2 = (Object[]) deconstructorHandle.invoke(instance);
48+
if (!Arrays.equals(expected, result2)) {
49+
throw new AssertionError("Unexpected result: " + Arrays.toString(result2));
50+
}
51+
}
52+
53+
public pattern UnreflectPattern(String s, int i) {
54+
match UnreflectPattern("correct", -1);
55+
}
56+
}

0 commit comments

Comments
 (0)