Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2015 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.reflect.Field;
     18 import java.lang.reflect.Method;
     19 import java.util.ArrayList;
     20 import java.util.List;
     21 
     22 class MyClassLoader extends ClassLoader {
     23   MyClassLoader() throws Exception {
     24     super(MyClassLoader.class.getClassLoader());
     25 
     26     // Some magic to get access to the pathList field of BaseDexClassLoader.
     27     ClassLoader loader = getClass().getClassLoader();
     28     Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
     29     Field f = baseDexClassLoader.getDeclaredField("pathList");
     30     f.setAccessible(true);
     31     Object pathList = f.get(loader);
     32 
     33     // Some magic to get access to the dexField field of pathList.
     34     // Need to make a copy of the dex elements since we don't want an app image with pre-resolved
     35     // things.
     36     f = pathList.getClass().getDeclaredField("dexElements");
     37     f.setAccessible(true);
     38     Object[] dexElements = (Object[]) f.get(pathList);
     39     f = dexElements[0].getClass().getDeclaredField("dexFile");
     40     f.setAccessible(true);
     41     for (Object element : dexElements) {
     42       Object dexFile = f.get(element);
     43       // Make copy.
     44       Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
     45       fileNameField.setAccessible(true);
     46       dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
     47         fileNameField.get(dexFile)));
     48     }
     49   }
     50 
     51   ArrayList<Object> dexFiles = new ArrayList<Object>();
     52   Field dexFileField;
     53 
     54   protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
     55     // Other classes may also get loaded, ignore those.
     56     if (className.equals("LoadedByMyClassLoader") || className.equals("FirstSeenByMyClassLoader")) {
     57       System.out.println("Request for " + className);
     58     }
     59 
     60     // We're only going to handle LoadedByMyClassLoader.
     61     if (className != "LoadedByMyClassLoader") {
     62       return getParent().loadClass(className);
     63     }
     64 
     65     // Mimic what DexPathList.findClass is doing.
     66     try {
     67       for (Object dexFile : dexFiles) {
     68         Method method = dexFile.getClass().getDeclaredMethod(
     69             "loadClassBinaryName", String.class, ClassLoader.class, List.class);
     70 
     71         if (dexFile != null) {
     72           Class clazz = (Class)method.invoke(dexFile, className, this, null);
     73           if (clazz != null) {
     74             return clazz;
     75           }
     76         }
     77       }
     78     } catch (Exception e) { /* Ignore */ }
     79     return null;
     80   }
     81 }
     82 
     83 class LoadedByMyClassLoader {
     84   /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
     85   /// CHECK:      LoadClass
     86   /// CHECK-NEXT: ClinitCheck
     87   /// CHECK-NEXT: InvokeStaticOrDirect
     88   /// CHECK-NEXT: LoadClass
     89   /// CHECK-NEXT: ClinitCheck
     90   /// CHECK-NEXT: StaticFieldGet
     91   /// CHECK-NEXT: LoadString
     92   /// CHECK-NEXT: NullCheck
     93   /// CHECK-NEXT: InvokeVirtual
     94 
     95   /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
     96   /// CHECK:      LoadClass
     97   /// CHECK-NEXT: ClinitCheck
     98                 /* We inlined FirstSeenByMyClassLoader.$inline$bar */
     99   /// CHECK-NEXT: LoadClass
    100   /// CHECK-NEXT: ClinitCheck
    101   /// CHECK-NEXT: StaticFieldGet
    102   /// CHECK-NEXT: LoadString
    103   /// CHECK-NEXT: NullCheck
    104   /// CHECK-NEXT: InvokeVirtual
    105 
    106   /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
    107                 /* Load and initialize FirstSeenByMyClassLoader */
    108   /// CHECK:      LoadClass gen_clinit_check:true
    109                 /* Load and initialize System */
    110   /// CHECK-NEXT: LoadClass gen_clinit_check:true
    111   /// CHECK-NEXT: StaticFieldGet
    112   // There may be HArmDexCacheArraysBase or HX86ComputeBaseMethodAddress here.
    113   /// CHECK:      LoadString
    114   /// CHECK-NEXT: NullCheck
    115   /// CHECK-NEXT: InvokeVirtual
    116   public static void bar() {
    117     FirstSeenByMyClassLoader.$inline$bar();
    118     System.out.println("In between the two calls.");
    119     FirstSeenByMyClassLoader.$noinline$bar();
    120   }
    121 }
    122 
    123 public class Main {
    124   public static void main(String[] args) throws Exception {
    125     MyClassLoader o = new MyClassLoader();
    126     Class foo = o.loadClass("LoadedByMyClassLoader");
    127     Method m = foo.getDeclaredMethod("bar");
    128     m.invoke(null);
    129   }
    130 }
    131