1 /* 2 * Copyright (C) 2017 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 dalvik.system.InMemoryDexClassLoader; 18 19 import java.lang.invoke.CallSite; 20 import java.lang.invoke.MethodType; 21 import java.lang.invoke.MutableCallSite; 22 23 import java.lang.reflect.InvocationTargetException; 24 import java.lang.reflect.Method; 25 import java.nio.ByteBuffer; 26 import java.util.Base64; 27 28 // This test is a stop-gap until we have support for generating invoke-custom 29 // in the Android tree. 30 31 public class Main { 32 33 private static void TestUninitializedCallSite() throws Throwable { 34 CallSite callSite = new MutableCallSite(MethodType.methodType(int.class)); 35 try { 36 callSite.getTarget().invoke(); 37 fail(); 38 } catch (IllegalStateException e) { 39 System.out.println("Caught exception from uninitialized call site"); 40 } 41 42 callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class)); 43 try { 44 callSite.getTarget().invoke(1535, 'd'); 45 fail(); 46 } catch (IllegalStateException e) { 47 System.out.println("Caught exception from uninitialized call site"); 48 } 49 } 50 51 private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable { 52 // This is a more comprehensive test of invoke-custom, the linker 53 // method takes additional arguments of types boolean, byte, char, 54 // short, int, float, double, String, Class, and long (in this order) 55 // The test asserts the values passed to the linker method match their 56 // expected values. 57 byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes(); 58 Base64.Decoder decoder = Base64.getDecoder(); 59 ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); 60 61 InMemoryDexClassLoader classLoader = 62 new InMemoryDexClassLoader(dexBuffer, 63 ClassLoader.getSystemClassLoader()); 64 Class<?> testClass = 65 classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes"); 66 Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class); 67 // First invocation should link via the bootstrap method (outputs "Linking add" ...). 68 testMethod.invoke(null, 33, 67); 69 // Subsequent invocations use the cached value of the CallSite and do not require linking. 70 testMethod.invoke(null, -10000, +1000); 71 testMethod.invoke(null, -1000, +10000); 72 } 73 74 private static void TestLinkerMethodMinimalArguments() throws Throwable { 75 // This test checks various failures when running the linker 76 // method and during invocation of the method handle. 77 byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes(); 78 Base64.Decoder decoder = Base64.getDecoder(); 79 ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); 80 81 InMemoryDexClassLoader classLoader = 82 new InMemoryDexClassLoader(dexBuffer, 83 ClassLoader.getSystemClassLoader()); 84 Class<?> testClass = 85 classLoader.loadClass("TestLinkerMethodMinimalArguments"); 86 Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class); 87 88 try { 89 testMethod.invoke(null, 1 /* linker method return null */, 10, 10); 90 } catch (InvocationTargetException e) { 91 assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); 92 assertEquals( 93 e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException"); 94 } 95 96 try { 97 testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11); 98 } catch (InvocationTargetException e) { 99 assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); 100 assertEquals( 101 e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException"); 102 } 103 try { 104 // Creating the CallSite works here, but fail invoking the method. 105 testMethod.invoke(null, 3 /* target throw NPE */, 10, 12); 106 } catch (InvocationTargetException e) { 107 assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException"); 108 } 109 110 // This should succeed using already resolved CallSite. 111 testMethod.invoke(null, 0 /* no error */, 10, 13); 112 } 113 114 private static void TestInvokeCustomWithConcurrentThreads() throws Throwable { 115 // This is a concurrency test that attempts to run invoke-custom on the same 116 // call site. 117 byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes(); 118 Base64.Decoder decoder = Base64.getDecoder(); 119 ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); 120 121 InMemoryDexClassLoader classLoader = 122 new InMemoryDexClassLoader(dexBuffer, 123 ClassLoader.getSystemClassLoader()); 124 Class<?> testClass = 125 classLoader.loadClass("TestInvokeCustomWithConcurrentThreads"); 126 Method testMethod = testClass.getDeclaredMethod("test"); 127 testMethod.invoke(null); 128 } 129 130 public static void assertEquals(Object o, Object p) { 131 if (o == p) { return; } 132 if (o != null && p != null && o.equals(p)) { return; } 133 throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); 134 } 135 136 public static void assertEquals(String s1, String s2) { 137 if (s1 == s2) { 138 return; 139 } 140 141 if (s1 != null && s2 != null && s1.equals(s2)) { 142 return; 143 } 144 145 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); 146 } 147 148 private static void fail() { 149 System.out.println("fail"); 150 Thread.dumpStack(); 151 } 152 153 public static void main(String[] args) throws Throwable { 154 TestUninitializedCallSite(); 155 TestLinkerMethodMinimalArguments(); 156 TestLinkerMethodMultipleArgumentTypes(); 157 TestInvokeCustomWithConcurrentThreads(); 158 } 159 }