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 { MyClassLoader()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 loadClass(String className, boolean resolve)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 HX86ComputeBaseMethodAddress 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 here. 117 /// CHECK: LoadString 118 /// CHECK-NEXT: NullCheck 119 /// CHECK-NEXT: InvokeVirtual bar()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 { main(String[] args)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