forked from openjdk/amber
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Invoke Patterns through PatternBootstraps
- Loading branch information
Showing
5 changed files
with
307 additions
and
2 deletions.
There are no files selected for viewing
172 changes: 172 additions & 0 deletions
172
src/java.base/share/classes/java/lang/runtime/PatternBootstraps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
/* | ||
* Copyright (c) 2017, 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. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* 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. | ||
*/ | ||
|
||
package java.lang.runtime; | ||
|
||
import jdk.internal.constant.ConstantUtils; | ||
import jdk.internal.constant.ReferenceClassDescImpl; | ||
import jdk.internal.misc.PreviewFeatures; | ||
|
||
import java.lang.Enum.EnumDesc; | ||
import java.lang.classfile.ClassFile; | ||
import java.lang.constant.ClassDesc; | ||
import java.lang.constant.MethodTypeDesc; | ||
import java.lang.invoke.*; | ||
import java.lang.reflect.*; | ||
import java.security.AccessController; | ||
import java.security.PrivilegedAction; | ||
import java.util.*; | ||
import java.util.function.BiPredicate; | ||
import java.util.stream.Collectors; | ||
|
||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; | ||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; | ||
import static java.util.Objects.requireNonNull; | ||
|
||
/** | ||
* Bootstrap methods for linking {@code invokedynamic} call sites that implement | ||
* the selection functionality of the {@code switch} statement. The bootstraps | ||
* take additional static arguments corresponding to the {@code case} labels | ||
* of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}. | ||
* | ||
* @since 21 | ||
*/ | ||
public class PatternBootstraps { | ||
|
||
private PatternBootstraps() {} | ||
|
||
private static final Object SENTINEL = new Object(); | ||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); | ||
private static final boolean previewEnabled = PreviewFeatures.isEnabled(); | ||
|
||
private static class StaticHolders { | ||
private static final MethodHandle SYNTHETIC_PATTERN; | ||
|
||
static { | ||
try { | ||
SYNTHETIC_PATTERN = LOOKUP.findStatic(PatternBootstraps.class, "syntheticPattern", | ||
MethodType.methodType(Object.class, Class.class, Object.class)); | ||
} | ||
catch (ReflectiveOperationException e) { | ||
throw new ExceptionInInitializerError(e); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Bootstrap method for linking an {@code invokedynamic} call site that | ||
* implements a pattern invocation on a target of a reference type. | ||
* <p> | ||
* If the reference type is a record class without a deconstructor then a deconstructor is synthesized from the | ||
* accessors of the method, otherwise the discovered deconstructor is invoked. | ||
* <p> | ||
* The static arguments are the component types of the record or the binding types if its a deconstructor pattern. | ||
* <p> | ||
* The type of the returned {@code CallSite}'s method handle will have | ||
* a return type of {@code Object} (the Carrier Object). | ||
* It has one parameter: the sole argument will be an {@code Object} instance ({@code target}). | ||
* <p> | ||
* If the {@code target} is {@code null}, then the method of the call site | ||
* returns {@literal -1}. | ||
* | ||
* @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 invocationName unused | ||
* @param invocationType The invocation type of the {@code CallSite} with one parameter, | ||
* a reference type, and an {@code Object} as a return type. | ||
* @param mangledName The mangled name of the method declaration that will act as a pattern. | ||
* | ||
* @return a {@code CallSite} returning the first matching element as described above | ||
* | ||
* @throws NullPointerException if any argument is {@code null} | ||
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a reference type, | ||
* and with {@code Object} as its return type, | ||
* | ||
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure | ||
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures | ||
*/ | ||
public static CallSite invokePattern(MethodHandles.Lookup lookup, | ||
String invocationName, | ||
MethodType invocationType, | ||
String mangledName) { | ||
Class<?> selectorType = invocationType.parameterType(0); | ||
if (invocationType.parameterCount() != 1 | ||
|| (!invocationType.returnType().equals(Object.class))) | ||
throw new IllegalArgumentException("Illegal invocation type " + invocationType); | ||
|
||
try { | ||
MethodHandle target = lookup.findStatic(selectorType, | ||
mangledName, | ||
MethodType.methodType( | ||
Object.class, selectorType)); | ||
return new ConstantCallSite(target); | ||
} catch (Throwable t) { | ||
MethodHandle methodHandle = MethodHandles.insertArguments(StaticHolders.SYNTHETIC_PATTERN, 0, selectorType); | ||
|
||
return new ConstantCallSite(methodHandle); | ||
} | ||
} | ||
|
||
/** | ||
* Returns a carrier, initialized with the extracted data according to the record component list of | ||
* {@code matchCandidateType}. | ||
* | ||
* @param matchCandidateInstance the receiver of a pattern | ||
* @param matchCandidateType the type of the match candidate | ||
* @return initialized carrier object | ||
* | ||
* @throws Throwable throws if invocation of synthetic pattern fails | ||
*/ | ||
@SuppressWarnings("removal") | ||
private static Object syntheticPattern(Class<?> matchCandidateType, Object matchCandidateInstance) throws Throwable { | ||
final RecordComponent[] components = AccessController.doPrivileged( | ||
(PrivilegedAction<RecordComponent[]>) matchCandidateType::getRecordComponents); | ||
|
||
Class<?>[] ctypes = Arrays.stream(components).map(c -> c.getType()).toArray(Class<?>[]::new); | ||
|
||
Carriers.CarrierElements carrierElements = Carriers.CarrierFactory.of(ctypes); | ||
|
||
MethodHandle initializingConstructor = carrierElements.initializingConstructor(); | ||
|
||
Object[] extracted = Arrays.stream(components).map(c -> { | ||
try { | ||
Method accessor = c.getAccessor(); | ||
accessor.setAccessible(true); | ||
return accessor.invoke(matchCandidateInstance); | ||
} catch (IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} catch (InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}).toArray(); | ||
|
||
MethodHandle spreadedPatternInvoker = initializingConstructor.asSpreader(Object[].class, ctypes.length); | ||
|
||
Object carrier = spreadedPatternInvoker.invoke(extracted); | ||
|
||
return carrier; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (c) 2021, 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. | ||
*/ | ||
|
||
import org.testng.annotations.Test; | ||
|
||
import java.io.Serializable; | ||
import java.lang.Enum.EnumDesc; | ||
import java.lang.classfile.ClassFile; | ||
import java.lang.constant.ClassDesc; | ||
import java.lang.constant.ConstantDescs; | ||
import java.lang.constant.MethodTypeDesc; | ||
import java.lang.invoke.CallSite; | ||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodHandles; | ||
import java.lang.invoke.MethodType; | ||
import java.lang.reflect.AccessFlag; | ||
import java.lang.runtime.Carriers; | ||
import java.lang.runtime.PatternBootstraps; | ||
import java.lang.runtime.SwitchBootstraps; | ||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import static org.testng.Assert.*; | ||
|
||
/** | ||
* @test | ||
* @bug 8318144 | ||
* @enablePreview | ||
* @modules java.base/jdk.internal.classfile | ||
* @run testng/othervm PatternBootstrapsTest | ||
*/ | ||
@Test | ||
public class PatternBootstrapsTest { | ||
|
||
public static final MethodHandle INVK_PATTERN; | ||
static { | ||
try { | ||
INVK_PATTERN = MethodHandles.lookup().findStatic(PatternBootstraps.class, "invokePattern", | ||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class)); | ||
} | ||
catch (ReflectiveOperationException e) { | ||
throw new AssertionError("Should not happen", e); | ||
} | ||
} | ||
|
||
record R(int i, int j) {} | ||
|
||
class R2 { | ||
int x; | ||
int y; | ||
public R2(int x, int y) { | ||
this.x = x; | ||
this.y = y; | ||
} | ||
public pattern R2(int x, int y) { | ||
match R2(this.x, this.y); | ||
} | ||
} | ||
|
||
private void testPatternInvocation(Object target, Class<?> targetType, String mangledName, int componentNo, int result) throws Throwable { | ||
MethodType dtorType = MethodType.methodType(Object.class, targetType); | ||
MethodHandle indy = ((CallSite) INVK_PATTERN.invoke(MethodHandles.lookup(), "", dtorType, mangledName)).dynamicInvoker(); | ||
List<MethodHandle> components = Carriers.components(MethodType.methodType(Object.class, int.class, int.class)); | ||
assertEquals((int) components.get(componentNo).invokeExact(indy.invoke(target)), result); | ||
} | ||
|
||
public void testPatternInvocations() throws Throwable { | ||
testPatternInvocation(new R(1, 2), R.class, "R:I:I", 0, 1); | ||
testPatternInvocation(new R2(1, 2), R2.class, "R2:I:I", 0, 1); | ||
} | ||
} |