diff options
Diffstat (limited to 'test/2277-methodhandle-invokeexact/src')
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/A.java | 52 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java | 270 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/B.java | 21 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/Bar.java | 22 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java | 532 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/Foo.java | 27 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/FooBar.java | 21 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/FooBarImpl.java | 22 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/I.java | 29 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/JavaApiTest.java | 351 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/Main.java | 544 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/MyRuntimeException.java | 17 | ||||
-rw-r--r-- | test/2277-methodhandle-invokeexact/src/Sums.java | 78 |
13 files changed, 1464 insertions, 522 deletions
diff --git a/test/2277-methodhandle-invokeexact/src/A.java b/test/2277-methodhandle-invokeexact/src/A.java new file mode 100644 index 0000000000..b6fc317cb0 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/A.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class A extends B implements I { + public int field; + public void voidMethod() { + AbstractInvokeExactTest.STATUS = "A.voidMethod"; + } + + @Override + public void overrideMe() { + AbstractInvokeExactTest.STATUS = "A.overrideMe"; + } + + public void throwException() { + AbstractInvokeExactTest.STATUS = "A.throwException"; + throw new MyRuntimeException(); + } + + public double returnDouble() { + return 42.0d; + } + + public int returnInt() { + return 42; + } + + private int privateReturnInt() { + return 1042; + } + + public static String staticMethod(A a) { + return "staticMethod"; + } + + public static double staticMethod() { + return 41.0d; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java b/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java new file mode 100644 index 0000000000..569bff61b2 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.WrongMethodTypeException; +import java.util.Objects; + +public abstract class AbstractInvokeExactTest { + + public static String STATUS = ""; + + public final void runAll() throws Throwable { + STATUS = ""; + Multi.$noinline$testMHFromMain(optionalGet()); + $noinline$privateMethods(); + $noinline$testNoArgsCalls(); + $noinline$nullchecks(); + $noinline$testWithArgs(); + $noinline$interfaceChecks(); + $noinline$abstractClass(); + } + + // There is no privateLookupIn for const-method-handle. + abstract void $noinline$privateMethods() throws Throwable; + + private void $noinline$testNoArgsCalls() throws Throwable { + voidMethod().invokeExact(new A()); + assertEquals("A.voidMethod", STATUS); + + int returnedInt = (int) returnInt().invokeExact(new A()); + assertEquals(42, returnedInt); + + double returnedDouble = (double) returnDouble().invokeExact(new A()); + assertEquals(42.0d, returnedDouble); + try { + interfaceDefaultMethod().invokeExact(new A()); + unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V"); + } catch (WrongMethodTypeException expected) {} + + interfaceDefaultMethod().invokeExact((I) new A()); + assertEquals("I.defaultMethod", STATUS); + + overwrittenInterfaceDefaultMethod().invokeExact((I) new A()); + assertEquals("A.overrideMe", STATUS); + + + try { + exceptionThrowingMethod().invokeExact(new A()); + unreachable("Target method always throws"); + } catch (MyRuntimeException expected) { + assertEquals("A.throwException", STATUS); + } + + try { + returnInt().invokeExact(new A()); + unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V"); + } catch (WrongMethodTypeException expected) {} + + String returnedString = (String) staticMethod().invokeExact(new A()); + assertEquals("staticMethod", returnedString); + } + + private void $noinline$nullchecks() throws Throwable { + try { + voidMethod().invokeExact((A) null); + unreachable("Receiver is null, should throw NPE"); + } catch (NullPointerException expected) {} + + try { + voidMethod().invokeExact((Main) null); + unreachable("Should throw WMTE: input is of wrong type"); + } catch (WrongMethodTypeException expected) {} + + try { + interfaceDefaultMethod().invokeExact((I) null); + unreachable("Receiver is null, should throw NPE"); + } catch (NullPointerException expected) {} + + try { + interfaceDefaultMethod().invokeExact((A) null); + unreachable("Should throw WMTE: input is of wrong type"); + } catch (WrongMethodTypeException expected) {} + + try { + MethodHandle mh = $noinline$nullMethodHandle(); + mh.invokeExact(); + unreachable("MethodHandle object is null, should throw NPE"); + } catch (NullPointerException expected) {} + } + + private static MethodHandle $noinline$nullMethodHandle() { + return null; + } + + private void $noinline$testWithArgs() throws Throwable { + int sum = (int) sumI().invokeExact(new Sums(), 1); + assertEquals(1, sum); + + sum = (int) sum2I().invokeExact(new Sums(), 1, 2); + assertEquals(3, sum); + + sum = (int) sum3I().invokeExact(new Sums(), 1, 2, 3); + assertEquals(6, sum); + + sum = (int) sum4I().invokeExact(new Sums(), 1, 2, 3, 4); + assertEquals(10, sum); + + sum = (int) sum5I().invokeExact(new Sums(), 1, 2, 3, 4, 5); + assertEquals(15, sum); + + sum = (int) sum6I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6); + assertEquals(21, sum); + + sum = (int) sum7I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7); + assertEquals(28, sum); + + sum = (int) sum8I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8); + assertEquals(36, sum); + + sum = (int) sum9I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9); + assertEquals(45, sum); + + sum = (int) sum10I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + assertEquals(55, sum); + + long lsum = (long) sumIJ().invokeExact(new Sums(), 1, 2L); + assertEquals(3L, lsum); + + lsum = (long) sum2IJ().invokeExact(new Sums(), 1, 2L, 3, 4L); + assertEquals(10L, lsum); + + lsum = (long) sum3IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L); + assertEquals(21L, lsum); + + lsum = (long) sum4IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L); + assertEquals(36L, lsum); + + lsum = (long) sum5IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L); + assertEquals(55L, lsum); + } + + private void $noinline$interfaceChecks() throws Throwable { + FooBarImpl instance = new FooBarImpl(); + + String result = null; + result = (String) fooNonDefault().invokeExact((Foo) instance); + assertEquals("FooBarImpl.nonDefault", result); + + result = (String) fooBarImplNonDefault().invokeExact(instance); + assertEquals("FooBarImpl.nonDefault", result); + + result = (String) barDefault().invokeExact((Bar) instance); + assertEquals("Bar.defaultToOverride", result); + + result = (String) fooDefault().invokeExact((Foo) instance); + assertEquals("Bar.defaultToOverride", result); + + result = (String) fooBarImplDefault().invokeExact(instance); + assertEquals("Bar.defaultToOverride", result); + + result = (String) fooNonOverriddenDefault().invokeExact((Foo) instance); + assertEquals("Foo.nonOverriddenDefault", result); + + result = (String) barNonOverriddenDefault().invokeExact((Bar) instance); + assertEquals("Foo.nonOverriddenDefault", result); + } + + private void $noinline$abstractClass() throws Throwable { + FooBarImpl instance = new FooBarImpl(); + + String result = null; + result = (String) fooBarDefinedInAbstract().invokeExact((FooBar) instance); + assertEquals("FooBar.definedInAbstract", result); + + result = (String) fooBarImplDefinedInAbstract().invokeExact(instance); + assertEquals("FooBar.definedInAbstract", result); + + FooBar fooBar = new FooBar() { + @Override + public String nonDefault() { + return "anonymous.nonDefault"; + } + }; + + result = (String) fooBarDefinedInAbstract().invokeExact(fooBar); + assertEquals("FooBar.definedInAbstract", result); + + result = (String) fooBarNonDefault().invokeExact(fooBar); + assertEquals("anonymous.nonDefault", result); + } + + static void assertEquals(Object expected, Object actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Expected: " + expected + ", got: " + actual); + } + } + + private static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected: " + expected + ", got: " + actual); + } + } + + private static void assertEquals(long expected, long actual) { + if (expected != actual) { + throw new AssertionError("Expected: " + expected + ", got: " + actual); + } + } + + private static void assertEquals(double expected, double actual) { + if (expected != actual) { + throw new AssertionError("Expected: " + expected + ", got: " + actual); + } + } + + static void unreachable(String msg) { + throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg); + } + + public abstract MethodHandle optionalGet(); + + public abstract MethodHandle voidMethod(); + public abstract MethodHandle returnInt(); + public abstract MethodHandle returnDouble(); + public abstract MethodHandle interfaceDefaultMethod(); + public abstract MethodHandle overwrittenInterfaceDefaultMethod(); + public abstract MethodHandle exceptionThrowingMethod(); + public abstract MethodHandle staticMethod(); + + public abstract MethodHandle sumI(); + public abstract MethodHandle sum2I(); + public abstract MethodHandle sum3I(); + public abstract MethodHandle sum4I(); + public abstract MethodHandle sum5I(); + public abstract MethodHandle sum6I(); + public abstract MethodHandle sum7I(); + public abstract MethodHandle sum8I(); + public abstract MethodHandle sum9I(); + public abstract MethodHandle sum10I(); + public abstract MethodHandle sumIJ(); + public abstract MethodHandle sum2IJ(); + public abstract MethodHandle sum3IJ(); + public abstract MethodHandle sum4IJ(); + public abstract MethodHandle sum5IJ(); + + public abstract MethodHandle fooNonDefault(); + public abstract MethodHandle fooBarImplNonDefault(); + public abstract MethodHandle barDefault(); + public abstract MethodHandle fooDefault(); + public abstract MethodHandle fooBarImplDefault(); + public abstract MethodHandle fooNonOverriddenDefault(); + public abstract MethodHandle barNonOverriddenDefault(); + + public abstract MethodHandle fooBarDefinedInAbstract(); + public abstract MethodHandle fooBarImplDefinedInAbstract(); + public abstract MethodHandle fooBarNonDefault(); +} diff --git a/test/2277-methodhandle-invokeexact/src/B.java b/test/2277-methodhandle-invokeexact/src/B.java new file mode 100644 index 0000000000..f398f8ed8e --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/B.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class B { + private int privateReturnInt() { + return 9999; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/Bar.java b/test/2277-methodhandle-invokeexact/src/Bar.java new file mode 100644 index 0000000000..b278cd8c52 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Bar.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface Bar extends Foo { + @Override + default String defaultToOverride() { + return "Bar.defaultToOverride"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java b/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java new file mode 100644 index 0000000000..38b7eeb10f --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import annotations.ConstantMethodHandle; + +import java.lang.invoke.MethodHandle; + +public class ConstMethodHandleTest extends AbstractInvokeExactTest { + + @Override + void $noinline$privateMethods() throws Throwable { + // TODO(b/378051428): can't create const-method-handle targeting private methods of + // inner classes. + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "java/util/Optional", + fieldOrMethodName = "get", + descriptor = "()Ljava/lang/Object;") + private static MethodHandle constOptionalGet() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle optionalGet() { + return constOptionalGet(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "voidMethod", + descriptor = "()V") + private static MethodHandle constVoidMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle voidMethod() { + return constVoidMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "returnInt", + descriptor = "()I") + private static MethodHandle constReturnInt() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle returnInt() { + return constReturnInt(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "returnDouble", + descriptor = "()D") + private static MethodHandle constReturnDouble() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle returnDouble() { + return constReturnDouble(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "I", + fieldOrMethodName = "defaultMethod", + descriptor = "()V", + ownerIsInterface = true) + private static MethodHandle constInterfaceDefaultMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle interfaceDefaultMethod() { + return constInterfaceDefaultMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "I", + fieldOrMethodName = "overrideMe", + descriptor = "()V", + ownerIsInterface = true) + private static MethodHandle constOverwrittenInterfaceDefaultMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle overwrittenInterfaceDefaultMethod() { + return constOverwrittenInterfaceDefaultMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "throwException", + descriptor = "()V") + private static MethodHandle constExceptionThrowingMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle exceptionThrowingMethod() { + return constExceptionThrowingMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "A", + fieldOrMethodName = "staticMethod", + descriptor = "(LA;)Ljava/lang/String;") + private static MethodHandle constStaticMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle staticMethod() { + return constStaticMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(I)I") + private static MethodHandle constSumI() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sumI() { + return constSumI(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(II)I") + private static MethodHandle constSum2I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum2I() { + return constSum2I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(III)I") + private static MethodHandle constSum3I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum3I() { + return constSum3I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIII)I") + private static MethodHandle constSum4I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum4I() { + return constSum4I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIII)I") + private static MethodHandle constSum5I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum5I() { + return constSum5I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIII)I") + private static MethodHandle constSum6I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum6I() { + return constSum6I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIII)I") + private static MethodHandle constSum7I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum7I() { + return constSum7I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIII)I") + private static MethodHandle constSum8I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum8I() { + return constSum8I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIIII)I") + private static MethodHandle constSum9I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum9I() { + return constSum9I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIIIII)I") + private static MethodHandle constSum10I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum10I() { + return constSum10I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJ)J") + private static MethodHandle constSumIJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sumIJ() { + return constSumIJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJ)J") + private static MethodHandle constSum2IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum2IJ() { + return constSum2IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJ)J") + private static MethodHandle constSum3IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum3IJ() { + return constSum3IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJIJ)J") + private static MethodHandle constSum4IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum4IJ() { + return constSum4IJ(); + } + + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJIJIJ)J") + private static MethodHandle constSum5IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum5IJ() { + return constSum5IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooNonDefault() { + return constFooNonDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplNonDefault() { + return constFooBarImplNonDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Bar", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constBarDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle barDefault() { + return constBarDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooDefault() { + return constFooDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplDefault() { + return constFooBarImplDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "nonOverriddenDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooNonOverriddenDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooNonOverriddenDefault() { + return constFooNonOverriddenDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Bar", + fieldOrMethodName = "nonOverriddenDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constBarNonOverriddenDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle barNonOverriddenDefault() { + return constBarNonOverriddenDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBar", + fieldOrMethodName = "definedInAbstract", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarDefinedInAbstract() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarDefinedInAbstract() { + return constFooBarDefinedInAbstract(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "definedInAbstract", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplDefinedInAbstract() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplDefinedInAbstract() { + return constFooBarImplDefinedInAbstract(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBar", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarNonDefault() { + return constFooBarNonDefault(); + } + +} diff --git a/test/2277-methodhandle-invokeexact/src/Foo.java b/test/2277-methodhandle-invokeexact/src/Foo.java new file mode 100644 index 0000000000..2a79b2175d --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Foo.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface Foo { + default String defaultToOverride() { + return "Foo.defaultToOverride"; + } + + default String nonOverriddenDefault() { + return "Foo.nonOverriddenDefault"; + } + + String nonDefault(); +} diff --git a/test/2277-methodhandle-invokeexact/src/FooBar.java b/test/2277-methodhandle-invokeexact/src/FooBar.java new file mode 100644 index 0000000000..46372e0f8e --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/FooBar.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract class FooBar implements Foo, Bar { + public String definedInAbstract() { + return "FooBar.definedInAbstract"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/FooBarImpl.java b/test/2277-methodhandle-invokeexact/src/FooBarImpl.java new file mode 100644 index 0000000000..19d9a0ae64 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/FooBarImpl.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class FooBarImpl extends FooBar { + @Override + public String nonDefault() { + return "FooBarImpl.nonDefault"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/I.java b/test/2277-methodhandle-invokeexact/src/I.java new file mode 100644 index 0000000000..eeb41ce2a9 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/I.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface I { + public default void defaultMethod() { + AbstractInvokeExactTest.STATUS = "I.defaultMethod"; + } + + public default void overrideMe() { + throw new RuntimeException("should be overwritten"); + } + + private String innerPrivateMethod() { + return "boo"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/JavaApiTest.java b/test/2277-methodhandle-invokeexact/src/JavaApiTest.java new file mode 100644 index 0000000000..77b4bc0c73 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/JavaApiTest.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static java.lang.invoke.MethodType.methodType; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Optional; + +public class JavaApiTest extends AbstractInvokeExactTest { + + private static final MethodHandle OPTIONAL_GET; + + private static final MethodHandle VOID_METHOD; + private static final MethodHandle RETURN_INT; + private static final MethodHandle RETURN_DOUBLE; + private static final MethodHandle PRIVATE_INTERFACE_METHOD; + private static final MethodHandle B_PRIVATE_RETURN_INT; + private static final MethodHandle A_PRIVATE_RETURN_INT; + private static final MethodHandle STATIC_METHOD; + private static final MethodHandle EXCEPTION_THROWING_METHOD; + private static final MethodHandle INTERFACE_DEFAULT_METHOD; + private static final MethodHandle OVERWRITTEN_INTERFACE_DEFAULT_METHOD; + + private static final MethodHandle SUM_I; + private static final MethodHandle SUM_2I; + private static final MethodHandle SUM_3I; + private static final MethodHandle SUM_4I; + private static final MethodHandle SUM_5I; + private static final MethodHandle SUM_6I; + private static final MethodHandle SUM_7I; + private static final MethodHandle SUM_8I; + private static final MethodHandle SUM_9I; + private static final MethodHandle SUM_10I; + + private static final MethodHandle SUM_IJ; + private static final MethodHandle SUM_2IJ; + private static final MethodHandle SUM_3IJ; + private static final MethodHandle SUM_4IJ; + private static final MethodHandle SUM_5IJ; + + private static final MethodHandle FOO_NONDEFAULT; + private static final MethodHandle FOOBARIMPL_NONDEFAULT; + private static final MethodHandle FOO_DEFAULT; + private static final MethodHandle BAR_DEFAULT; + private static final MethodHandle FOOBAR_DEFINEDINABSTRACT; + private static final MethodHandle FOOBAR_NONDEFAULT; + private static final MethodHandle FOOBARIMPL_DEFINEDINABSTRACT; + private static final MethodHandle FOOBARIMPL_DEFAULT; + private static final MethodHandle FOO_NONOVERRIDDEN_DEFAULT; + private static final MethodHandle BAR_NONOVERRIDDEN_DEFAULT; + + static { + try { + OPTIONAL_GET = MethodHandles.lookup() + .findVirtual(Optional.class, "get", methodType(Object.class)); + + VOID_METHOD = MethodHandles.lookup() + .findVirtual(A.class, "voidMethod", methodType(void.class)); + RETURN_DOUBLE = MethodHandles.lookup() + .findVirtual(A.class, "returnDouble", methodType(double.class)); + RETURN_INT = MethodHandles.lookup() + .findVirtual(A.class, "returnInt", methodType(int.class)); + PRIVATE_INTERFACE_METHOD = MethodHandles.privateLookupIn(I.class, MethodHandles.lookup()) + .findVirtual(I.class, "innerPrivateMethod", methodType(String.class)); + A_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(A.class, MethodHandles.lookup()) + .findVirtual(A.class, "privateReturnInt", methodType(int.class)); + B_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(B.class, MethodHandles.lookup()) + .findVirtual(B.class, "privateReturnInt", methodType(int.class)); + STATIC_METHOD = MethodHandles.lookup() + .findStatic(A.class, "staticMethod", methodType(String.class, A.class)); + EXCEPTION_THROWING_METHOD = MethodHandles.lookup() + .findVirtual(A.class, "throwException", methodType(void.class)); + INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() + .findVirtual(I.class, "defaultMethod", methodType(void.class)); + OVERWRITTEN_INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() + .findVirtual(I.class, "overrideMe", methodType(void.class)); + + SUM_I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(1, int.class))); + SUM_2I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(2, int.class))); + SUM_3I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(3, int.class))); + SUM_4I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(4, int.class))); + SUM_5I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(5, int.class))); + SUM_6I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(6, int.class))); + SUM_7I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(7, int.class))); + SUM_8I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(8, int.class))); + SUM_9I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(9, int.class))); + SUM_10I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class))); + + SUM_IJ = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class)); + SUM_2IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(2, int.class, long.class))); + SUM_3IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(3, int.class, long.class))); + SUM_4IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(4, int.class, long.class))); + SUM_5IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(5, int.class, long.class))); + + FOO_NONDEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "nonDefault", methodType(String.class)); + FOOBARIMPL_NONDEFAULT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "nonDefault", methodType(String.class)); + FOO_DEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "defaultToOverride", methodType(String.class)); + BAR_DEFAULT = MethodHandles.lookup() + .findVirtual(Bar.class, "defaultToOverride", methodType(String.class)); + FOOBAR_DEFINEDINABSTRACT = MethodHandles.lookup() + .findVirtual(FooBar.class, "definedInAbstract", methodType(String.class)); + FOOBAR_NONDEFAULT = MethodHandles.lookup() + .findVirtual(FooBar.class, "nonDefault", methodType(String.class)); + FOOBARIMPL_DEFINEDINABSTRACT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "definedInAbstract", methodType(String.class)); + FOOBARIMPL_DEFAULT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "defaultToOverride", methodType(String.class)); + FOO_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "nonOverriddenDefault", methodType(String.class)); + BAR_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() + .findVirtual(Bar.class, "nonOverriddenDefault", methodType(String.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Class<?>[] repeat(int times, Class<?> clazz) { + Class<?>[] classes = new Class<?>[times]; + Arrays.fill(classes, clazz); + return classes; + } + + private static Class<?>[] repeat(int times, Class<?> first, Class<?> second) { + Class<?>[] classes = new Class<?>[times * 2]; + for (int i = 0; i < 2 * times;) { + classes[i++] = first; + classes[i++] = second; + } + return classes; + } + + @Override + void $noinline$privateMethods() throws Throwable { + assertEquals("boo", (String) PRIVATE_INTERFACE_METHOD.invokeExact((I) new A())); + + int privateIntA = (int) A_PRIVATE_RETURN_INT.invokeExact(new A()); + assertEquals(1042, privateIntA); + + int privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact(new B()); + assertEquals(9999, privateIntB); + + privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact((B) new A()); + assertEquals(9999, privateIntB); + } + + @Override + public MethodHandle voidMethod() { + return VOID_METHOD; + } + + @Override + public MethodHandle returnDouble() { + return RETURN_DOUBLE; + } + + @Override + public MethodHandle returnInt() { + return RETURN_INT; + } + + @Override + public MethodHandle interfaceDefaultMethod() { + return INTERFACE_DEFAULT_METHOD; + } + + @Override + public MethodHandle overwrittenInterfaceDefaultMethod() { + return OVERWRITTEN_INTERFACE_DEFAULT_METHOD; + } + + @Override + public MethodHandle exceptionThrowingMethod() { + return EXCEPTION_THROWING_METHOD; + } + + @Override + public MethodHandle staticMethod() { + return STATIC_METHOD; + } + + @Override + public MethodHandle sumI() { + return SUM_I; + } + + @Override + public MethodHandle sum2I() { + return SUM_2I; + } + + @Override + public MethodHandle sum3I() { + return SUM_3I; + } + + @Override + public MethodHandle sum4I() { + return SUM_4I; + } + + @Override + public MethodHandle sum5I() { + return SUM_5I; + } + + @Override + public MethodHandle sum6I() { + return SUM_6I; + } + + @Override + public MethodHandle sum7I() { + return SUM_7I; + } + + @Override + public MethodHandle sum8I() { + return SUM_8I; + } + + @Override + public MethodHandle sum9I() { + return SUM_9I; + } + + @Override + public MethodHandle sum10I() { + return SUM_10I; + } + + @Override + public MethodHandle sumIJ() { + return SUM_IJ; + } + + @Override + public MethodHandle sum2IJ() { + return SUM_2IJ; + } + + @Override + public MethodHandle sum3IJ() { + return SUM_3IJ; + } + + @Override + public MethodHandle sum4IJ() { + return SUM_4IJ; + } + + @Override + public MethodHandle sum5IJ() { + return SUM_5IJ; + } + + @Override + public MethodHandle fooNonDefault() { + return FOO_NONDEFAULT; + } + + @Override + public MethodHandle fooBarImplNonDefault() { + return FOOBARIMPL_NONDEFAULT; + } + + @Override + public MethodHandle barDefault() { + return BAR_DEFAULT; + } + + @Override + public MethodHandle fooDefault() { + return FOO_DEFAULT; + } + + @Override + public MethodHandle fooBarImplDefault() { + return FOOBARIMPL_DEFAULT; + } + + @Override + public MethodHandle fooNonOverriddenDefault() { + return FOO_NONOVERRIDDEN_DEFAULT; + } + + @Override + public MethodHandle barNonOverriddenDefault() { + return BAR_NONOVERRIDDEN_DEFAULT; + } + + @Override + public MethodHandle fooBarDefinedInAbstract() { + return FOOBAR_DEFINEDINABSTRACT; + } + + @Override + public MethodHandle fooBarImplDefinedInAbstract() { + return FOOBARIMPL_DEFINEDINABSTRACT; + } + + @Override + public MethodHandle fooBarNonDefault() { + return FOOBAR_NONDEFAULT; + } + + @Override + public MethodHandle optionalGet() { + return OPTIONAL_GET; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/Main.java b/test/2277-methodhandle-invokeexact/src/Main.java index a34e5eef60..5d4bfad519 100644 --- a/test/2277-methodhandle-invokeexact/src/Main.java +++ b/test/2277-methodhandle-invokeexact/src/Main.java @@ -14,539 +14,39 @@ * limitations under the License. */ - import static java.lang.invoke.MethodType.methodType; - -import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandle; import java.lang.invoke.WrongMethodTypeException; -import java.util.Arrays; import java.util.Objects; import java.util.Optional; public class Main { - private static String STATUS = ""; - - public static void main(String[] args) throws Throwable { - $noinline$testNoArgsCalls(); - $noinline$testMethodHandleFromOtherDex(); - Multi.$noinline$testMHFromMain(OPTIONAL_GET); - $noinline$testWithArgs(); - $noinline$nullchecks(); - $noinline$interfaceChecks(); - $noinline$abstractClass(); - } - - private static void $noinline$nullchecks() throws Throwable { - try { - VOID_METHOD.invokeExact((A) null); - unreachable("Receiver is null, should throw NPE"); - } catch (NullPointerException expected) {} - - try { - VOID_METHOD.invokeExact((Main) null); - unreachable("Should throw WMTE: input is of wrong type"); - } catch (WrongMethodTypeException expected) {} - - try { - INTERFACE_DEFAULT_METHOD.invokeExact((I) null); - unreachable("Receiver is null, should throw NPE"); - } catch (NullPointerException expected) {} - - try { - INTERFACE_DEFAULT_METHOD.invokeExact((A) null); - unreachable("Should throw WMTE: input is of wrong type"); - } catch (WrongMethodTypeException expected) {} - - try { - MethodHandle mh = null; - mh.invokeExact(); - unreachable("MethodHandle object is null, should throw NPE"); - } catch (NullPointerException expected) {} - } - - private static void $noinline$testMethodHandleFromOtherDex() throws Throwable { - MethodHandle mh = Multi.$noinline$getMethodHandle(); - Optional<String> nonEmpty = Optional.<String>of("hello"); - Object returnedObject = mh.invokeExact(nonEmpty); - assertEquals("hello", returnedObject); - - try { - mh.invokeExact(nonEmpty); - unreachable("mh.type() is (Optional)Object, but callsite is (Optional)V"); - } catch (WrongMethodTypeException expected) {} - } - - private static void $noinline$testNoArgsCalls() throws Throwable { - VOID_METHOD.invokeExact(new A()); - assertEquals("A.voidMethod", STATUS); - - int returnedInt = (int) RETURN_INT.invokeExact(new A()); - assertEquals(42, returnedInt); - - double returnedDouble = (double) RETURN_DOUBLE.invokeExact(new A()); - assertEquals(42.0d, returnedDouble); - - try { - INTERFACE_DEFAULT_METHOD.invokeExact(new A()); - unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V"); - } catch (WrongMethodTypeException expected) {} - - INTERFACE_DEFAULT_METHOD.invokeExact((I) new A()); - assertEquals("I.defaultMethod", STATUS); - - OVERWRITTEN_INTERFACE_DEFAULT_METHOD.invokeExact((I) new A()); - assertEquals("A.overrideMe", STATUS); - - assertEquals("boo", (String) PRIVATE_INTERFACE_METHOD.invokeExact((I) new A())); - - int privateIntA = (int) A_PRIVATE_RETURN_INT.invokeExact(new A()); - assertEquals(1042, privateIntA); - - int privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact(new B()); - assertEquals(9999, privateIntB); - - privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact((B) new A()); - assertEquals(9999, privateIntB); - - try { - EXCEPTION_THROWING_METHOD.invokeExact(new A()); - unreachable("Target method always throws"); - } catch (MyRuntimeException expected) { - assertEquals("A.throwException", STATUS); - } - - try { - RETURN_INT.invokeExact(new A()); - unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V"); - } catch (WrongMethodTypeException expected) {} - - String returnedString = (String) STATIC_METHOD.invokeExact(new A()); - assertEquals("staticMethod", returnedString); - } - - private static void $noinline$testWithArgs() throws Throwable { - int sum = (int) SUM_I.invokeExact(new Sums(), 1); - assertEquals(1, sum); - - sum = (int) SUM_2I.invokeExact(new Sums(), 1, 2); - assertEquals(3, sum); - - sum = (int) SUM_3I.invokeExact(new Sums(), 1, 2, 3); - assertEquals(6, sum); - - sum = (int) SUM_4I.invokeExact(new Sums(), 1, 2, 3, 4); - assertEquals(10, sum); + public static void main(String[] args) throws Throwable { + $noinline$testMethodHandleFromOtherDex(); - sum = (int) SUM_5I.invokeExact(new Sums(), 1, 2, 3, 4, 5); - assertEquals(15, sum); + new ConstMethodHandleTest().runAll(); + new JavaApiTest().runAll(); + } - sum = (int) SUM_6I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6); - assertEquals(21, sum); + private static void $noinline$testMethodHandleFromOtherDex() throws Throwable { + MethodHandle mh = Multi.$noinline$getMethodHandle(); + Optional<String> nonEmpty = Optional.<String>of("hello"); + Object returnedObject = mh.invokeExact(nonEmpty); + assertEquals("hello", returnedObject); - sum = (int) SUM_7I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7); - assertEquals(28, sum); - - sum = (int) SUM_8I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8); - assertEquals(36, sum); - - sum = (int) SUM_9I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9); - assertEquals(45, sum); - - sum = (int) SUM_10I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - assertEquals(55, sum); - - long lsum = (long) SUM_IJ.invokeExact(new Sums(), 1, 2L); - assertEquals(3L, lsum); - - lsum = (long) SUM_2IJ.invokeExact(new Sums(), 1, 2L, 3, 4L); - assertEquals(10L, lsum); - - lsum = (long) SUM_3IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L); - assertEquals(21L, lsum); - - lsum = (long) SUM_4IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L); - assertEquals(36L, lsum); - - lsum = (long) SUM_5IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L); - assertEquals(55L, lsum); - } - - private static void $noinline$interfaceChecks() throws Throwable { - FooBarImpl instance = new FooBarImpl(); - - String result = null; - result = (String) FOO_NONDEFAULT.invokeExact((Foo) instance); - assertEquals("FooBarImpl.nonDefault", result); - - result = (String) FOOBARIMPL_NONDEFAULT.invokeExact(instance); - assertEquals("FooBarImpl.nonDefault", result); - - result = (String) BAR_DEFAULT.invokeExact((Bar) instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOO_DEFAULT.invokeExact((Foo) instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOOBARIMPL_DEFAULT.invokeExact(instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOO_NONOVERRIDDEN_DEFAULT.invokeExact((Foo) instance); - assertEquals("Foo.nonOverriddenDefault", result); - - result = (String) BAR_NONOVERRIDDEN_DEFAULT.invokeExact((Bar) instance); - assertEquals("Foo.nonOverriddenDefault", result); - } - - private static void $noinline$abstractClass() throws Throwable { - FooBarImpl instance = new FooBarImpl(); - - String result = null; - result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact((FooBar) instance); - assertEquals("FooBar.definedInAbstract", result); - - result = (String) FOOBARIMPL_DEFINEDINABSTRACT.invokeExact(instance); - assertEquals("FooBar.definedInAbstract", result); - - FooBar fooBar = new FooBar() { - @Override - public String nonDefault() { - return "anonymous.nonDefault"; - } - }; - - result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact(fooBar); - assertEquals("FooBar.definedInAbstract", result); - - result = (String) FOOBAR_NONDEFAULT.invokeExact(fooBar); - assertEquals("anonymous.nonDefault", result); - } - - private static void assertEquals(Object expected, Object actual) { - if (!Objects.equals(expected, actual)) { - throw new AssertionError("Expected: " + expected + ", got: " + actual); - } - } - - private static void assertEquals(int expected, int actual) { - if (expected != actual) { - throw new AssertionError("Expected: " + expected + ", got: " + actual); - } - } + try { + mh.invokeExact(nonEmpty); + unreachable("mh.type() is (Optional)Object, but callsite is (Optional)V"); + } catch (WrongMethodTypeException expected) {} + } - private static void assertEquals(long expected, long actual) { - if (expected != actual) { - throw new AssertionError("Expected: " + expected + ", got: " + actual); - } + private static void assertEquals(Object expected, Object actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Expected: " + expected + ", got: " + actual); } + } - private static void assertEquals(double expected, double actual) { - if (expected != actual) { - throw new AssertionError("Expected: " + expected + ", got: " + actual); - } - } - - private static void unreachable(String msg) { - throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg); - } - - private static final MethodHandle VOID_METHOD; - private static final MethodHandle RETURN_DOUBLE; - private static final MethodHandle RETURN_INT; - private static final MethodHandle PRIVATE_INTERFACE_METHOD; - private static final MethodHandle B_PRIVATE_RETURN_INT; - private static final MethodHandle A_PRIVATE_RETURN_INT; - private static final MethodHandle STATIC_METHOD; - private static final MethodHandle EXCEPTION_THROWING_METHOD; - private static final MethodHandle INTERFACE_DEFAULT_METHOD; - private static final MethodHandle OVERWRITTEN_INTERFACE_DEFAULT_METHOD; - private static final MethodHandle OPTIONAL_GET; - - private static final MethodHandle SUM_I; - private static final MethodHandle SUM_2I; - private static final MethodHandle SUM_3I; - private static final MethodHandle SUM_4I; - private static final MethodHandle SUM_5I; - private static final MethodHandle SUM_6I; - private static final MethodHandle SUM_7I; - private static final MethodHandle SUM_8I; - private static final MethodHandle SUM_9I; - private static final MethodHandle SUM_10I; - - private static final MethodHandle SUM_IJ; - private static final MethodHandle SUM_2IJ; - private static final MethodHandle SUM_3IJ; - private static final MethodHandle SUM_4IJ; - private static final MethodHandle SUM_5IJ; - - private static final MethodHandle FOO_NONDEFAULT; - private static final MethodHandle FOOBARIMPL_NONDEFAULT; - private static final MethodHandle FOO_DEFAULT; - private static final MethodHandle BAR_DEFAULT; - private static final MethodHandle FOOBAR_DEFINEDINABSTRACT; - private static final MethodHandle FOOBAR_NONDEFAULT; - private static final MethodHandle FOOBARIMPL_DEFINEDINABSTRACT; - private static final MethodHandle FOOBARIMPL_DEFAULT; - private static final MethodHandle FOO_NONOVERRIDDEN_DEFAULT; - private static final MethodHandle BAR_NONOVERRIDDEN_DEFAULT; - - static { - try { - VOID_METHOD = MethodHandles.lookup() - .findVirtual(A.class, "voidMethod", methodType(void.class)); - RETURN_DOUBLE = MethodHandles.lookup() - .findVirtual(A.class, "returnDouble", methodType(double.class)); - RETURN_INT = MethodHandles.lookup() - .findVirtual(A.class, "returnInt", methodType(int.class)); - PRIVATE_INTERFACE_METHOD = MethodHandles.privateLookupIn(I.class, MethodHandles.lookup()) - .findVirtual(I.class, "innerPrivateMethod", methodType(String.class)); - A_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(A.class, MethodHandles.lookup()) - .findVirtual(A.class, "privateReturnInt", methodType(int.class)); - B_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(B.class, MethodHandles.lookup()) - .findVirtual(B.class, "privateReturnInt", methodType(int.class)); - STATIC_METHOD = MethodHandles.lookup() - .findStatic(A.class, "staticMethod", methodType(String.class, A.class)); - EXCEPTION_THROWING_METHOD = MethodHandles.lookup() - .findVirtual(A.class, "throwException", methodType(void.class)); - INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() - .findVirtual(I.class, "defaultMethod", methodType(void.class)); - OVERWRITTEN_INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() - .findVirtual(I.class, "overrideMe", methodType(void.class)); - OPTIONAL_GET = MethodHandles.lookup() - .findVirtual(Optional.class, "get", methodType(Object.class)); - - SUM_I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(1, int.class))); - SUM_2I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(2, int.class))); - SUM_3I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(3, int.class))); - SUM_4I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(4, int.class))); - SUM_5I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(5, int.class))); - SUM_6I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(6, int.class))); - SUM_7I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(7, int.class))); - SUM_8I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(8, int.class))); - SUM_9I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(9, int.class))); - SUM_10I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class))); - - SUM_IJ = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class)); - SUM_2IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(2, int.class, long.class))); - SUM_3IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(3, int.class, long.class))); - SUM_4IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(4, int.class, long.class))); - SUM_5IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(5, int.class, long.class))); - - FOO_NONDEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "nonDefault", methodType(String.class)); - FOOBARIMPL_NONDEFAULT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "nonDefault", methodType(String.class)); - FOO_DEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "defaultToOverride", methodType(String.class)); - BAR_DEFAULT = MethodHandles.lookup() - .findVirtual(Bar.class, "defaultToOverride", methodType(String.class)); - FOOBAR_DEFINEDINABSTRACT = MethodHandles.lookup() - .findVirtual(FooBar.class, "definedInAbstract", methodType(String.class)); - FOOBAR_NONDEFAULT = MethodHandles.lookup() - .findVirtual(FooBar.class, "nonDefault", methodType(String.class)); - FOOBARIMPL_DEFINEDINABSTRACT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "definedInAbstract", methodType(String.class)); - FOOBARIMPL_DEFAULT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "defaultToOverride", methodType(String.class)); - FOO_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "nonOverriddenDefault", methodType(String.class)); - BAR_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() - .findVirtual(Bar.class, "nonOverriddenDefault", methodType(String.class)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static Class<?>[] repeat(int times, Class<?> clazz) { - Class<?>[] classes = new Class<?>[times]; - Arrays.fill(classes, clazz); - return classes; - } - - private static Class<?>[] repeat(int times, Class<?> first, Class<?> second) { - Class<?>[] classes = new Class<?>[times * 2]; - for (int i = 0; i < 2 * times;) { - classes[i++] = first; - classes[i++] = second; - } - return classes; - } - - static interface Foo { - default String defaultToOverride() { - return "Foo.defaultToOverride"; - } - - default String nonOverriddenDefault() { - return "Foo.nonOverriddenDefault"; - } - - String nonDefault(); - } - - static interface Bar extends Foo { - @Override - default String defaultToOverride() { - return "Bar.defaultToOverride"; - } - } - - static abstract class FooBar implements Foo, Bar { - public String definedInAbstract() { - return "FooBar.definedInAbstract"; - } - } - - static class FooBarImpl extends FooBar { - @Override - public String nonDefault() { - return "FooBarImpl.nonDefault"; - } - } - - static class MyRuntimeException extends RuntimeException {} - - static interface I { - public default void defaultMethod() { - STATUS = "I.defaultMethod"; - } - - public default void overrideMe() { - throw new RuntimeException("should be overwritten"); - } - - private String innerPrivateMethod() { - return "boo"; - } - } - - static class A extends B implements I { - public int field; - public void voidMethod() { - STATUS = "A.voidMethod"; - } - - @Override - public void overrideMe() { - STATUS = "A.overrideMe"; - } - - public void throwException() { - STATUS = "A.throwException"; - throw new MyRuntimeException(); - } - - public double returnDouble() { - return 42.0d; - } - - public int returnInt() { - return 42; - } - - private int privateReturnInt() { - return 1042; - } - - public static String staticMethod(A a) { - return "staticMethod"; - } - - public static double staticMethod() { - return 41.0d; - } - } - - static class B { - private int privateReturnInt() { - return 9999; - } - } - - static class Sums { - public int sum(int a) { - return a; - } - - public int sum(int a1, int a2) { - return a1 + a2; - } - - public int sum(int a1, int a2, int a3) { - return a1 + a2 + a3; - } - - public int sum(int a1, int a2, int a3, int a4) { - return a1 + a2 + a3 + a4; - } - - public int sum(int a1, int a2, int a3, int a4, int a5) { - return a1 + a2 + a3 + a4 + a5; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6) { - return a1 + a2 + a3 + a4 + a5 + a6; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, - int a10) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; - } - - public long sum(int a1, long a2) { - return a1 + a2; - } - - public long sum(int a1, long a2, int a3, long a4) { - return a1 + a2 + a3 + a4; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6) { - return a1 + a2 + a3 + a4 + a5 + a6; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8, int a9, - long a10) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; - } - } + private static void unreachable(String msg) { + throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg); + } } diff --git a/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java b/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java new file mode 100644 index 0000000000..623843d4e6 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class MyRuntimeException extends RuntimeException {} diff --git a/test/2277-methodhandle-invokeexact/src/Sums.java b/test/2277-methodhandle-invokeexact/src/Sums.java new file mode 100644 index 0000000000..cb5d852d9d --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Sums.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class Sums { + public int sum(int a) { + return a; + } + + public int sum(int a1, int a2) { + return a1 + a2; + } + + public int sum(int a1, int a2, int a3) { + return a1 + a2 + a3; + } + + public int sum(int a1, int a2, int a3, int a4) { + return a1 + a2 + a3 + a4; + } + + public int sum(int a1, int a2, int a3, int a4, int a5) { + return a1 + a2 + a3 + a4 + a5; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6) { + return a1 + a2 + a3 + a4 + a5 + a6; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, + int a10) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; + } + + public long sum(int a1, long a2) { + return a1 + a2; + } + + public long sum(int a1, long a2, int a3, long a4) { + return a1 + a2 + a3 + a4; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6) { + return a1 + a2 + a3 + a4 + a5 + a6; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8, int a9, + long a10) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; + } +} |