aboutsummaryrefslogtreecommitdiff
path: root/v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java
diff options
context:
space:
mode:
Diffstat (limited to 'v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java')
-rw-r--r--v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java499
1 files changed, 0 insertions, 499 deletions
diff --git a/v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java b/v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java
deleted file mode 100644
index e55f76fbc..000000000
--- a/v1/src/main/java/com/xtremelabs/robolectric/bytecode/AndroidTranslator.java
+++ /dev/null
@@ -1,499 +0,0 @@
-package com.xtremelabs.robolectric.bytecode;
-
-import android.net.Uri;
-import com.xtremelabs.robolectric.internal.DoNotInstrument;
-import com.xtremelabs.robolectric.internal.Instrument;
-import javassist.*;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-@SuppressWarnings({"UnusedDeclaration"})
-public class AndroidTranslator implements Translator {
- /**
- * IMPORTANT -- increment this number when the bytecode generated for modified classes changes
- * so the cache file can be invalidated.
- */
- public static final int CACHE_VERSION = 21;
-
- private static final List<ClassHandler> CLASS_HANDLERS = new ArrayList<ClassHandler>();
-
- private ClassHandler classHandler;
- private ClassCache classCache;
- private final List<String> instrumentingList = new ArrayList<String>();
- private final List<String> instrumentingExcludeList = new ArrayList<String>();
-
- public AndroidTranslator(ClassHandler classHandler, ClassCache classCache) {
- this.classHandler = classHandler;
- this.classCache = classCache;
-
- // Initialize lists
- instrumentingList.add("android.");
- instrumentingList.add("com.google.android.maps");
- instrumentingList.add("org.apache.http.impl.client.DefaultRequestDirector");
-
- instrumentingExcludeList.add("android.support.v4.app.NotificationCompat");
- instrumentingExcludeList.add("android.support.v4.content.LocalBroadcastManager");
- instrumentingExcludeList.add("android.support.v4.util.LruCache");
- }
-
- public AndroidTranslator(ClassHandler classHandler, ClassCache classCache, List<String> customShadowClassNames) {
- this(classHandler, classCache);
- if (customShadowClassNames != null && !customShadowClassNames.isEmpty()) {
- instrumentingList.addAll(customShadowClassNames);
- }
- }
-
- public void addCustomShadowClass(String customShadowClassName) {
- if (!instrumentingList.contains(customShadowClassName)) {
- instrumentingList.add(customShadowClassName);
- }
- }
-
- public static ClassHandler getClassHandler(int index) {
- return CLASS_HANDLERS.get(index);
- }
-
- @Override
- public void start(ClassPool classPool) throws NotFoundException, CannotCompileException {
- injectClassHandlerToInstrumentedClasses(classPool);
- }
-
- private void injectClassHandlerToInstrumentedClasses(ClassPool classPool) throws NotFoundException, CannotCompileException {
- int index;
- synchronized (CLASS_HANDLERS) {
- CLASS_HANDLERS.add(classHandler);
- index = CLASS_HANDLERS.size() - 1;
- }
-
- CtClass robolectricInternalsCtClass = classPool.get(RobolectricInternals.class.getName());
- robolectricInternalsCtClass.setModifiers(Modifier.PUBLIC);
-
- robolectricInternalsCtClass.getClassInitializer().insertBefore("{\n" +
- "classHandler = " + AndroidTranslator.class.getName() + ".getClassHandler(" + index + ");\n" +
- "}");
- }
-
- @Override
- public void onLoad(ClassPool classPool, String className) throws NotFoundException, CannotCompileException {
- if (classCache.isWriting()) {
- throw new IllegalStateException("shouldn't be modifying bytecode after we've started writing cache! class=" + className);
- }
-
- if (classHasFromAndroidEquivalent(className)) {
- replaceClassWithFromAndroidEquivalent(classPool, className);
- return;
- }
-
- CtClass ctClass;
- try {
- ctClass = classPool.get(className);
- } catch (NotFoundException e) {
- throw new IgnorableClassNotFoundException(e);
- }
-
- if (shouldInstrument(ctClass)) {
- int modifiers = ctClass.getModifiers();
- if (Modifier.isFinal(modifiers)) {
- ctClass.setModifiers(modifiers & ~Modifier.FINAL);
- }
-
- classHandler.instrument(ctClass);
-
- fixConstructors(ctClass);
- fixMethods(ctClass);
-
- try {
- classCache.addClass(className, ctClass.toBytecode());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /* package */ boolean shouldInstrument(CtClass ctClass) {
- if (ctClass.hasAnnotation(Instrument.class)) {
- return true;
- } else if (ctClass.isInterface() || ctClass.hasAnnotation(DoNotInstrument.class)) {
- return false;
- } else {
- for (String klassName : instrumentingExcludeList) {
- if (ctClass.getName().startsWith(klassName)) {
- return false;
- }
- }
- for (String klassName : instrumentingList) {
- if (ctClass.getName().startsWith(klassName)) {
- return true;
- }
- }
- return false;
- }
- }
-
- private boolean classHasFromAndroidEquivalent(String className) {
- return className.startsWith(Uri.class.getName());
- }
-
- private void replaceClassWithFromAndroidEquivalent(ClassPool classPool, String className) throws NotFoundException {
- FromAndroidClassNameParts classNameParts = new FromAndroidClassNameParts(className);
- if (classNameParts.isFromAndroid()) return;
-
- String from = classNameParts.getNameWithFromAndroid();
- CtClass ctClass = classPool.getAndRename(from, className);
-
- ClassMap map = new ClassMap() {
- @Override
- public Object get(Object jvmClassName) {
- FromAndroidClassNameParts classNameParts = new FromAndroidClassNameParts(jvmClassName.toString());
- if (classNameParts.isFromAndroid()) {
- return classNameParts.getNameWithoutFromAndroid();
- } else {
- return jvmClassName;
- }
- }
- };
- ctClass.replaceClassName(map);
- }
-
- class FromAndroidClassNameParts {
- private static final String TOKEN = "__FromAndroid";
-
- private String prefix;
- private String suffix;
-
- FromAndroidClassNameParts(String name) {
- int dollarIndex = name.indexOf("$");
- prefix = name;
- suffix = "";
- if (dollarIndex > -1) {
- prefix = name.substring(0, dollarIndex);
- suffix = name.substring(dollarIndex);
- }
- }
-
- public boolean isFromAndroid() {
- return prefix.endsWith(TOKEN);
- }
-
- public String getNameWithFromAndroid() {
- return prefix + TOKEN + suffix;
- }
-
- public String getNameWithoutFromAndroid() {
- return prefix.replace(TOKEN, "") + suffix;
- }
- }
-
- private void addBypassShadowField(CtClass ctClass, String fieldName) {
- try {
- try {
- ctClass.getField(fieldName);
- } catch (NotFoundException e) {
- CtField field = new CtField(CtClass.booleanType, fieldName, ctClass);
- field.setModifiers(java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.STATIC);
- ctClass.addField(field);
- }
- } catch (CannotCompileException e) {
- throw new RuntimeException(e);
- }
- }
-
- private void fixConstructors(CtClass ctClass) throws CannotCompileException, NotFoundException {
-
- if (ctClass.isEnum()) {
- // skip enum constructors because they are not stubs in android.jar
- return;
- }
-
- boolean hasDefault = false;
-
- for (CtConstructor ctConstructor : ctClass.getDeclaredConstructors()) {
- try {
- fixConstructor(ctClass, hasDefault, ctConstructor);
-
- if (ctConstructor.getParameterTypes().length == 0) {
- hasDefault = true;
- }
- } catch (Exception e) {
- throw new RuntimeException("problem instrumenting " + ctConstructor, e);
- }
- }
-
- if (!hasDefault) {
- String methodBody = generateConstructorBody(ctClass, new CtClass[0]);
- ctClass.addConstructor(CtNewConstructor.make(new CtClass[0], new CtClass[0], "{\n" + methodBody + "}\n", ctClass));
- }
- }
-
- private boolean fixConstructor(CtClass ctClass, boolean needsDefault, CtConstructor ctConstructor) throws NotFoundException, CannotCompileException {
- String methodBody = generateConstructorBody(ctClass, ctConstructor.getParameterTypes());
- ctConstructor.setBody("{\n" + methodBody + "}\n");
- return needsDefault;
- }
-
- private String generateConstructorBody(CtClass ctClass, CtClass[] parameterTypes) throws NotFoundException {
- return generateMethodBody(ctClass,
- new CtMethod(CtClass.voidType, "<init>", parameterTypes, ctClass),
- CtClass.voidType,
- Type.VOID,
- false,
- false);
- }
-
- private void fixMethods(CtClass ctClass) throws NotFoundException, CannotCompileException {
- for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
- fixMethod(ctClass, ctMethod, true);
- }
- CtMethod equalsMethod = ctClass.getMethod("equals", "(Ljava/lang/Object;)Z");
- CtMethod hashCodeMethod = ctClass.getMethod("hashCode", "()I");
- CtMethod toStringMethod = ctClass.getMethod("toString", "()Ljava/lang/String;");
-
- fixMethod(ctClass, equalsMethod, false);
- fixMethod(ctClass, hashCodeMethod, false);
- fixMethod(ctClass, toStringMethod, false);
- }
-
- private String describe(CtMethod ctMethod) throws NotFoundException {
- return Modifier.toString(ctMethod.getModifiers()) + " " + ctMethod.getReturnType().getSimpleName() + " " + ctMethod.getLongName();
- }
-
- private void fixMethod(CtClass ctClass, CtMethod ctMethod, boolean wasFoundInClass) throws NotFoundException {
- String describeBefore = describe(ctMethod);
- try {
- CtClass declaringClass = ctMethod.getDeclaringClass();
- int originalModifiers = ctMethod.getModifiers();
-
- boolean wasNative = Modifier.isNative(originalModifiers);
- boolean wasFinal = Modifier.isFinal(originalModifiers);
- boolean wasAbstract = Modifier.isAbstract(originalModifiers);
- boolean wasDeclaredInClass = ctClass == declaringClass;
-
- if (wasFinal && ctClass.isEnum()) {
- return;
- }
-
- int newModifiers = originalModifiers;
- if (wasNative) {
- newModifiers = Modifier.clear(newModifiers, Modifier.NATIVE);
- }
- if (wasFinal) {
- newModifiers = Modifier.clear(newModifiers, Modifier.FINAL);
- }
- if (wasFoundInClass) {
- ctMethod.setModifiers(newModifiers);
- }
-
- CtClass returnCtClass = ctMethod.getReturnType();
- Type returnType = Type.find(returnCtClass);
-
- String methodName = ctMethod.getName();
- CtClass[] paramTypes = ctMethod.getParameterTypes();
-
-// if (!isAbstract) {
-// if (methodName.startsWith("set") && paramTypes.length == 1) {
-// String fieldName = "__" + methodName.substring(3);
-// if (declareField(ctClass, fieldName, paramTypes[0])) {
-// methodBody = fieldName + " = $1;\n" + methodBody;
-// }
-// } else if (methodName.startsWith("get") && paramTypes.length == 0) {
-// String fieldName = "__" + methodName.substring(3);
-// if (declareField(ctClass, fieldName, returnType)) {
-// methodBody = "return " + fieldName + ";\n";
-// }
-// }
-// }
-
- boolean isStatic = Modifier.isStatic(originalModifiers);
- String methodBody = generateMethodBody(ctClass, ctMethod, wasNative, wasAbstract, returnCtClass, returnType, isStatic, !wasFoundInClass);
-
- if (!wasFoundInClass) {
- CtMethod newMethod = makeNewMethod(ctClass, ctMethod, returnCtClass, methodName, paramTypes, "{\n" + methodBody + generateCallToSuper(methodName, paramTypes) + "\n}");
- newMethod.setModifiers(newModifiers);
- if (wasDeclaredInClass) {
- ctMethod.insertBefore("{\n" + methodBody + "}\n");
- } else {
- ctClass.addMethod(newMethod);
- }
- } else if (wasAbstract || wasNative) {
- CtMethod newMethod = makeNewMethod(ctClass, ctMethod, returnCtClass, methodName, paramTypes, "{\n" + methodBody + "\n}");
- ctMethod.setBody(newMethod, null);
- } else {
- ctMethod.insertBefore("{\n" + methodBody + "}\n");
- }
- } catch (Exception e) {
- throw new RuntimeException("problem instrumenting " + describeBefore, e);
- }
- }
-
- private CtMethod makeNewMethod(CtClass ctClass, CtMethod ctMethod, CtClass returnCtClass, String methodName, CtClass[] paramTypes, String methodBody) throws CannotCompileException, NotFoundException {
- return CtNewMethod.make(
- ctMethod.getModifiers(),
- returnCtClass,
- methodName,
- paramTypes,
- ctMethod.getExceptionTypes(),
- methodBody,
- ctClass);
- }
-
- public String generateCallToSuper(String methodName, CtClass[] paramTypes) {
- return "return super." + methodName + "(" + makeParameterReplacementList(paramTypes.length) + ");";
- }
-
- public String makeParameterReplacementList(int length) {
- if (length == 0) {
- return "";
- }
-
- String parameterReplacementList = "$1";
- for (int i = 2; i <= length; ++i) {
- parameterReplacementList += ", $" + i;
- }
- return parameterReplacementList;
- }
-
- private String generateMethodBody(CtClass ctClass, CtMethod ctMethod, boolean wasNative, boolean wasAbstract, CtClass returnCtClass, Type returnType, boolean aStatic, boolean shouldGenerateCallToSuper) throws NotFoundException {
- String methodBody;
- if (wasAbstract) {
- methodBody = returnType.isVoid() ? "" : "return " + returnType.defaultReturnString() + ";";
- } else {
- methodBody = generateMethodBody(ctClass, ctMethod, returnCtClass, returnType, aStatic, shouldGenerateCallToSuper);
- }
-
- if (wasNative) {
- methodBody += returnType.isVoid() ? "" : "return " + returnType.defaultReturnString() + ";";
- }
- return methodBody;
- }
-
- public String generateMethodBody(CtClass ctClass, CtMethod ctMethod, CtClass returnCtClass, Type returnType, boolean isStatic, boolean shouldGenerateCallToSuper) throws NotFoundException {
- boolean returnsVoid = returnType.isVoid();
- String className = ctClass.getName();
-
- String methodBody;
- StringBuilder buf = new StringBuilder();
- buf.append("if (!");
- buf.append(RobolectricInternals.class.getName());
- buf.append(".shouldCallDirectly(");
- buf.append(isStatic ? className + ".class" : "this");
- buf.append(")) {\n");
-
- if (!returnsVoid) {
- buf.append("Object x = ");
- }
- buf.append(RobolectricInternals.class.getName());
- buf.append(".methodInvoked(\n ");
- buf.append(className);
- buf.append(".class, \"");
- buf.append(ctMethod.getName());
- buf.append("\", ");
- if (!isStatic) {
- buf.append("this");
- } else {
- buf.append("null");
- }
- buf.append(", ");
-
- appendParamTypeArray(buf, ctMethod);
- buf.append(", ");
- appendParamArray(buf, ctMethod);
-
- buf.append(")");
- buf.append(";\n");
-
- if (!returnsVoid) {
- buf.append("if (x != null) return ((");
- buf.append(returnType.nonPrimitiveClassName(returnCtClass));
- buf.append(") x)");
- buf.append(returnType.unboxString());
- buf.append(";\n");
- if (shouldGenerateCallToSuper) {
- buf.append(generateCallToSuper(ctMethod.getName(), ctMethod.getParameterTypes()));
- } else {
- buf.append("return ");
- buf.append(returnType.defaultReturnString());
- buf.append(";\n");
- }
- } else {
- buf.append("return;\n");
- }
-
- buf.append("}\n");
-
- methodBody = buf.toString();
- return methodBody;
- }
-
- private void appendParamTypeArray(StringBuilder buf, CtMethod ctMethod) throws NotFoundException {
- CtClass[] parameterTypes = ctMethod.getParameterTypes();
- if (parameterTypes.length == 0) {
- buf.append("new String[0]");
- } else {
- buf.append("new String[] {");
- for (int i = 0; i < parameterTypes.length; i++) {
- if (i > 0) buf.append(", ");
- buf.append("\"");
- CtClass parameterType = parameterTypes[i];
- buf.append(parameterType.getName());
- buf.append("\"");
- }
- buf.append("}");
- }
- }
-
- private void appendParamArray(StringBuilder buf, CtMethod ctMethod) throws NotFoundException {
- int parameterCount = ctMethod.getParameterTypes().length;
- if (parameterCount == 0) {
- buf.append("new Object[0]");
- } else {
- buf.append("new Object[] {");
- for (int i = 0; i < parameterCount; i++) {
- if (i > 0) buf.append(", ");
- buf.append(RobolectricInternals.class.getName());
- buf.append(".autobox(");
- buf.append("$").append(i + 1);
- buf.append(")");
- }
- buf.append("}");
- }
- }
-
- private boolean declareField(CtClass ctClass, String fieldName, CtClass fieldType) throws CannotCompileException, NotFoundException {
- CtMethod ctMethod = getMethod(ctClass, "get" + fieldName, "");
- if (ctMethod == null) {
- return false;
- }
- CtClass getterFieldType = ctMethod.getReturnType();
-
- if (!getterFieldType.equals(fieldType)) {
- return false;
- }
-
- if (getField(ctClass, fieldName) == null) {
- CtField field = new CtField(fieldType, fieldName, ctClass);
- field.setModifiers(Modifier.PRIVATE);
- ctClass.addField(field);
- }
-
- return true;
- }
-
- private CtField getField(CtClass ctClass, String fieldName) {
- try {
- return ctClass.getField(fieldName);
- } catch (NotFoundException e) {
- return null;
- }
- }
-
- private CtMethod getMethod(CtClass ctClass, String methodName, String desc) {
- try {
- return ctClass.getMethod(methodName, desc);
- } catch (NotFoundException e) {
- return null;
- }
- }
-
-}