/* * Copyright (C) 2017 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 dalvik.system.InMemoryDexClassLoader; import java.lang.invoke.CallSite; import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.Base64; // This test is a stop-gap until we have support for generating invoke-custom // in the Android tree. public class Main { private static void TestUninitializedCallSite() throws Throwable { CallSite callSite = new MutableCallSite(MethodType.methodType(int.class)); try { callSite.getTarget().invoke(); fail(); } catch (IllegalStateException e) { System.out.println("Caught exception from uninitialized call site"); } callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class)); try { callSite.getTarget().invoke(1535, 'd'); fail(); } catch (IllegalStateException e) { System.out.println("Caught exception from uninitialized call site"); } } private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable { // This is a more comprehensive test of invoke-custom, the linker // method takes additional arguments of types boolean, byte, char, // short, int, float, double, String, Class, and long (in this order) // The test asserts the values passed to the linker method match their // expected values. byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes(); Base64.Decoder decoder = Base64.getDecoder(); ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); InMemoryDexClassLoader classLoader = new InMemoryDexClassLoader(dexBuffer, ClassLoader.getSystemClassLoader()); Class<?> testClass = classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes"); Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class); // First invocation should link via the bootstrap method (outputs "Linking add" ...). testMethod.invoke(null, 33, 67); // Subsequent invocations use the cached value of the CallSite and do not require linking. testMethod.invoke(null, -10000, +1000); testMethod.invoke(null, -1000, +10000); } private static void TestLinkerMethodMinimalArguments() throws Throwable { // This test checks various failures when running the linker // method and during invocation of the method handle. byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes(); Base64.Decoder decoder = Base64.getDecoder(); ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); InMemoryDexClassLoader classLoader = new InMemoryDexClassLoader(dexBuffer, ClassLoader.getSystemClassLoader()); Class<?> testClass = classLoader.loadClass("TestLinkerMethodMinimalArguments"); Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class); try { testMethod.invoke(null, 1 /* linker method return null */, 10, 10); } catch (InvocationTargetException e) { assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); assertEquals( e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException"); } try { testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11); } catch (InvocationTargetException e) { assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); assertEquals( e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException"); } try { // Creating the CallSite works here, but fail invoking the method. testMethod.invoke(null, 3 /* target throw NPE */, 10, 12); } catch (InvocationTargetException e) { assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException"); } // This should succeed using already resolved CallSite. testMethod.invoke(null, 0 /* no error */, 10, 13); } private static void TestInvokeCustomWithConcurrentThreads() throws Throwable { // This is a concurrency test that attempts to run invoke-custom on the same // call site. byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes(); Base64.Decoder decoder = Base64.getDecoder(); ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); InMemoryDexClassLoader classLoader = new InMemoryDexClassLoader(dexBuffer, ClassLoader.getSystemClassLoader()); Class<?> testClass = classLoader.loadClass("TestInvokeCustomWithConcurrentThreads"); Method testMethod = testClass.getDeclaredMethod("test"); testMethod.invoke(null); } public static void assertEquals(Object o, Object p) { if (o == p) { return; } if (o != null && p != null && o.equals(p)) { return; } throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); } public static void assertEquals(String s1, String s2) { if (s1 == s2) { return; } if (s1 != null && s2 != null && s1.equals(s2)) { return; } throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); } private static void fail() { System.out.println("fail"); Thread.dumpStack(); } public static void main(String[] args) throws Throwable { TestUninitializedCallSite(); TestLinkerMethodMinimalArguments(); TestLinkerMethodMultipleArgumentTypes(); TestInvokeCustomWithConcurrentThreads(); } }