DexMaker.java revision 314cb2efb7b1d8d9b584a6e0bd82727168cfd181
1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/* 2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2011 The Android Open Source Project 3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License. 6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at 7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software 11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and 14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License. 15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 17b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinpackage com.android.dx; 18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 19b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport com.android.dex.DexFormat; 20579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.DexOptions; 21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.code.DalvCode; 22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.code.PositionList; 23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.code.RopTranslator; 24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.file.ClassDefItem; 25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.file.DexFile; 26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.file.EncodedField; 27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.dex.file.EncodedMethod; 285624228626d7cdf206de25a6981ba8107be61057Jesse Wilsonimport com.android.dx.rop.code.AccessFlags; 29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.code.LocalVariableInfo; 30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.code.RopMethod; 31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.cst.CstString; 32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.cst.CstType; 33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.type.StdTypeList; 34171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann 35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.File; 36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.FileOutputStream; 37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.io.IOException; 38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.lang.reflect.InvocationTargetException; 3923abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilsonimport java.lang.reflect.Modifier; 40054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousefimport java.util.Arrays; 41054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousefimport java.util.Iterator; 42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.LinkedHashMap; 43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Map; 44054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousefimport java.util.Set; 45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.jar.JarEntry; 46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.jar.JarOutputStream; 47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 48b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR; 49b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static java.lang.reflect.Modifier.PRIVATE; 50b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffinimport static java.lang.reflect.Modifier.STATIC; 51b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin 52314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmannimport android.util.Log; 53314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann 54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/** 55b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin * Generates a <strong>D</strong>alvik <strong>EX</strong>ecutable (dex) 56b0f6ea8cec29bd1b2453e8fd15d9c6f65ca3ea2cJesse Wilson * file for execution on Android. Dex files define classes and interfaces, 570e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * including their member methods and fields, executable code, and debugging 580e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * information. They also define annotations, though this API currently has no 590e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * facility to create a dex file that contains annotations. 600e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 610e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <p>This library is intended to satisfy two use cases: 620e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <ul> 630e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <li><strong>For runtime code generation.</strong> By embedding this library 640e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * in your Android application, you can dynamically generate and load 650e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * executable code. This approach takes advantage of the fact that the 660e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * host environment and target environment are both Android. 670e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <li><strong>For compile time code generation.</strong> You may use this 680e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * library as a part of a compiler that targets Android. In this scenario 690e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * the generated dex file must be installed on an Android device before it 700e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * can be executed. 710e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * </ul> 720e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 730e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <h3>Example: Fibonacci</h3> 740e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * To illustrate how this API is used, we'll use DexMaker to generate a class 75008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * equivalent to the following Java source: <pre> {@code 760e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 770e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * package com.publicobject.fib; 780e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 790e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * public class Fibonacci { 800e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * public static int fib(int i) { 810e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * if (i < 2) { 820e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * return i; 830e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * } 840e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * return fib(i - 1) + fib(i - 2); 850e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * } 860e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }}</pre> 870e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 880e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <p>We start by creating a {@link TypeId} to identify the generated {@code 890e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Fibonacci} class. DexMaker identifies types by their internal names like 900e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * {@code Ljava/lang/Object;} rather than their Java identifiers like {@code 910e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * java.lang.Object}. <pre> {@code 920e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 930e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * TypeId<?> fibonacci = TypeId.get("Lcom/google/dexmaker/examples/Fibonacci;"); 940e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 950e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 960e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <p>Next we declare the class. It allows us to specify the type's source file 970e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * for stack traces, its modifiers, its superclass, and the interfaces it 980e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * implements. In this case, {@code Fibonacci} is a public class that extends 990e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * from {@code Object}: <pre> {@code 1000e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1010e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * String fileName = "Fibonacci.generated"; 1020e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * DexMaker dexMaker = new DexMaker(); 1030e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * dexMaker.declare(fibonacci, fileName, Modifier.PUBLIC, TypeId.OBJECT); 1040e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1050e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * It is illegal to declare members of a class without also declaring the class 1060e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * itself. 1070e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1080e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <p>To make it easier to go from our Java method to dex instructions, we'll 1090e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * manually translate it to pseudocode fit for an assembler. We need to replace 1100e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * control flow like {@code if()} blocks and {@code for()} loops with labels and 1110e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * branches. We'll also avoid performing multiple operations in one statement, 1120e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * using local variables to hold intermediate values as necessary: 1130e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <pre> {@code 1140e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1150e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int constant1 = 1; 1160e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int constant2 = 2; 1170e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * if (i < constant2) goto baseCase; 1180e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int a = i - constant1; 1190e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int b = i - constant2; 1200e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int c = fib(a); 1210e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int d = fib(b); 1220e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * int result = c + d; 1230e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * return result; 1240e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * baseCase: 1250e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * return i; 1260e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1270e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 128008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * <p>We look up the {@code MethodId} for the method on the declaring type. This 129008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * takes the method's return type (possibly {@link TypeId#VOID}), its name and 130008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * its parameters types. Next we declare the method, specifying its modifiers by 131008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * bitwise ORing constants from {@link java.lang.reflect.Modifier}. The declare 132008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * call returns a {@link Code} object, which we'll use to define the method's 133008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * instructions. <pre> {@code 1340e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1350e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * MethodId<?, Integer> fib = fibonacci.getMethod(TypeId.INT, "fib", TypeId.INT); 1360e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Code code = dexMaker.declare(fib, Modifier.PUBLIC | Modifier.STATIC); 1370e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1380e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1390e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * <p>One limitation of {@code DexMaker}'s API is that it requires all local 1400e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * variables to be created before any instructions are emitted. Use {@link 141008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * Code#newLocal newLocal()} to create a new local variable. The method's 142008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * parameters are exposed as locals using {@link Code#getParameter 143008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * getParameter()}. For non-static methods the {@code this} pointer is exposed 144008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * using {@link Code#getThis getThis()}. Here we declare all of the local 145008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * variables that we'll need for our {@code fib()} method: <pre> {@code 1460e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1470e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> i = code.getParameter(0, TypeId.INT); 1480e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> constant1 = code.newLocal(TypeId.INT); 1490e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> constant2 = code.newLocal(TypeId.INT); 1500e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> a = code.newLocal(TypeId.INT); 1510e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> b = code.newLocal(TypeId.INT); 1520e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> c = code.newLocal(TypeId.INT); 1530e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> d = code.newLocal(TypeId.INT); 1540e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Local<Integer> result = code.newLocal(TypeId.INT); 1550e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1560e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 157008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * <p>Notice that {@link Local} has a type parameter of {@code Integer}. This is 158008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * useful for generating code that works with existing types like {@code String} 159008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * and {@code Integer}, but it can be a hindrance when generating code that 160008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * involves new types. For this reason you may prefer to use raw types only and 161008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * add {@code @SuppressWarnings("unsafe")} on your calling code. This will yield 162008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * the same result but you won't get IDE support if you make a type error. 1630e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 164b0f6ea8cec29bd1b2453e8fd15d9c6f65ca3ea2cJesse Wilson * <p>We're ready to start defining our method's instructions. The {@link Code} 165b0f6ea8cec29bd1b2453e8fd15d9c6f65ca3ea2cJesse Wilson * class catalogs the available instructions and their use. <pre> {@code 1660e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1670e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.loadConstant(constant1, 1); 1680e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.loadConstant(constant2, 2); 16923abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Label baseCase = new Label(); 1700e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.compare(Comparison.LT, baseCase, i, constant2); 1710e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.op(BinaryOp.SUBTRACT, a, i, constant1); 1720e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.op(BinaryOp.SUBTRACT, b, i, constant2); 1730e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.invokeStatic(fib, c, a); 1740e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.invokeStatic(fib, d, b); 1750e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.op(BinaryOp.ADD, result, c, d); 1760e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.returnValue(result); 1770e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.mark(baseCase); 1780e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * code.returnValue(i); 1790e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1800e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 181008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * <p>We're done defining the dex file. We just need to write it to the 182008290ab55ac24ef656d254e41a03ad2b1fba7d2Jesse Wilson * filesystem or load it into the current process. For this example we'll load 1830e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * the generated code into the current process. This only works when the current 1843e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * process is running on Android. We use {@link #generateAndLoad 1853e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * generateAndLoad()} which takes the class loader that will be used as our 1863e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * generated code's parent class loader. It also requires a directory where 1873e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * temporary files can be written. <pre> {@code 1880e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1890e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * ClassLoader loader = dexMaker.generateAndLoad( 1905692b3b0303c55524ff206dc7840ffdb1fa47628Jesse Wilson * FibonacciMaker.class.getClassLoader(), getDataDirectory()); 1910e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 1920e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Finally we'll use reflection to lookup our generated class on its class 1930e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * loader and invoke its {@code fib()} method: <pre> {@code 1940e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * 1950e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Class<?> fibonacciClass = loader.loadClass("com.google.dexmaker.examples.Fibonacci"); 1960e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * Method fibMethod = fibonacciClass.getMethod("fib", int.class); 1970e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * System.out.println(fibMethod.invoke(null, 8)); 1980e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson * }</pre> 199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 200ab220f004db90fa94ef9349ca1adde5f89012e8dJesse Wilsonpublic final class DexMaker { 201314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann private static final String LOG_TAG = DexMaker.class.getSimpleName(); 202314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann 203b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin private final Map<TypeId<?>, TypeDeclaration> types = new LinkedHashMap<>(); 204171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann private ClassLoader sharedClassLoader; 205d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann private DexFile outputDex; 206314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann private boolean markAsTrusted; 207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 2083e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson /** 2093e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * Creates a new {@code DexMaker} instance, which can be used to create a 2103e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson * single dex file. 2113e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson */ 2123e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson public DexMaker() { 2133e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson } 2143e7a2230ec75b59ae9b4aad292f51df2542ced7dJesse Wilson 215d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann TypeDeclaration getTypeDeclaration(TypeId<?> type) { 216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TypeDeclaration result = types.get(type); 217579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (result == null) { 218579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson result = new TypeDeclaration(type); 219579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson types.put(type, result); 220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return result; 222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 22523abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Declares {@code type}. 22623abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * 22723abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 22823abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Modifier#FINAL} and {@link Modifier#ABSTRACT}. 229579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 2300e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson public void declare(TypeId<?> type, String sourceFile, int flags, 2310e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson TypeId<?> supertype, TypeId<?>... interfaces) { 232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TypeDeclaration declaration = getTypeDeclaration(type); 233c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT; 234c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson if ((flags & ~supportedFlags) != 0) { 235c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson throw new IllegalArgumentException("Unexpected flag: " 236c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson + Integer.toHexString(flags)); 237c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson } 238579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (declaration.declared) { 239579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalStateException("already declared: " + type); 240579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 241579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson declaration.declared = true; 242579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson declaration.flags = flags; 243579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson declaration.supertype = supertype; 244579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson declaration.sourceFile = sourceFile; 245579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson declaration.interfaces = new TypeList(interfaces); 246579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 247579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 248579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 249c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson * Declares a method or constructor. 25023abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * 25123abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 25223abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC}, 253c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson * {@link Modifier#FINAL} and {@link Modifier#SYNCHRONIZED}. 2545624228626d7cdf206de25a6981ba8107be61057Jesse Wilson * <p><strong>Warning:</strong> the {@link Modifier#SYNCHRONIZED} flag 2555624228626d7cdf206de25a6981ba8107be61057Jesse Wilson * is insufficient to generate a synchronized method. You must also use 2565624228626d7cdf206de25a6981ba8107be61057Jesse Wilson * {@link Code#monitorEnter} and {@link Code#monitorExit} to acquire 2575624228626d7cdf206de25a6981ba8107be61057Jesse Wilson * a monitor. 25890699b97998f1582a921202fb909f17f9718d177Jesse Wilson */ 259579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public Code declare(MethodId<?, ?> method, int flags) { 260579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TypeDeclaration typeDeclaration = getTypeDeclaration(method.declaringType); 261579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (typeDeclaration.methods.containsKey(method)) { 262579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalStateException("already declared: " + method); 263579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 264c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 265c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED 266c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED; 267c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson if ((flags & ~supportedFlags) != 0) { 268c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson throw new IllegalArgumentException("Unexpected flag: " 269c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson + Integer.toHexString(flags)); 270c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson } 271c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 2725624228626d7cdf206de25a6981ba8107be61057Jesse Wilson // replace the SYNCHRONIZED flag with the DECLARED_SYNCHRONIZED flag 2735624228626d7cdf206de25a6981ba8107be61057Jesse Wilson if ((flags & Modifier.SYNCHRONIZED) != 0) { 2745624228626d7cdf206de25a6981ba8107be61057Jesse Wilson flags = (flags & ~Modifier.SYNCHRONIZED) | AccessFlags.ACC_DECLARED_SYNCHRONIZED; 2755624228626d7cdf206de25a6981ba8107be61057Jesse Wilson } 276c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 277171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann if (method.isConstructor() || method.isStaticInitializer()) { 278c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson flags |= ACC_CONSTRUCTOR; 279c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson } 280c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 281579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson MethodDeclaration methodDeclaration = new MethodDeclaration(method, flags); 282579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson typeDeclaration.methods.put(method, methodDeclaration); 283579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return methodDeclaration.code; 284579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 285579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 286579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 28723abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Declares a field. 28823abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * 28923abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 29023abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC}, 29123abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * {@link Modifier#FINAL}, {@link Modifier#VOLATILE}, and {@link 29223abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Modifier#TRANSIENT}. 293c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson * @param staticValue a constant representing the initial value for the 294c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson * static field, possibly null. This must be null if this field is 295c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson * non-static. 296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue) { 298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TypeDeclaration typeDeclaration = getTypeDeclaration(fieldId.declaringType); 299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (typeDeclaration.fields.containsKey(fieldId)) { 300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalStateException("already declared: " + fieldId); 301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 302c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 303c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED 304c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | Modifier.TRANSIENT; 305c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson if ((flags & ~supportedFlags) != 0) { 306c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson throw new IllegalArgumentException("Unexpected flag: " 307c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson + Integer.toHexString(flags)); 308c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson } 309c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 310c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson if ((flags & Modifier.STATIC) == 0 && staticValue != null) { 311c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson throw new IllegalArgumentException("staticValue is non-null, but field is not static"); 312c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson } 313c0271e9981ddd85a13ed88defd0b5b1a5ccc6f46Jesse Wilson 314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson FieldDeclaration fieldDeclaration = new FieldDeclaration(fieldId, flags, staticValue); 315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson typeDeclaration.fields.put(fieldId, fieldDeclaration); 316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 317579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 318579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 31923abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Generates a dex file and returns its bytes. 320579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 321579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public byte[] generate() { 322d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann if (outputDex == null) { 323d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann DexOptions options = new DexOptions(); 324d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 325d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann outputDex = new DexFile(options); 326d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } 327579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 328579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (TypeDeclaration typeDeclaration : types.values()) { 329579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson outputDex.add(typeDeclaration.toClassDefItem()); 330579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 331579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 332579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson try { 333579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return outputDex.toDex(null, false); 334579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } catch (IOException e) { 335579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new RuntimeException(e); 336579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 337579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 338579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 339054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef // Generate a file name for the jar by taking a checksum of MethodIds and 340054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef // parent class types. 341054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef private String generateFileName() { 342054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef int checksum = 1; 343054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 344054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef Set<TypeId<?>> typesKeySet = types.keySet(); 345054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef Iterator<TypeId<?>> it = typesKeySet.iterator(); 346054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef int[] checksums = new int[typesKeySet.size()]; 347054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef int i = 0; 348054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 349054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef while (it.hasNext()) { 350054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef TypeId<?> typeId = it.next(); 351054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef TypeDeclaration decl = getTypeDeclaration(typeId); 352054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef Set<MethodId> methodSet = decl.methods.keySet(); 353054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef if (decl.supertype != null) { 354054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef checksums[i++] = 31 * decl.supertype.hashCode() + methodSet.hashCode(); 355054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 356054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 357054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef Arrays.sort(checksums); 358054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 359054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef for (int sum : checksums) { 360054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef checksum *= 31; 361054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef checksum += sum; 362054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 363054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 364054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef return "Generated_" + checksum +".jar"; 365054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 366054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 367314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann /** 368314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * Set shared class loader to use. 369314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * 370314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * <p>If a class wants to call package private methods of another class they need to share a 371314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * class loader. One common case for this requirement is a mock class wanting to mock package 372314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * private methods of the original class. 373314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * 374314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann * @param classLoader the class loader the new class should be loaded by 375314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann */ 376171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann public void setSharedClassLoader(ClassLoader classLoader) { 377171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann this.sharedClassLoader = classLoader; 378171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann } 379171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann 380314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann public void markAsTrusted() { 381314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann this.markAsTrusted = true; 382314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } 383314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann 384171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader parent) { 385054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef try { 386314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann // Try to load the class so that it can call hidden APIs. This is required for spying 387314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann // on system classes as real-methods of these classes might call blacklisted APIs 388314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann if (markAsTrusted) { 389314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann try { 390314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann if (sharedClassLoader != null) { 391314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann ClassLoader loader = parent != null ? parent : sharedClassLoader; 392314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann loader.getClass().getMethod("addDexPath", String.class, 393314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann Boolean.TYPE).invoke(loader, result.getPath(), true); 394314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann return loader; 395314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } else { 396314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann return (ClassLoader) Class.forName("dalvik.system.BaseDexClassLoader") 397314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann .getConstructor(String.class, File.class, String.class, 398314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann ClassLoader.class, Boolean.TYPE) 399314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann .newInstance(result.getPath(), dexCache.getAbsoluteFile(), null, 400314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann parent, true); 401314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } 402314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } catch (InvocationTargetException e) { 403314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann if (e.getCause() instanceof SecurityException) { 404314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann Log.i(LOG_TAG, "Cannot allow to call blacklisted super methods. This might " 405314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann + "break spying on system classes.", e.getCause()); 406314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } else { 407314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann throw e; 408314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } 409314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } 410314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann } 411314cb2efb7b1d8d9b584a6e0bd82727168cfd181Philip P. Moltmann 412171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann if (sharedClassLoader != null) { 413171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann ClassLoader loader = parent != null ? parent : sharedClassLoader; 414db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin loader.getClass().getMethod("addDexPath", String.class).invoke(loader, 415171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann result.getPath()); 416db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin return loader; 417db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin } else { 418db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin return (ClassLoader) Class.forName("dalvik.system.DexClassLoader") 419db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin .getConstructor(String.class, String.class, String.class, ClassLoader.class) 420db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin .newInstance(result.getPath(), dexCache.getAbsolutePath(), null, parent); 421db20bbcc82de39f499a804d215851995041f3bcfPaul Duffin } 422054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } catch (ClassNotFoundException e) { 423054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef throw new UnsupportedOperationException("load() requires a Dalvik VM", e); 424054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } catch (InvocationTargetException e) { 425054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef throw new RuntimeException(e.getCause()); 426054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } catch (InstantiationException e) { 427054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef throw new AssertionError(); 428054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } catch (NoSuchMethodException e) { 429054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef throw new AssertionError(); 430054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } catch (IllegalAccessException e) { 431054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef throw new AssertionError(); 432054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 433054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 434054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 435579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 43623abc2fe89ec3713645d64bdb74415a9090084f4Jesse Wilson * Generates a dex file and loads its types into the current process. 437579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 43873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * <h3>Picking a dex cache directory</h3> 43973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * The {@code dexCache} should be an application-private directory. If 44073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * you pass a world-writable directory like {@code /sdcard} a malicious app 44173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * could inject code into your process. Most applications should use this: 44273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * <pre> {@code 443579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 44473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * File dexCache = getApplicationContext().getDir("dx", Context.MODE_PRIVATE); 44573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * }</pre> 44673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * If the {@code dexCache} is null, this method will consult the {@code 44773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * dexmaker.dexcache} system property. If that exists, it will be used for 44873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * the dex cache. If it doesn't exist, this method will attempt to guess 44973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * the application's private data directory as a last resort. If that fails, 45073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * this method will fail with an unchecked exception. You can avoid the 45173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * exception by either providing a non-null value or setting the system 45273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * property. 453579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 45473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * @param parent the parent ClassLoader to be used when loading our 45573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * generated types 45673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * @param dexCache the destination directory where generated and optimized 45773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * dex files will be written. If null, this class will try to guess the 45873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * application's private data dir. 459579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 46073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { 46173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson if (dexCache == null) { 46273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson String property = System.getProperty("dexmaker.dexcache"); 46373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson if (property != null) { 46473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson dexCache = new File(property); 46573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson } else { 46673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson dexCache = new AppDataDirGuesser().guess(); 46773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson if (dexCache == null) { 46873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson throw new IllegalArgumentException("dexcache == null (and no default could be" 46973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson + " found; consider setting the 'dexmaker.dexcache' system property)"); 47073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson } 47173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson } 47273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson } 47373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson 474054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef File result = new File(dexCache, generateFileName()); 475054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef // Check that the file exists. If it does, return a DexClassLoader and skip all 476054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef // the dex bytecode generation. 477054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef if (result.exists()) { 478171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann return generateClassLoader(result, dexCache, parent); 479054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef } 480054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef 481579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson byte[] dex = generate(); 482579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 483579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /* 484579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This implementation currently dumps the dex to the filesystem. It 485579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * jars the emitted .dex for the benefit of Gingerbread and earlier 486579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * devices, which can't load .dex files directly. 487579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 488579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * TODO: load the dex from memory where supported. 489579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 490054604d4c95cb530bea955718d79dbe3dc0962cfAndrew Yousef result.createNewFile(); 491579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result)); 4925999ddea21d4d5887cecdcb7730b0d16cdc54d93Andreas Gampe JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME); 4935999ddea21d4d5887cecdcb7730b0d16cdc54d93Andreas Gampe entry.setSize(dex.length); 4945999ddea21d4d5887cecdcb7730b0d16cdc54d93Andreas Gampe jarOut.putNextEntry(entry); 495579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson jarOut.write(dex); 496579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson jarOut.closeEntry(); 497579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson jarOut.close(); 498171f097997993b84053f643dc275ce66364315caPhilip P. Moltmann return generateClassLoader(result, dexCache, parent); 499579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 500579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 501d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann DexFile getDexFile() { 502d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann if (outputDex == null) { 503d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann DexOptions options = new DexOptions(); 504d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 505d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann outputDex = new DexFile(options); 506d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } 507d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann return outputDex; 508d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } 509d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann 510d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann static class TypeDeclaration { 5110e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson private final TypeId<?> type; 512579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 513579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** declared state */ 514579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private boolean declared; 515579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int flags; 5160e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson private TypeId<?> supertype; 517579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private String sourceFile; 518579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private TypeList interfaces; 519d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann private ClassDefItem classDefItem; 520579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 521b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin private final Map<FieldId, FieldDeclaration> fields = new LinkedHashMap<>(); 522b8a5896885d4da2b798888b688a46df1adbf5b89Paul Duffin private final Map<MethodId, MethodDeclaration> methods = new LinkedHashMap<>(); 523579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 5240e49fb9243b7463835ab80ef7cc62435f55846ceJesse Wilson TypeDeclaration(TypeId<?> type) { 525579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.type = type; 526579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 527579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 528579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ClassDefItem toClassDefItem() { 529579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (!declared) { 530579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalStateException("Undeclared type " + type + " declares members: " 531579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson + fields.keySet() + " " + methods.keySet()); 532579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 533579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 534579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson DexOptions dexOptions = new DexOptions(); 535579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson dexOptions.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 536579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 537579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson CstType thisType = type.constant; 538579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 539d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann if (classDefItem == null) { 540d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann classDefItem = new ClassDefItem(thisType, flags, supertype.constant, 541d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann interfaces.ropTypes, new CstString(sourceFile)); 542d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann 543d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann for (MethodDeclaration method : methods.values()) { 544d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann EncodedMethod encoded = method.toEncodedMethod(dexOptions); 545d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann if (method.isDirect()) { 546d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann classDefItem.addDirectMethod(encoded); 547d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } else { 548d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann classDefItem.addVirtualMethod(encoded); 549d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } 550579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 551d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann for (FieldDeclaration field : fields.values()) { 552d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann EncodedField encoded = field.toEncodedField(); 553d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann if (field.isStatic()) { 554d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann classDefItem.addStaticField(encoded, Constants.getConstant(field.staticValue)); 555d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } else { 556d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann classDefItem.addInstanceField(encoded); 557d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann } 558579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 559579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 560579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 561d4a20568bcbaa34526676e4758e011ea32784825Philip P. Moltmann return classDefItem; 562579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 563579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 564579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 565579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson static class FieldDeclaration { 566579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson final FieldId<?, ?> fieldId; 567579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final int accessFlags; 568579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final Object staticValue; 569579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 570579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue) { 57190699b97998f1582a921202fb909f17f9718d177Jesse Wilson if ((accessFlags & STATIC) == 0 && staticValue != null) { 572579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalArgumentException("instance fields may not have a value"); 573579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 574579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.fieldId = fieldId; 575579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.accessFlags = accessFlags; 576579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.staticValue = staticValue; 577579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 578579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 579579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson EncodedField toEncodedField() { 580579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return new EncodedField(fieldId.constant, accessFlags); 581579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 582579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 583579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public boolean isStatic() { 58490699b97998f1582a921202fb909f17f9718d177Jesse Wilson return (accessFlags & STATIC) != 0; 585579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 586579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 587579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 588579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson static class MethodDeclaration { 589579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson final MethodId<?, ?> method; 590579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final int flags; 591579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final Code code; 592579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 593579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public MethodDeclaration(MethodId<?, ?> method, int flags) { 594579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.method = method; 595579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.flags = flags; 596579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.code = new Code(this); 597579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 598579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 599579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson boolean isStatic() { 60090699b97998f1582a921202fb909f17f9718d177Jesse Wilson return (flags & STATIC) != 0; 601579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 602579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 603579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson boolean isDirect() { 60490699b97998f1582a921202fb909f17f9718d177Jesse Wilson return (flags & (STATIC | PRIVATE | ACC_CONSTRUCTOR)) != 0; 605579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 606579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 607579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson EncodedMethod toEncodedMethod(DexOptions dexOptions) { 608579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0); 609579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson LocalVariableInfo locals = null; 610579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson DalvCode dalvCode = RopTranslator.translate( 611579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ropMethod, PositionList.NONE, locals, code.paramSize(), dexOptions); 612579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return new EncodedMethod(method.constant, flags, dalvCode, StdTypeList.EMPTY); 613579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 614579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 615579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson} 616