xref: /aosp_15_r20/art/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.invoke.MethodHandle;
18 import java.lang.invoke.WrongMethodTypeException;
19 import java.util.Objects;
20 
21 public abstract class AbstractInvokeExactTest {
22 
23   public static String STATUS = "";
24 
runAll()25   public final void runAll() throws Throwable {
26     STATUS = "";
27     Multi.$noinline$testMHFromMain(optionalGet());
28     $noinline$privateMethods();
29     $noinline$testNoArgsCalls();
30     $noinline$nullchecks();
31     $noinline$testWithArgs();
32     $noinline$interfaceChecks();
33     $noinline$abstractClass();
34   }
35 
36   // There is no privateLookupIn for const-method-handle.
$noinline$privateMethods()37   abstract void $noinline$privateMethods() throws Throwable;
38 
$noinline$testNoArgsCalls()39   private void $noinline$testNoArgsCalls() throws Throwable {
40     voidMethod().invokeExact(new A());
41     assertEquals("A.voidMethod", STATUS);
42 
43     int returnedInt = (int) returnInt().invokeExact(new A());
44     assertEquals(42, returnedInt);
45 
46     double returnedDouble = (double) returnDouble().invokeExact(new A());
47     assertEquals(42.0d, returnedDouble);
48     try {
49       interfaceDefaultMethod().invokeExact(new A());
50       unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V");
51     } catch (WrongMethodTypeException expected) {}
52 
53     interfaceDefaultMethod().invokeExact((I) new A());
54     assertEquals("I.defaultMethod", STATUS);
55 
56     overwrittenInterfaceDefaultMethod().invokeExact((I) new A());
57     assertEquals("A.overrideMe", STATUS);
58 
59 
60     try {
61       exceptionThrowingMethod().invokeExact(new A());
62       unreachable("Target method always throws");
63     } catch (MyRuntimeException expected) {
64       assertEquals("A.throwException", STATUS);
65     }
66 
67     try {
68       returnInt().invokeExact(new A());
69       unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V");
70     } catch (WrongMethodTypeException expected) {}
71 
72     String returnedString = (String) staticMethod().invokeExact(new A());
73     assertEquals("staticMethod", returnedString);
74   }
75 
$noinline$nullchecks()76   private void $noinline$nullchecks() throws Throwable {
77     try {
78       voidMethod().invokeExact((A) null);
79       unreachable("Receiver is null, should throw NPE");
80     } catch (NullPointerException expected) {}
81 
82     try {
83       voidMethod().invokeExact((Main) null);
84       unreachable("Should throw WMTE: input is of wrong type");
85     } catch (WrongMethodTypeException expected) {}
86 
87     try {
88       interfaceDefaultMethod().invokeExact((I) null);
89       unreachable("Receiver is null, should throw NPE");
90     } catch (NullPointerException expected) {}
91 
92     try {
93       interfaceDefaultMethod().invokeExact((A) null);
94       unreachable("Should throw WMTE: input is of wrong type");
95     } catch (WrongMethodTypeException expected) {}
96 
97     try {
98       MethodHandle mh = $noinline$nullMethodHandle();
99       mh.invokeExact();
100       unreachable("MethodHandle object is null, should throw NPE");
101     } catch (NullPointerException expected) {}
102   }
103 
$noinline$nullMethodHandle()104   private static MethodHandle $noinline$nullMethodHandle() {
105     return null;
106   }
107 
$noinline$testWithArgs()108   private void $noinline$testWithArgs() throws Throwable {
109     int sum = (int) sumI().invokeExact(new Sums(), 1);
110     assertEquals(1, sum);
111 
112     sum = (int) sum2I().invokeExact(new Sums(), 1, 2);
113     assertEquals(3, sum);
114 
115     sum = (int) sum3I().invokeExact(new Sums(), 1, 2, 3);
116     assertEquals(6, sum);
117 
118     sum = (int) sum4I().invokeExact(new Sums(), 1, 2, 3, 4);
119     assertEquals(10, sum);
120 
121     sum = (int) sum5I().invokeExact(new Sums(), 1, 2, 3, 4, 5);
122     assertEquals(15, sum);
123 
124     sum = (int) sum6I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6);
125     assertEquals(21, sum);
126 
127     sum = (int) sum7I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7);
128     assertEquals(28, sum);
129 
130     sum = (int) sum8I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8);
131     assertEquals(36, sum);
132 
133     sum = (int) sum9I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
134     assertEquals(45, sum);
135 
136     sum = (int) sum10I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
137     assertEquals(55, sum);
138 
139     long lsum = (long) sumIJ().invokeExact(new Sums(), 1, 2L);
140     assertEquals(3L, lsum);
141 
142     lsum = (long) sum2IJ().invokeExact(new Sums(), 1, 2L, 3, 4L);
143     assertEquals(10L, lsum);
144 
145     lsum = (long) sum3IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L);
146     assertEquals(21L, lsum);
147 
148     lsum = (long) sum4IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L);
149     assertEquals(36L, lsum);
150 
151     lsum = (long) sum5IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L);
152     assertEquals(55L, lsum);
153   }
154 
$noinline$interfaceChecks()155   private void $noinline$interfaceChecks() throws Throwable {
156     FooBarImpl instance = new FooBarImpl();
157 
158     String result = null;
159     result = (String) fooNonDefault().invokeExact((Foo) instance);
160     assertEquals("FooBarImpl.nonDefault", result);
161 
162     result = (String) fooBarImplNonDefault().invokeExact(instance);
163     assertEquals("FooBarImpl.nonDefault", result);
164 
165     result = (String) barDefault().invokeExact((Bar) instance);
166     assertEquals("Bar.defaultToOverride", result);
167 
168     result = (String) fooDefault().invokeExact((Foo) instance);
169     assertEquals("Bar.defaultToOverride", result);
170 
171     result = (String) fooBarImplDefault().invokeExact(instance);
172     assertEquals("Bar.defaultToOverride", result);
173 
174     result = (String) fooNonOverriddenDefault().invokeExact((Foo) instance);
175     assertEquals("Foo.nonOverriddenDefault", result);
176 
177     result = (String) barNonOverriddenDefault().invokeExact((Bar) instance);
178     assertEquals("Foo.nonOverriddenDefault", result);
179 
180     ToStringable toStringable = new ToStringableImpl();
181     result = (String) toStringDefinedInAnInterface().invokeExact(toStringable);
182     assertEquals("ToStringableImpl", result);
183 
184     try {
185       String ignored = (String) toStringDefinedInAnInterface().invokeExact(instance);
186       unreachable("Should throw WMTE");
187     } catch (WrongMethodTypeException expected) {}
188 
189     Impl1 impl1 = new Impl1();
190 
191     result = (String) interfaceOneMethod().invokeExact((Interface1) impl1);
192     assertEquals("Impl1.methodOne", result);
193 
194     Impl2 impl2 = new Impl2();
195 
196     result = (String) interfaceTwoMethod().invokeExact((Interface2) impl2);
197     assertEquals("Impl2.methodTwo", result);
198 
199     result = (String) interfaceOneMethod().invokeExact((Interface1) impl2);
200     assertEquals("Impl2.methodOne", result);
201 
202     Impl3 impl3 = new Impl3();
203 
204     result = (String) interfaceThreeMethod().invokeExact((Interface3) impl3);
205     assertEquals("Impl3.methodThree", result);
206 
207     result = (String) interfaceTwoMethod().invokeExact((Interface2) impl3);
208     assertEquals("Impl3.methodTwo", result);
209 
210     result = (String) interfaceOneMethod().invokeExact((Interface1) impl3);
211     assertEquals("Impl3.methodOne", result);
212 
213     Impl4 impl4 = new Impl4();
214 
215     result = (String) interfaceFourMethod().invokeExact((Interface4) impl4);
216     assertEquals("Impl4.methodFour", result);
217 
218     result = (String) interfaceThreeMethod().invokeExact((Interface3) impl4);
219     assertEquals("Impl4.methodThree", result);
220 
221     result = (String) interfaceTwoMethod().invokeExact((Interface2) impl4);
222     assertEquals("Impl4.methodTwo", result);
223 
224     result = (String) interfaceOneMethod().invokeExact((Interface1) impl4);
225     assertEquals("Impl4.methodOne", result);
226 
227     FooAndFooConflictImpl conflictImpl = new FooAndFooConflictImpl();
228 
229     result = (String) fooDefault().invokeExact((Foo) conflictImpl);
230     assertEquals("DefaultMethodConflictImpl.defaultToOverride", result);
231 
232     FooAndFooConflict fooAndFooConflict = new FooAndFooConflict() {
233       public String nonDefault() {
234         throw new UnsupportedOperationException();
235       }
236     };
237 
238     // TODO(b/297147201): The RI throws AbsractMethodError in these cases, just like plain
239     // fooAndFooConflict.defaultToOverride() call. So RI's invokeExact follows "equivalent of a
240     // particular bytecode behavior". ART throws ICCE in both cases, so current implementation of
241     // invokeExact does not break "equivalent ..." part of the javadoc.
242     // Need to check what the spec says about the error thrown when a conflicting default method is
243     // attempted to be called.
244     try {
245       String ignored = (String) fooAndFooConflictDefault().invokeExact(fooAndFooConflict);
246       unreachable("Non-overridden default conflict method");
247     } catch (IncompatibleClassChangeError expected) {}
248 
249     try {
250       String ignored = (String) fooDefault().invokeExact((Foo) fooAndFooConflict);
251       unreachable("Non-overridden default conflict method");
252     } catch (IncompatibleClassChangeError expected) {}
253 
254     BaseInterface baseClassImpl = new BaseClassImpl();
255     try {
256       String ignored = (String) baseInterface().invokeExact(baseClassImpl);
257       unreachable("Calling unimplemented interface method");
258     } catch (AbstractMethodError expected) {}
259   }
260 
$noinline$abstractClass()261   private void $noinline$abstractClass() throws Throwable {
262     FooBarImpl instance = new FooBarImpl();
263 
264     String result = null;
265     result = (String) fooBarDefinedInAbstract().invokeExact((FooBar) instance);
266     assertEquals("FooBar.definedInAbstract", result);
267 
268     result = (String) fooBarImplDefinedInAbstract().invokeExact(instance);
269     assertEquals("FooBar.definedInAbstract", result);
270 
271     FooBar fooBar = new FooBar() {
272       @Override
273       public String nonDefault() {
274         return "anonymous.nonDefault";
275       }
276     };
277 
278     result = (String) fooBarDefinedInAbstract().invokeExact(fooBar);
279     assertEquals("FooBar.definedInAbstract", result);
280 
281     result = (String) fooBarNonDefault().invokeExact(fooBar);
282     assertEquals("anonymous.nonDefault", result);
283   }
284 
assertEquals(Object expected, Object actual)285   static void assertEquals(Object expected, Object actual) {
286     if (!Objects.equals(expected, actual)) {
287       throw new AssertionError("Expected: " + expected + ", got: " + actual);
288     }
289   }
290 
assertEquals(int expected, int actual)291   private static void assertEquals(int expected, int actual) {
292     if (expected != actual) {
293       throw new AssertionError("Expected: " + expected + ", got: " + actual);
294     }
295   }
296 
assertEquals(long expected, long actual)297   private static void assertEquals(long expected, long actual) {
298     if (expected != actual) {
299       throw new AssertionError("Expected: " + expected + ", got: " + actual);
300     }
301   }
302 
assertEquals(double expected, double actual)303   private static void assertEquals(double expected, double actual) {
304     if (expected != actual) {
305       throw new AssertionError("Expected: " + expected + ", got: " + actual);
306     }
307   }
308 
unreachable(String msg)309   static void unreachable(String msg) {
310     throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg);
311   }
312 
optionalGet()313   public abstract MethodHandle optionalGet();
314 
voidMethod()315   public abstract MethodHandle voidMethod();
returnInt()316   public abstract MethodHandle returnInt();
returnDouble()317   public abstract MethodHandle returnDouble();
interfaceDefaultMethod()318   public abstract MethodHandle interfaceDefaultMethod();
overwrittenInterfaceDefaultMethod()319   public abstract MethodHandle overwrittenInterfaceDefaultMethod();
exceptionThrowingMethod()320   public abstract MethodHandle exceptionThrowingMethod();
staticMethod()321   public abstract MethodHandle staticMethod();
322 
sumI()323   public abstract MethodHandle sumI();
sum2I()324   public abstract MethodHandle sum2I();
sum3I()325   public abstract MethodHandle sum3I();
sum4I()326   public abstract MethodHandle sum4I();
sum5I()327   public abstract MethodHandle sum5I();
sum6I()328   public abstract MethodHandle sum6I();
sum7I()329   public abstract MethodHandle sum7I();
sum8I()330   public abstract MethodHandle sum8I();
sum9I()331   public abstract MethodHandle sum9I();
sum10I()332   public abstract MethodHandle sum10I();
sumIJ()333   public abstract MethodHandle sumIJ();
sum2IJ()334   public abstract MethodHandle sum2IJ();
sum3IJ()335   public abstract MethodHandle sum3IJ();
sum4IJ()336   public abstract MethodHandle sum4IJ();
sum5IJ()337   public abstract MethodHandle sum5IJ();
338 
fooNonDefault()339   public abstract MethodHandle fooNonDefault();
fooBarImplNonDefault()340   public abstract MethodHandle fooBarImplNonDefault();
barDefault()341   public abstract MethodHandle barDefault();
fooDefault()342   public abstract MethodHandle fooDefault();
fooBarImplDefault()343   public abstract MethodHandle fooBarImplDefault();
fooNonOverriddenDefault()344   public abstract MethodHandle fooNonOverriddenDefault();
barNonOverriddenDefault()345   public abstract MethodHandle barNonOverriddenDefault();
toStringDefinedInAnInterface()346   public abstract MethodHandle toStringDefinedInAnInterface();
347 
interfaceOneMethod()348   public abstract MethodHandle interfaceOneMethod();
interfaceTwoMethod()349   public abstract MethodHandle interfaceTwoMethod();
interfaceThreeMethod()350   public abstract MethodHandle interfaceThreeMethod();
interfaceFourMethod()351   public abstract MethodHandle interfaceFourMethod();
352 
fooBarDefinedInAbstract()353   public abstract MethodHandle fooBarDefinedInAbstract();
fooBarImplDefinedInAbstract()354   public abstract MethodHandle fooBarImplDefinedInAbstract();
fooBarNonDefault()355   public abstract MethodHandle fooBarNonDefault();
fooAndFooConflictDefault()356   public abstract MethodHandle fooAndFooConflictDefault();
baseInterface()357   public abstract MethodHandle baseInterface();
358 }
359