Home | History | Annotate | Download | only in src-art
      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 }