Skip to content

Commit

Permalink
A crude sketch for Lookup.unreflectDeconstructor (#7)
Browse files Browse the repository at this point in the history
* A crude sketch of unreflect for deconstructors.

* Reuse the same mangling everywhere.

* Cleanup.
  • Loading branch information
lahodaj authored Aug 30, 2024
1 parent 84d94ff commit 09d8083
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 1 deletion.
49 changes: 49 additions & 0 deletions src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
import static java.lang.invoke.MethodHandleStatics.newInternalError;
import static java.lang.invoke.MethodType.methodType;
import java.lang.reflect.Deconstructor;
import java.lang.runtime.Carriers;

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

/**
* Produces a method handle for a reflected deconstructor.
* TBD
* @param d the reflected deconstructor
* @return a method handle which can invoke the reflected deconstructor
* @throws IllegalAccessException if access checking fails
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectDeconstructor(Deconstructor<?> d) throws IllegalAccessException {
try {
String mangled = SharedSecrets.getJavaLangReflectAccess().getMangledName(d);
Method deconstructorMethod = d.getDeclaringClass().getDeclaredMethod(mangled, d.getDeclaringClass());
MethodHandle deconstructorPhysicalHandle = unreflect(deconstructorMethod);
List<Class<?>> methodTypeTypes = new ArrayList<>();
methodTypeTypes.add(Object.class);
MethodType bindingMethodType = MethodType.methodType(Object.class, Arrays.stream(d.getPatternBindings())
.map(b -> b.getType())
.toArray(Class[]::new));
return MethodHandles.filterReturnValue(deconstructorPhysicalHandle, MethodHandles.insertArguments(CARRIER_TO_ARRAY, 1, Carriers.components(bindingMethodType)));
} catch (NoSuchMethodException | SecurityException ex) {
throw new InternalError(ex);
}
}

private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodHandle CARRIER_TO_ARRAY;

static {
try {
CARRIER_TO_ARRAY = LOOKUP.findStatic(Lookup.class, "carrier2Array",
MethodType.methodType(Object[].class, Object.class, List.class));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}

private static Object[] carrier2Array(Object carrier, List<MethodHandle> componentHandles) throws Throwable {
Object[] result = new Object[componentHandles.size()];
int i = 0;

for (MethodHandle componentAccessor : componentHandles) {
result[i++] = componentAccessor.invoke(carrier);
}

return result;
}

/*
* Produces a method handle that is capable of creating instances of the given class
* and instantiated by the given constructor. No security manager check.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ public int getPatternFlags() {
public Object[] invoke(Object matchCandidate)
throws IllegalAccessException, MatchException
{
String underlyingName = mangle(this.getDeclaringClass(), Arrays.stream(getPatternBindings()).map(pb -> pb.getType()).toArray(Class[]::new));
String underlyingName = getMangledName();

try {
Method method = this.getDeclaringClass().getDeclaredMethod(underlyingName, matchCandidate.getClass());
Expand Down Expand Up @@ -472,4 +472,8 @@ public Annotation[] getDeclaredAnnotations() {
public AnnotatedType getAnnotatedReturnType() {
return null;
}

String getMangledName() {
return mangle(this.getDeclaringClass(), Arrays.stream(getPatternBindings()).map(pb -> pb.getType()).toArray(Class[]::new));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,9 @@ public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
{
return ctor.newInstanceWithCaller(args, true, caller);
}

@Override
public String getMangledName(Deconstructor<?> d) {
return d.getMangledName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,6 @@ public void setConstructorAccessor(Constructor<?> c,
/** Returns a new instance created by the given constructor with access check */
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
throws IllegalAccessException, InstantiationException, InvocationTargetException;

public String getMangledName(Deconstructor<?> d);
}
56 changes: 56 additions & 0 deletions test/jdk/java/lang/invoke/lookup/UnreflectPattern.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/* @test
* @summary Verify unreflect works for deconstructors
* @enablePreview
* @compile UnreflectPattern.java
* @run main UnreflectPattern
*/

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Deconstructor;
import java.util.Arrays;

public class UnreflectPattern {

public static void main(String... args) throws Throwable {
Object[] expected = new Object[] {"correct", -1};
Object instance = new UnreflectPattern();
Deconstructor<?> deconstructor = UnreflectPattern.class.getDeclaredDeconstructor(String.class, int.class);
Object[] result1 = deconstructor.invoke(instance);
if (!Arrays.equals(expected, result1)) {
throw new AssertionError("Unexpected result: " + Arrays.toString(result1));
}
MethodHandle deconstructorHandle = MethodHandles.lookup().unreflectDeconstructor(deconstructor);
Object[] result2 = (Object[]) deconstructorHandle.invoke(instance);
if (!Arrays.equals(expected, result2)) {
throw new AssertionError("Unexpected result: " + Arrays.toString(result2));
}
}

public pattern UnreflectPattern(String s, int i) {
match UnreflectPattern("correct", -1);
}
}

0 comments on commit 09d8083

Please sign in to comment.