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