aboutsummaryrefslogtreecommitdiffstats
path: root/src/proguard/optimize/peephole/TargetClassChanger.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/optimize/peephole/TargetClassChanger.java')
-rw-r--r--src/proguard/optimize/peephole/TargetClassChanger.java439
1 files changed, 439 insertions, 0 deletions
diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java
new file mode 100644
index 0000000..22fd83d
--- /dev/null
+++ b/src/proguard/optimize/peephole/TargetClassChanger.java
@@ -0,0 +1,439 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassVisitor replaces references to classes and class members if the
+ * classes have targets that are intended to replace them.
+ *
+ * @see VerticalClassMerger
+ * @see ClassReferenceFixer
+ * @see MemberReferenceFixer
+ * @author Eric Lafortune
+ */
+public class TargetClassChanger
+extends SimplifiedVisitor
+implements ClassVisitor,
+ ConstantVisitor,
+ MemberVisitor,
+ AttributeVisitor,
+ LocalVariableInfoVisitor,
+ LocalVariableTypeInfoVisitor,
+ AnnotationVisitor,
+ ElementValueVisitor
+{
+ // Implementations for ClassVisitor.
+
+ public void visitProgramClass(ProgramClass programClass)
+ {
+ Clazz superClass = null;
+ Clazz[] interfaceClasses = null;
+
+ // Change the references of the constant pool.
+ programClass.constantPoolEntriesAccept(this);
+
+ // Change the references of the class members.
+ programClass.fieldsAccept(this);
+ programClass.methodsAccept(this);
+
+ // Change the references of the attributes.
+ programClass.attributesAccept(this);
+
+ // Is the class itself being retargeted?
+ Clazz targetClass = ClassMerger.getTargetClass(programClass);
+ if (targetClass != null)
+ {
+ // Restore the class name. We have to add a new class entry
+ // to avoid an existing entry with the same name being reused. The
+ // names have to be fixed later, based on their referenced classes.
+ programClass.u2thisClass =
+ addNewClassConstant(programClass,
+ programClass.getName(),
+ programClass);
+
+ // This class will loose all its subclasses.
+ programClass.subClasses = null;
+ }
+
+ // Remove interface classes that are pointing to this class.
+ int newInterfacesCount = 0;
+ for (int index = 0; index < programClass.u2interfacesCount; index++)
+ {
+ Clazz interfaceClass = programClass.getInterface(index);
+ if (!programClass.equals(interfaceClass))
+ {
+ programClass.u2interfaces[newInterfacesCount++] =
+ programClass.u2interfaces[index];
+ }
+ }
+ programClass.u2interfacesCount = newInterfacesCount;
+
+ // Update the subclasses of the superclass and interfaces of the
+ // target class.
+ ConstantVisitor subclassAdder =
+ new ReferencedClassVisitor(
+ new SubclassFilter(programClass,
+ new SubclassAdder(programClass)));
+
+ programClass.superClassConstantAccept(subclassAdder);
+ programClass.interfaceConstantsAccept(subclassAdder);
+
+ // TODO: Maybe restore private method references.
+ }
+
+
+ public void visitLibraryClass(LibraryClass libraryClass)
+ {
+ // Change the references of the class members.
+ libraryClass.fieldsAccept(this);
+ libraryClass.methodsAccept(this);
+ }
+
+
+ // Implementations for MemberVisitor.
+
+ public void visitProgramField(ProgramClass programClass, ProgramField programField)
+ {
+ // Change the referenced class.
+ programField.referencedClass =
+ updateReferencedClass(programField.referencedClass);
+
+ // Change the references of the attributes.
+ programField.attributesAccept(programClass, this);
+ }
+
+
+ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+ {
+ // Change the referenced classes.
+ updateReferencedClasses(programMethod.referencedClasses);
+
+ // Change the references of the attributes.
+ programMethod.attributesAccept(programClass, this);
+ }
+
+
+ public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+ {
+ // Change the referenced class.
+ libraryField.referencedClass =
+ updateReferencedClass(libraryField.referencedClass);
+ }
+
+
+ public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+ {
+ // Change the referenced classes.
+ updateReferencedClasses(libraryMethod.referencedClasses);
+ }
+
+
+ // Implementations for ConstantVisitor.
+
+ public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+ public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+ {
+ // Does the string refer to a class, due to a Class.forName construct?
+ Clazz referencedClass = stringConstant.referencedClass;
+ Clazz newReferencedClass = updateReferencedClass(referencedClass);
+ if (referencedClass != newReferencedClass)
+ {
+ // Change the referenced class.
+ stringConstant.referencedClass = newReferencedClass;
+
+ // Change the referenced class member, if applicable.
+ stringConstant.referencedMember =
+ updateReferencedMember(stringConstant.referencedMember,
+ stringConstant.getString(clazz),
+ null,
+ newReferencedClass);
+ }
+ }
+
+
+ public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+ {
+ Clazz referencedClass = refConstant.referencedClass;
+ Clazz newReferencedClass = updateReferencedClass(referencedClass);
+ if (referencedClass != newReferencedClass)
+ {
+ // Change the referenced class.
+ refConstant.referencedClass = newReferencedClass;
+
+ // Change the referenced class member.
+ refConstant.referencedMember =
+ updateReferencedMember(refConstant.referencedMember,
+ refConstant.getName(clazz),
+ refConstant.getType(clazz),
+ newReferencedClass);
+ }
+ }
+
+
+ public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+ {
+ // Change the referenced class.
+ classConstant.referencedClass =
+ updateReferencedClass(classConstant.referencedClass);
+ }
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ // Change the references of the attributes.
+ codeAttribute.attributesAccept(clazz, method, this);
+ }
+
+
+ public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+ {
+ // Change the references of the local variables.
+ localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+ }
+
+
+ public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+ {
+ // Change the references of the local variables.
+ localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+ }
+
+
+ public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+ {
+ // Change the referenced classes.
+ updateReferencedClasses(signatureAttribute.referencedClasses);
+ }
+
+
+ public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+ {
+ // Change the references of the annotations.
+ annotationsAttribute.annotationsAccept(clazz, this);
+ }
+
+
+ public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+ {
+ // Change the references of the annotations.
+ parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+ }
+
+
+ public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+ {
+ // Change the references of the annotation.
+ annotationDefaultAttribute.defaultValueAccept(clazz, this);
+ }
+
+
+
+ // Implementations for LocalVariableInfoVisitor.
+
+ public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+ {
+ // Change the referenced class.
+ localVariableInfo.referencedClass =
+ updateReferencedClass(localVariableInfo.referencedClass);
+ }
+
+ // Implementations for LocalVariableTypeInfoVisitor.
+
+ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+ {
+ // Change the referenced classes.
+ updateReferencedClasses(localVariableTypeInfo.referencedClasses);
+ }
+
+ // Implementations for AnnotationVisitor.
+
+ public void visitAnnotation(Clazz clazz, Annotation annotation)
+ {
+ // Change the referenced classes.
+ updateReferencedClasses(annotation.referencedClasses);
+
+ // Change the references of the element values.
+ annotation.elementValuesAccept(clazz, this);
+ }
+
+
+ // Implementations for ElementValueVisitor.
+
+ public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+ {
+ Clazz referencedClass = elementValue.referencedClass;
+ Clazz newReferencedClass = updateReferencedClass(referencedClass);
+ if (referencedClass != newReferencedClass)
+ {
+ // Change the referenced annotation class.
+ elementValue.referencedClass = newReferencedClass;
+
+ // Change the referenced method.
+ elementValue.referencedMethod =
+ (Method)updateReferencedMember(elementValue.referencedMethod,
+ elementValue.getMethodName(clazz),
+ null,
+ newReferencedClass);
+ }
+ }
+
+
+ public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+ {
+ // Change the referenced annotation class and method.
+ visitAnyElementValue(clazz, annotation, constantElementValue);
+ }
+
+
+ public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+ {
+ // Change the referenced annotation class and method.
+ visitAnyElementValue(clazz, annotation, enumConstantElementValue);
+
+ // Change the referenced classes.
+ updateReferencedClasses(enumConstantElementValue.referencedClasses);
+ }
+
+
+ public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+ {
+ // Change the referenced annotation class and method.
+ visitAnyElementValue(clazz, annotation, classElementValue);
+
+ // Change the referenced classes.
+ updateReferencedClasses(classElementValue.referencedClasses);
+ }
+
+
+ public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+ {
+ // Change the referenced annotation class and method.
+ visitAnyElementValue(clazz, annotation, annotationElementValue);
+
+ // Change the references of the annotation.
+ annotationElementValue.annotationAccept(clazz, this);
+ }
+
+
+ public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+ {
+ // Change the referenced annotation class and method.
+ visitAnyElementValue(clazz, annotation, arrayElementValue);
+
+ // Change the references of the element values.
+ arrayElementValue.elementValuesAccept(clazz, annotation, this);
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Updates the retargeted classes in the given array of classes.
+ */
+ private void updateReferencedClasses(Clazz[] referencedClasses)
+ {
+ if (referencedClasses == null)
+ {
+ return;
+ }
+
+ for (int index = 0; index < referencedClasses.length; index++)
+ {
+ referencedClasses[index] =
+ updateReferencedClass(referencedClasses[index]);
+ }
+ }
+
+
+ /**
+ * Returns the retargeted class of the given class.
+ */
+ private Clazz updateReferencedClass(Clazz referencedClass)
+ {
+ if (referencedClass == null)
+ {
+ return null;
+ }
+
+ Clazz targetClazz = ClassMerger.getTargetClass(referencedClass);
+ return targetClazz != null ?
+ targetClazz :
+ referencedClass;
+ }
+
+
+ /**
+ * Returns the retargeted class member of the given class member.
+ */
+ private Member updateReferencedMember(Member referencedMember,
+ String name,
+ String type,
+ Clazz newReferencedClass)
+ {
+ if (referencedMember == null)
+ {
+ return null;
+ }
+
+ return referencedMember instanceof Field ?
+ (Member)newReferencedClass.findField(name, type) :
+ (Member)newReferencedClass.findMethod(name, type);
+ }
+
+
+ /**
+ * Explicitly adds a new class constant for the given class in the given
+ * program class.
+ */
+ private int addNewClassConstant(ProgramClass programClass,
+ String className,
+ Clazz referencedClass)
+ {
+ ConstantPoolEditor constantPoolEditor =
+ new ConstantPoolEditor(programClass);
+
+ int nameIndex =
+ constantPoolEditor.addUtf8Constant(className);
+
+ int classConstantIndex =
+ constantPoolEditor.addConstant(new ClassConstant(nameIndex,
+ referencedClass));
+ return classConstantIndex;
+ }
+} \ No newline at end of file