diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 4ff7d71edc7..4ff7b05b022 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -34,14 +34,12 @@ import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; -import java.lang.classfile.FieldBuilder; import java.lang.classfile.MethodBuilder; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; import java.lang.classfile.constantpool.MethodHandleEntry; import java.lang.classfile.constantpool.NameAndTypeEntry; import java.lang.constant.ClassDesc; -import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandles.Lookup; import java.lang.module.Configuration; @@ -49,8 +47,6 @@ import java.lang.reflect.Modifier; import java.util.LinkedHashSet; import java.util.List; -import java.util.Optional; -import java.util.ServiceLoader; import java.util.Set; import java.util.function.Consumer; @@ -58,12 +54,10 @@ import java.lang.classfile.attribute.ExceptionsAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.constantpool.MethodRefEntry; + import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import static java.lang.invoke.MethodType.methodType; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; @@ -368,7 +362,7 @@ else if (finalAccidentallySerializable) generateSerializationHostileMethods(clb); if (quotableOpGetter != null) { - generateQuotableMethod(clb); + generateQuotedMethod(clb); } } }); @@ -577,9 +571,9 @@ public void accept(CodeBuilder cob) { } /** - * Generate a writeReplace method that supports serialization + * Generate method #quoted() */ - private void generateQuotableMethod(ClassBuilder clb) { + private void generateQuotedMethod(ClassBuilder clb) { clb.withMethod(NAME_METHOD_QUOTED, CodeReflectionSupport.MTD_Quoted, ACC_PUBLIC + ACC_FINAL, new MethodBody(new Consumer() { @Override public void accept(CodeBuilder cob) { diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/Op.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/Op.java index 8b923d8e3cc..41e782422a0 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/Op.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/Op.java @@ -48,7 +48,9 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.BiFunction; @@ -472,6 +474,34 @@ public String toText() { } + /** + * Returns the code model of the Quotable passed in. + * @param q the Quotable we want to get its code model. + * @return the code model of the Quotable passed in. + * @since 99 + */ + public static Optional ofQuotable(Quotable q) { + Object oq = q; + if (Proxy.isProxyClass(oq.getClass())) { + oq = Proxy.getInvocationHandler(oq); + } + + Method method; + try { + method = oq.getClass().getMethod("quoted"); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + method.setAccessible(true); + + Quoted quoted; + try { + quoted = (Quoted) method.invoke(oq); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + return Optional.of(quoted); + } /** * Returns the code model of the method body, if present. diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/Quotable.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/Quotable.java index ef7a79aa3f6..33ac854f2b7 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/Quotable.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/Quotable.java @@ -27,11 +27,8 @@ /** * Classes implementing this interface support code reflection. That is, they can obtain - * a {@link Quoted} object using {@link #quoted()}, which returns the intermediate + * a {@link Quoted} object using {@link Op#ofQuotable(Quotable)}, which returns the intermediate * representation associated with a lambda expression or method reference. */ public interface Quotable { - default Quoted quoted() { - throw new UnsupportedOperationException(); - } } diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java index 567cd2fe2a9..0b8f05e889b 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/interpreter/Interpreter.java @@ -27,6 +27,8 @@ import java.lang.invoke.*; import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; import java.lang.reflect.Proxy; import jdk.incubator.code.*; import jdk.incubator.code.op.CoreOp; @@ -487,16 +489,23 @@ static Object interpretOp(MethodHandles.Lookup l, OpContext oc, Op o) { .asCollector(Object[].class, lo.parameters().size()); Object fiInstance = MethodHandleProxies.asInterfaceInstance(fi, fProxy); - // If a quotable lambda proxy again to implement Quotable + // If a quotable lambda proxy again to add method Quoted quoted() if (Quotable.class.isAssignableFrom(fi)) { return Proxy.newProxyInstance(l.lookupClass().getClassLoader(), new Class[]{fi}, - (_, method, args) -> { - if (method.getDeclaringClass() == Quotable.class) { - // Implement Quotable::quoted - return new Quoted(lo, capturedValuesAndArguments); - } else { - // Delegate to FI instance - return method.invoke(fiInstance, args); + new InvocationHandler() { + private final Quoted quoted = new Quoted(lo, capturedValuesAndArguments); + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (Objects.equals(method.getName(), "quoted") && method.getParameterCount() == 0) { + return quoted(); + } else { + // Delegate to FI instance + return method.invoke(fiInstance, args); + } + } + + public Quoted quoted() { + return quoted; } }); } else { diff --git a/test/jdk/java/lang/reflect/code/TestBuild.java b/test/jdk/java/lang/reflect/code/TestBuild.java index fe4638bf43e..bcc61563f85 100644 --- a/test/jdk/java/lang/reflect/code/TestBuild.java +++ b/test/jdk/java/lang/reflect/code/TestBuild.java @@ -46,7 +46,7 @@ public class TestBuild { public LambdaOp f() { IntBinaryOperator ibo = (IntBinaryOperator & Quotable) (a, b) -> a + b; Quotable iboq = (Quotable) ibo; - return SSA.transform((LambdaOp) iboq.quoted().op()); + return SSA.transform((LambdaOp) Op.ofQuotable(iboq).get().op()); } @Test diff --git a/test/jdk/java/lang/reflect/code/TestLambdaOps.java b/test/jdk/java/lang/reflect/code/TestLambdaOps.java index ef0ec2ecde8..456624c2a09 100644 --- a/test/jdk/java/lang/reflect/code/TestLambdaOps.java +++ b/test/jdk/java/lang/reflect/code/TestLambdaOps.java @@ -153,7 +153,7 @@ static int f(int i) { @Test public void testQuotableModel() { Quotable quotable = (Runnable & Quotable) () -> {}; - Op qop = quotable.quoted().op(); + Op qop = Op.ofQuotable(quotable).get().op(); Op top = qop.ancestorBody().parentOp().ancestorBody().parentOp(); Assert.assertTrue(top instanceof CoreOp.FuncOp); @@ -180,7 +180,7 @@ public void testQuote() { QuotableIntSupplier op = (QuotableIntSupplier) Interpreter.invoke(MethodHandles.lookup(), g, 42); Assert.assertEquals(op.getAsInt(), 42); - Quoted q = op.quoted(); + Quoted q = Op.ofQuotable(op).get(); q.op().writeTo(System.out); Assert.assertEquals(q.capturedValues().size(), 1); Assert.assertEquals(((Var)q.capturedValues().values().iterator().next()).value(), 42); @@ -198,7 +198,7 @@ public void testQuote() { QuotableIntSupplier op = quote(42); Assert.assertEquals(op.getAsInt(), 42); - Quoted q = op.quoted(); + Quoted q = Op.ofQuotable(op).get(); q.op().writeTo(System.out); System.out.print(q.capturedValues().values()); Assert.assertEquals(q.capturedValues().size(), 1); @@ -235,7 +235,7 @@ Iterator methodRefLambdas() { @Test(dataProvider = "methodRefLambdas") public void testIsMethodReference(Quotable q) { - Quoted quoted = q.quoted(); + Quoted quoted = Op.ofQuotable(q).get(); CoreOp.LambdaOp lop = (CoreOp.LambdaOp) quoted.op(); Assert.assertTrue(lop.methodReference().isPresent()); } diff --git a/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java b/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java index ac824911d45..b5d17bbf5ab 100644 --- a/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java +++ b/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java @@ -386,9 +386,9 @@ static int consume(int i, Func f) { } static int consumeQuotable(int i, QuotableFunc f) { - Assert.assertNotNull(f.quoted()); - Assert.assertNotNull(f.quoted().op()); - Assert.assertTrue(f.quoted().op() instanceof CoreOp.LambdaOp); + Assert.assertNotNull(Op.ofQuotable(f).get()); + Assert.assertNotNull(Op.ofQuotable(f).get().op()); + Assert.assertTrue(Op.ofQuotable(f).get().op() instanceof CoreOp.LambdaOp); return f.apply(i + 1); } diff --git a/test/jdk/java/lang/reflect/code/bytecode/TestNestedCapturingLambda.java b/test/jdk/java/lang/reflect/code/bytecode/TestNestedCapturingLambda.java index acccb36aa37..f424275b12b 100644 --- a/test/jdk/java/lang/reflect/code/bytecode/TestNestedCapturingLambda.java +++ b/test/jdk/java/lang/reflect/code/bytecode/TestNestedCapturingLambda.java @@ -62,7 +62,7 @@ static public int f(int a) { static void test(QIntSupplier s, int a) { @SuppressWarnings("unchecked") - CoreOp.Var capture = (CoreOp.Var) s.quoted().capturedValues().values().iterator().next(); + CoreOp.Var capture = (CoreOp.Var) Op.ofQuotable(s).get().capturedValues().values().iterator().next(); Assert.assertEquals(capture.value().intValue(), a); } diff --git a/test/jdk/java/lang/reflect/code/linq/Queryable.java b/test/jdk/java/lang/reflect/code/linq/Queryable.java index 2ee5a199b0b..5927cf1c9b6 100644 --- a/test/jdk/java/lang/reflect/code/linq/Queryable.java +++ b/test/jdk/java/lang/reflect/code/linq/Queryable.java @@ -46,13 +46,13 @@ public interface Queryable { @SuppressWarnings("unchecked") default Queryable where(QuotablePredicate f) { - LambdaOp l = (LambdaOp) f.quoted().op(); + LambdaOp l = (LambdaOp) Op.ofQuotable(f).get().op(); return (Queryable) insertQuery(elementType(), "where", l); } @SuppressWarnings("unchecked") default Queryable select(QuotableFunction f) { - LambdaOp l = (LambdaOp) f.quoted().op(); + LambdaOp l = (LambdaOp) Op.ofQuotable(f).get().op(); return (Queryable) insertQuery((JavaType) l.invokableType().returnType(), "select", l); } diff --git a/test/jdk/java/lang/reflect/code/stream/StreamFuserUsingQuotable.java b/test/jdk/java/lang/reflect/code/stream/StreamFuserUsingQuotable.java index 5df64764ebf..189a8222bc9 100644 --- a/test/jdk/java/lang/reflect/code/stream/StreamFuserUsingQuotable.java +++ b/test/jdk/java/lang/reflect/code/stream/StreamFuserUsingQuotable.java @@ -71,10 +71,10 @@ static class StreamOp { final LambdaOp lambdaOp; StreamOp(Quotable quotedLambda) { - if (!(quotedLambda.quoted().op() instanceof LambdaOp lambdaOp)) { + if (!(Op.ofQuotable(quotedLambda).get().op() instanceof LambdaOp lambdaOp)) { throw new IllegalArgumentException("Quotable operation is not lambda operation"); } - if (!(quotedLambda.quoted().capturedValues().isEmpty())) { + if (!(Op.ofQuotable(quotedLambda).get().capturedValues().isEmpty())) { throw new IllegalArgumentException("Quotable operation captures values"); } this.lambdaOp = lambdaOp; @@ -197,10 +197,10 @@ void fuseIntermediateOperation(int i, Block.Builder body, Value element, Block.B } public FuncOp forEach(QuotableConsumer quotableConsumer) { - if (!(quotableConsumer.quoted().op() instanceof LambdaOp consumer)) { + if (!(Op.ofQuotable(quotableConsumer).get().op() instanceof LambdaOp consumer)) { throw new IllegalArgumentException("Quotable consumer is not lambda operation"); } - if (!(quotableConsumer.quoted().capturedValues().isEmpty())) { + if (!(Op.ofQuotable(quotableConsumer).get().capturedValues().isEmpty())) { throw new IllegalArgumentException("Quotable consumer captures values"); } @@ -224,16 +224,16 @@ public FuncOp forEach(QuotableConsumer quotableConsumer) { } public FuncOp collect(QuotableSupplier quotableSupplier, QuotableBiConsumer quotableAccumulator) { - if (!(quotableSupplier.quoted().op() instanceof LambdaOp supplier)) { + if (!(Op.ofQuotable(quotableSupplier).get().op() instanceof LambdaOp supplier)) { throw new IllegalArgumentException("Quotable supplier is not lambda operation"); } - if (!(quotableSupplier.quoted().capturedValues().isEmpty())) { + if (!(Op.ofQuotable(quotableSupplier).get().capturedValues().isEmpty())) { throw new IllegalArgumentException("Quotable supplier captures values"); } - if (!(quotableAccumulator.quoted().op() instanceof LambdaOp accumulator)) { + if (!(Op.ofQuotable(quotableAccumulator).get().op() instanceof LambdaOp accumulator)) { throw new IllegalArgumentException("Quotable accumulator is not lambda operation"); } - if (!(quotableAccumulator.quoted().capturedValues().isEmpty())) { + if (!(Op.ofQuotable(quotableAccumulator).get().capturedValues().isEmpty())) { throw new IllegalArgumentException("Quotable accumulator captures values"); } diff --git a/test/langtools/tools/javac/reflect/CodeReflectionTester.java b/test/langtools/tools/javac/reflect/CodeReflectionTester.java index 97ec8d37854..abd73b406cf 100644 --- a/test/langtools/tools/javac/reflect/CodeReflectionTester.java +++ b/test/langtools/tools/javac/reflect/CodeReflectionTester.java @@ -90,7 +90,7 @@ static void check(Field field) throws ReflectiveOperationException { } } else if (Quotable.class.isAssignableFrom(field.getType())) { Quotable quotable = (Quotable) field.get(null); - String found = canonicalizeModel(field, getModelOfQuotedOp(quotable.quoted())); + String found = canonicalizeModel(field, getModelOfQuotedOp(Op.ofQuotable(quotable).get())); String expected = canonicalizeModel(field, ir.value()); if (!found.equals(expected)) { error("Bad IR\nFound:\n%s\n\nExpected:\n%s", found, expected); diff --git a/test/langtools/tools/javac/reflect/QuotedSameInstanceTest.java b/test/langtools/tools/javac/reflect/QuotedSameInstanceTest.java index 015187f15a6..8668b8fab0f 100644 --- a/test/langtools/tools/javac/reflect/QuotedSameInstanceTest.java +++ b/test/langtools/tools/javac/reflect/QuotedSameInstanceTest.java @@ -1,3 +1,4 @@ +import jdk.incubator.code.Op; import org.testng.Assert; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ /* * @test - * @summary test that invoking Quotable#quoted returns the same instance + * @summary test that invoking Op#ofQuotable returns the same instance * @modules jdk.incubator.code * @run testng QuotedSameInstanceTest */ @@ -19,7 +20,7 @@ public class QuotedSameInstanceTest { @Test void testWithOneThread() { - Assert.assertSame(q1.quoted(), q1.quoted()); + Assert.assertSame(Op.ofQuotable(q1).get(), Op.ofQuotable(q1).get()); } interface QuotableIntUnaryOperator extends IntUnaryOperator, Quotable { } @@ -27,7 +28,7 @@ interface QuotableIntUnaryOperator extends IntUnaryOperator, Quotable { } @Test void testWithMultiThreads() { - Object[] quotedObjects = IntStream.range(0, 1024).parallel().mapToObj(__ -> q2.quoted()).toArray(); + Object[] quotedObjects = IntStream.range(0, 1024).parallel().mapToObj(__ -> Op.ofQuotable(q2).get()).toArray(); for (int i = 1; i < quotedObjects.length; i++) { Assert.assertSame(quotedObjects[i], quotedObjects[i - 1]); } diff --git a/test/langtools/tools/javac/reflect/quoted/TestCaptureQuotable.java b/test/langtools/tools/javac/reflect/quoted/TestCaptureQuotable.java index b3437f33c16..2a2e35134db 100644 --- a/test/langtools/tools/javac/reflect/quoted/TestCaptureQuotable.java +++ b/test/langtools/tools/javac/reflect/quoted/TestCaptureQuotable.java @@ -51,7 +51,7 @@ public class TestCaptureQuotable { @Test(dataProvider = "ints") public void testCaptureIntParam(int x) { Quotable quotable = (Quotable & IntUnaryOperator)y -> x + y; - Quoted quoted = quotable.quoted(); + Quoted quoted = Op.ofQuotable(quotable).get(); assertEquals(quoted.capturedValues().size(), 1); assertEquals(((Var)quoted.capturedValues().values().iterator().next()).value(), x); List arguments = new ArrayList<>(); @@ -67,7 +67,7 @@ public void testCaptureThisRefAndIntConstant() { final int x = 100; String hello = "hello"; Quotable quotable = (Quotable & ToIntFunction)y -> y.intValue() + hashCode() + hello.length() + x; - Quoted quoted = quotable.quoted(); + Quoted quoted = Op.ofQuotable(quotable).get(); assertEquals(quoted.capturedValues().size(), 3); Iterator it = quoted.capturedValues().values().iterator(); assertEquals(it.next(), this); @@ -94,7 +94,7 @@ public void testCaptureMany() { int i8 = ia[7] = 7; Quotable quotable = (Quotable & IntSupplier) () -> i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; - Quoted quoted = quotable.quoted(); + Quoted quoted = Op.ofQuotable(quotable).get(); assertEquals(quoted.capturedValues().size(), ia.length); assertEquals(quoted.op().capturedValues(), new ArrayList<>(quoted.capturedValues().keySet())); Iterator it = quoted.capturedValues().values().iterator(); @@ -120,7 +120,7 @@ Quotable quotable() { } Context context = new Context(x); Quotable quotable = context.quotable(); - Quoted quoted = quotable.quoted(); + Quoted quoted = Op.ofQuotable(quotable).get(); assertEquals(quoted.capturedValues().size(), 1); assertEquals(quoted.capturedValues().values().iterator().next(), context); List arguments = new ArrayList<>(); @@ -142,7 +142,7 @@ public Object[][] ints() { public void testCaptureReferenceReceiver(int i) { int prevCount = Box.count; Quotable quotable = (Quotable & IntUnaryOperator)new Box(i)::add; - Quoted quoted = quotable.quoted(); + Quoted quoted = Op.ofQuotable(quotable).get(); assertEquals(Box.count, prevCount + 1); // no duplicate receiver computation! assertEquals(quoted.capturedValues().size(), 1); assertEquals(((Box)((Var)quoted.capturedValues().values().iterator().next()).value()).i, i);