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 class_name:FirstSeenByMyClassLoader
     86   /// CHECK-NEXT: ClinitCheck
     87   /// CHECK-NEXT: InvokeStaticOrDirect
     88   /// CHECK-NEXT: LoadClass class_name:java.lang.System
     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 class_name:FirstSeenByMyClassLoader
     97   /// CHECK-NEXT: ClinitCheck
     98                 /* We inlined FirstSeenByMyClassLoader.$inline$bar */
     99   /// CHECK-NEXT: LoadClass class_name:java.lang.System
    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 class_name:FirstSeenByMyClassLoader gen_clinit_check:true
    109                 /* Load and initialize System */
    110   // There may be MipsComputeBaseMethodAddress here.
    111   /// CHECK:      LoadClass class_name:java.lang.System
    112   // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
    113   // (The merging checks for environment match but HLoadClass/kBootImageAddress
    114   // used for non-PIC mode does not have an environment at all.)
    115   /// CHECK:      StaticFieldGet
    116   // There may be HX86ComputeBaseMethodAddress or MipsComputeBaseMethodAddress here.
    117   /// CHECK:      LoadString
    118   /// CHECK-NEXT: NullCheck
    119   /// CHECK-NEXT: InvokeVirtual
    120   public static void bar() {
    121     FirstSeenByMyClassLoader.$inline$bar();
    122     System.out.println("In between the two calls.");
    123     FirstSeenByMyClassLoader.$noinline$bar();
    124   }
    125 }
    126 
    127 public class Main {
    128   public static void main(String[] args) throws Exception {
    129     MyClassLoader o = new MyClassLoader();
    130     Class<?> foo = o.loadClass("LoadedByMyClassLoader");
    131     Method m = foo.getDeclaredMethod("bar");
    132     m.invoke(null);
    133   }
    134 }
    135