diff options
Diffstat (limited to 'src/proguard/classfile')
259 files changed, 38427 insertions, 0 deletions
diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java new file mode 100644 index 0000000..3b243e0 --- /dev/null +++ b/src/proguard/classfile/ClassConstants.java @@ -0,0 +1,255 @@ +/* + * 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.classfile; + +/** + * Constants used in representing a Java class (*.class). + * + * @author Eric Lafortune + */ +public interface ClassConstants +{ + public static final String CLASS_FILE_EXTENSION = ".class"; + + public static final int MAGIC = 0xCAFEBABE; + + public static final int INTERNAL_CLASS_VERSION_1_0_MAJOR = 45; + public static final int INTERNAL_CLASS_VERSION_1_0_MINOR = 3; + public static final int INTERNAL_CLASS_VERSION_1_2_MAJOR = 46; + public static final int INTERNAL_CLASS_VERSION_1_2_MINOR = 0; + public static final int INTERNAL_CLASS_VERSION_1_3_MAJOR = 47; + public static final int INTERNAL_CLASS_VERSION_1_3_MINOR = 0; + public static final int INTERNAL_CLASS_VERSION_1_4_MAJOR = 48; + public static final int INTERNAL_CLASS_VERSION_1_4_MINOR = 0; + public static final int INTERNAL_CLASS_VERSION_1_5_MAJOR = 49; + public static final int INTERNAL_CLASS_VERSION_1_5_MINOR = 0; + public static final int INTERNAL_CLASS_VERSION_1_6_MAJOR = 50; + public static final int INTERNAL_CLASS_VERSION_1_6_MINOR = 0; + + public static final int INTERNAL_CLASS_VERSION_1_0 = (INTERNAL_CLASS_VERSION_1_0_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_0_MINOR; + public static final int INTERNAL_CLASS_VERSION_1_2 = (INTERNAL_CLASS_VERSION_1_2_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_2_MINOR; + public static final int INTERNAL_CLASS_VERSION_1_3 = (INTERNAL_CLASS_VERSION_1_3_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_3_MINOR; + public static final int INTERNAL_CLASS_VERSION_1_4 = (INTERNAL_CLASS_VERSION_1_4_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_4_MINOR; + public static final int INTERNAL_CLASS_VERSION_1_5 = (INTERNAL_CLASS_VERSION_1_5_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_5_MINOR; + public static final int INTERNAL_CLASS_VERSION_1_6 = (INTERNAL_CLASS_VERSION_1_6_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_6_MINOR; + + public static final String EXTERNAL_CLASS_VERSION_1_0 = "1.0"; + public static final String EXTERNAL_CLASS_VERSION_1_1 = "1.1"; + public static final String EXTERNAL_CLASS_VERSION_1_2 = "1.2"; + public static final String EXTERNAL_CLASS_VERSION_1_3 = "1.3"; + public static final String EXTERNAL_CLASS_VERSION_1_4 = "1.4"; + public static final String EXTERNAL_CLASS_VERSION_1_5 = "1.5"; + public static final String EXTERNAL_CLASS_VERSION_1_6 = "1.6"; + public static final String EXTERNAL_CLASS_VERSION_1_5_ALIAS = "5"; + public static final String EXTERNAL_CLASS_VERSION_1_6_ALIAS = "6"; + + public static final int INTERNAL_ACC_PUBLIC = 0x0001; + public static final int INTERNAL_ACC_PRIVATE = 0x0002; + public static final int INTERNAL_ACC_PROTECTED = 0x0004; + public static final int INTERNAL_ACC_STATIC = 0x0008; + public static final int INTERNAL_ACC_FINAL = 0x0010; + public static final int INTERNAL_ACC_SUPER = 0x0020; + public static final int INTERNAL_ACC_SYNCHRONIZED = 0x0020; + public static final int INTERNAL_ACC_VOLATILE = 0x0040; + public static final int INTERNAL_ACC_TRANSIENT = 0x0080; + public static final int INTERNAL_ACC_BRIDGE = 0x0040; + public static final int INTERNAL_ACC_VARARGS = 0x0080; + public static final int INTERNAL_ACC_NATIVE = 0x0100; + public static final int INTERNAL_ACC_INTERFACE = 0x0200; + public static final int INTERNAL_ACC_ABSTRACT = 0x0400; + public static final int INTERNAL_ACC_STRICT = 0x0800; + public static final int INTERNAL_ACC_SYNTHETIC = 0x1000; + public static final int INTERNAL_ACC_ANNOTATTION = 0x2000; + public static final int INTERNAL_ACC_ENUM = 0x4000; + + public static final int VALID_INTERNAL_ACC_CLASS = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_SUPER | + INTERNAL_ACC_INTERFACE | + INTERNAL_ACC_ABSTRACT | + INTERNAL_ACC_SYNTHETIC | + INTERNAL_ACC_ANNOTATTION | + INTERNAL_ACC_ENUM; + public static final int VALID_INTERNAL_ACC_FIELD = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_PRIVATE | + INTERNAL_ACC_PROTECTED | + INTERNAL_ACC_STATIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_VOLATILE | + INTERNAL_ACC_TRANSIENT | + INTERNAL_ACC_SYNTHETIC | + INTERNAL_ACC_ENUM; + public static final int VALID_INTERNAL_ACC_METHOD = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_PRIVATE | + INTERNAL_ACC_PROTECTED | + INTERNAL_ACC_STATIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_SYNCHRONIZED | + INTERNAL_ACC_BRIDGE | + INTERNAL_ACC_VARARGS | + INTERNAL_ACC_NATIVE | + INTERNAL_ACC_ABSTRACT | + INTERNAL_ACC_STRICT | + INTERNAL_ACC_SYNTHETIC; + + public static final String EXTERNAL_ACC_PUBLIC = "public"; + public static final String EXTERNAL_ACC_PRIVATE = "private"; + public static final String EXTERNAL_ACC_PROTECTED = "protected"; + public static final String EXTERNAL_ACC_STATIC = "static"; + public static final String EXTERNAL_ACC_FINAL = "final"; + public static final String EXTERNAL_ACC_SUPER = "super"; + public static final String EXTERNAL_ACC_SYNCHRONIZED = "synchronized"; + public static final String EXTERNAL_ACC_VOLATILE = "volatile"; + public static final String EXTERNAL_ACC_TRANSIENT = "transient"; + public static final String EXTERNAL_ACC_NATIVE = "native"; + public static final String EXTERNAL_ACC_INTERFACE = "interface"; + public static final String EXTERNAL_ACC_ABSTRACT = "abstract"; + public static final String EXTERNAL_ACC_STRICT = "strictfp"; + public static final String EXTERNAL_ACC_ANNOTATION = "@"; + public static final String EXTERNAL_ACC_ENUM = "enum"; + + public static final int CONSTANT_Utf8 = 1; + public static final int CONSTANT_Integer = 3; + public static final int CONSTANT_Float = 4; + public static final int CONSTANT_Long = 5; + public static final int CONSTANT_Double = 6; + public static final int CONSTANT_Class = 7; + public static final int CONSTANT_String = 8; + public static final int CONSTANT_Fieldref = 9; + public static final int CONSTANT_Methodref = 10; + public static final int CONSTANT_InterfaceMethodref = 11; + public static final int CONSTANT_NameAndType = 12; + + public static final String ATTR_SourceFile = "SourceFile"; + public static final String ATTR_SourceDir = "SourceDir"; + public static final String ATTR_InnerClasses = "InnerClasses"; + public static final String ATTR_EnclosingMethod = "EnclosingMethod"; + public static final String ATTR_Deprecated = "Deprecated"; + public static final String ATTR_Synthetic = "Synthetic"; + public static final String ATTR_Signature = "Signature"; + public static final String ATTR_ConstantValue = "ConstantValue"; + public static final String ATTR_Exceptions = "Exceptions"; + public static final String ATTR_Code = "Code"; + public static final String ATTR_StackMap = "StackMap"; + public static final String ATTR_StackMapTable = "StackMapTable"; + public static final String ATTR_LineNumberTable = "LineNumberTable"; + public static final String ATTR_LocalVariableTable = "LocalVariableTable"; + public static final String ATTR_LocalVariableTypeTable = "LocalVariableTypeTable"; + public static final String ATTR_RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations"; + public static final String ATTR_RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations"; + public static final String ATTR_RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations"; + public static final String ATTR_RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTR_AnnotationDefault = "AnnotationDefault"; + + public static final int ELEMENT_VALUE_STRING_CONSTANT = 's'; + public static final int ELEMENT_VALUE_ENUM_CONSTANT = 'e'; + public static final int ELEMENT_VALUE_CLASS = 'c'; + public static final int ELEMENT_VALUE_ANNOTATION = '@'; + public static final int ELEMENT_VALUE_ARRAY = '['; + + public static final char EXTERNAL_PACKAGE_SEPARATOR = '.'; + public static final char EXTERNAL_INNER_CLASS_SEPARATOR = '.'; + public static final char INTERNAL_PACKAGE_SEPARATOR = '/'; + public static final char INTERNAL_INNER_CLASS_SEPARATOR = '$'; + public static final char SPECIAL_CLASS_CHARACTER = '-'; + public static final char SPECIAL_MEMBER_SEPARATOR = '$'; + + public static final char EXTERNAL_METHOD_ARGUMENTS_OPEN = '('; + public static final char EXTERNAL_METHOD_ARGUMENTS_CLOSE = ')'; + public static final char EXTERNAL_METHOD_ARGUMENTS_SEPARATOR = ','; + + public static final char INTERNAL_METHOD_ARGUMENTS_OPEN = '('; + public static final char INTERNAL_METHOD_ARGUMENTS_CLOSE = ')'; + + public static final String INTERNAL_PACKAGE_JAVA_LANG = "java/lang/"; + public static final String INTERNAL_NAME_JAVA_LANG_OBJECT = "java/lang/Object"; + public static final String INTERNAL_TYPE_JAVA_LANG_OBJECT = "Ljava/lang/Object;"; + public static final String INTERNAL_NAME_JAVA_LANG_CLONEABLE = "java/lang/Cloneable"; + public static final String INTERNAL_NAME_JAVA_LANG_THROWABLE = "java/lang/Throwable"; + public static final String INTERNAL_NAME_JAVA_LANG_CLASS = "java/lang/Class"; + public static final String INTERNAL_NAME_JAVA_LANG_STRING = "java/lang/String"; + public static final String INTERNAL_NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable"; + + public static final String INTERNAL_METHOD_NAME_INIT = "<init>"; + public static final String INTERNAL_METHOD_TYPE_INIT = "()V"; + public static final String INTERNAL_METHOD_NAME_CLINIT = "<clinit>"; + public static final String INTERNAL_METHOD_TYPE_CLINIT = "()V"; + + public static final String INTERNAL_METHOD_NAME_CLASS_FOR_NAME = "forName"; + public static final String INTERNAL_METHOD_TYPE_CLASS_FOR_NAME = "(Ljava/lang/String;)Ljava/lang/Class;"; + public static final String INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE = "getComponentType"; + public static final String INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE = "()Ljava/lang/Class;"; + public static final String INTERNAL_METHOD_NAME_CLASS_GET_FIELD = "getField"; + public static final String INTERNAL_METHOD_TYPE_CLASS_GET_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; + public static final String INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_FIELD = "getDeclaredField"; + public static final String INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; + public static final String INTERNAL_METHOD_NAME_CLASS_GET_METHOD = "getMethod"; + public static final String INTERNAL_METHOD_TYPE_CLASS_GET_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; + public static final String INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_METHOD = "getDeclaredMethod"; + public static final String INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; + + public static final String INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC = "class$"; + public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC = "(Ljava/lang/String;)Ljava/lang/Class;"; + public static final String INTERNAL_METHOD_NAME_DOT_CLASS_JIKES = "class"; + public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES = "(Ljava/lang/String;Z)Ljava/lang/Class;"; + + public static final String INTERNAL_METHOD_NAME_NEW_INSTANCE = "newInstance"; + public static final String INTERNAL_METHOD_TYPE_NEW_INSTANCE = "()Ljava/lang/Object;"; + + public static final char INTERNAL_TYPE_VOID = 'V'; + public static final char INTERNAL_TYPE_BOOLEAN = 'Z'; + public static final char INTERNAL_TYPE_BYTE = 'B'; + public static final char INTERNAL_TYPE_CHAR = 'C'; + public static final char INTERNAL_TYPE_SHORT = 'S'; + public static final char INTERNAL_TYPE_INT = 'I'; + public static final char INTERNAL_TYPE_LONG = 'J'; + public static final char INTERNAL_TYPE_FLOAT = 'F'; + public static final char INTERNAL_TYPE_DOUBLE = 'D'; + public static final char INTERNAL_TYPE_CLASS_START = 'L'; + public static final char INTERNAL_TYPE_CLASS_END = ';'; + public static final char INTERNAL_TYPE_ARRAY = '['; + public static final char INTERNAL_TYPE_GENERIC_VARIABLE_START = 'T'; + public static final char INTERNAL_TYPE_GENERIC_START = '<'; + public static final char INTERNAL_TYPE_GENERIC_BOUND = ':'; + public static final char INTERNAL_TYPE_GENERIC_END = '>'; + + public static final String EXTERNAL_TYPE_JAVA_LANG_OBJECT = "java.lang.Object"; + public static final String EXTERNAL_PACKAGE_JAVA_LANG = "java.lang."; + + public static final String EXTERNAL_TYPE_VOID = "void"; + public static final String EXTERNAL_TYPE_BOOLEAN = "boolean"; + public static final String EXTERNAL_TYPE_BYTE = "byte"; + public static final String EXTERNAL_TYPE_CHAR = "char"; + public static final String EXTERNAL_TYPE_SHORT = "short"; + public static final String EXTERNAL_TYPE_INT = "int"; + public static final String EXTERNAL_TYPE_FLOAT = "float"; + public static final String EXTERNAL_TYPE_LONG = "long"; + public static final String EXTERNAL_TYPE_DOUBLE = "double"; + public static final String EXTERNAL_TYPE_ARRAY = "[]"; + + public static final int TYPICAL_CONSTANT_POOL_SIZE = 256; + public static final int TYPICAL_FIELD_COUNT = 64; + public static final int TYPICAL_METHOD_COUNT = 64; + public static final int TYPICAL_CODE_LENGTH = 1024; + public static final int TYPICAL_EXCEPTION_TABLE_LENGTH = 16; + public static final int TYPICAL_VARIABLES_SIZE = 64; + public static final int TYPICAL_STACK_SIZE = 16; +} diff --git a/src/proguard/classfile/ClassPool.java b/src/proguard/classfile/ClassPool.java new file mode 100644 index 0000000..57728a5 --- /dev/null +++ b/src/proguard/classfile/ClassPool.java @@ -0,0 +1,147 @@ +/* + * 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.classfile; + +import proguard.classfile.util.ClassUtil; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * This is a set of representations of classes. They can be enumerated or + * retrieved by name. They can also be accessed by means of class visitors. + * + * @author Eric Lafortune + */ +public class ClassPool +{ + private final Map classes = new HashMap(); + + + /** + * Clears the class pool. + */ + public void clear() + { + classes.clear(); + } + + + /** + * Adds the given Clazz to the class pool. + */ + public void addClass(Clazz clazz) + { + classes.put(clazz.getName(), clazz); + } + + + /** + * Removes the given Clazz from the class pool. + */ + public void removeClass(Clazz clazz) + { + classes.remove(clazz.getName()); + } + + + /** + * Returns a Clazz from the class pool based on its name. Returns + * <code>null</code> if the class with the given name is not in the class + * pool. Returns the base class if the class name is an array type. + */ + public Clazz getClass(String className) + { + return (Clazz)classes.get(ClassUtil.internalClassNameFromClassType(className)); + } + + + /** + * Returns an Iterator of all class names in the class pool. + */ + public Iterator classNames() + { + return classes.keySet().iterator(); + } + + + /** + * Returns the number of classes in the class pool. + */ + public int size() + { + return classes.size(); + } + + + /** + * Applies the given ClassPoolVisitor to the class pool. + */ + public void accept(ClassPoolVisitor classPoolVisitor) + { + classPoolVisitor.visitClassPool(this); + } + + + /** + * Applies the given ClassVisitor to all classes in the class pool, + * in random order. + */ + public void classesAccept(ClassVisitor classVisitor) + { + Iterator iterator = classes.values().iterator(); + while (iterator.hasNext()) + { + Clazz clazz = (Clazz)iterator.next(); + clazz.accept(classVisitor); + } + } + + + /** + * Applies the given ClassVisitor to all classes in the class pool, + * in sorted order. + */ + public void classesAcceptAlphabetically(ClassVisitor classVisitor) + { + TreeMap sortedClasses = new TreeMap(classes); + Iterator iterator = sortedClasses.values().iterator(); + while (iterator.hasNext()) + { + Clazz clazz = (Clazz)iterator.next(); + clazz.accept(classVisitor); + } + } + + + /** + * Applies the given ClassVisitor to the class with the given name, + * if it is present in the class pool. + */ + public void classAccept(String className, ClassVisitor classVisitor) + { + Clazz clazz = getClass(className); + if (clazz != null) + { + clazz.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/Clazz.java b/src/proguard/classfile/Clazz.java new file mode 100644 index 0000000..da37d9a --- /dev/null +++ b/src/proguard/classfile/Clazz.java @@ -0,0 +1,232 @@ +/* + * 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.classfile; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.*; + + +/** + * This interface provides access to the representation of a Java class. + * + * @author Eric Lafortune + */ +public interface Clazz extends VisitorAccepter +{ + /** + * Returns the access flags of this class. + * @see ClassConstants + */ + public int getAccessFlags(); + + /** + * Returns the full internal name of this class. + */ + public String getName(); + + /** + * Returns the full internal name of the super class of this class, or + * null if this class represents java.lang.Object. + */ + public String getSuperName(); + + /** + * Returns the number of interfaces that this class implements. + */ + public int getInterfaceCount(); + + /** + * Returns the full internal name of the interface at the given index of + * this class. + */ + public String getInterfaceName(int index); + + /** + * Returns the tag value of the Constant at the specified index. + */ + public int getTag(int constantIndex); + + /** + * Returns the String value of the Utf8Constant at the specified index. + */ + public String getString(int constantIndex); + + /** + * Returns the String value of the StringConstant at the specified index. + */ + public String getStringString(int constantIndex); + + /** + * Returns the class name of ClassConstant at the specified index. + */ + public String getClassName(int constantIndex); + + /** + * Returns the name of the NameAndTypeConstant at the specified index. + */ + public String getName(int constantIndex); + + /** + * Returns the type of the NameAndTypeConstant at the specified index. + */ + public String getType(int constantIndex); + + + // Methods pertaining to related classes. + + /** + * Notifies this Clazz that it is being subclassed by another class. + */ + public void addSubClass(Clazz clazz); + + /** + * Returns the super class of this class. + */ + public Clazz getSuperClass(); + + /** + * Returns the interface at the given index. + */ + public Clazz getInterface(int index); + + /** + * Returns whether this class extends the given class. + * A class is always considered to extend itself. + * Interfaces are considered to only extend the root Object class. + */ + public boolean extends_(Clazz clazz); + + /** + * Returns whether this class implements the given class. + * A class is always considered to implement itself. + * Interfaces are considered to implement all their superinterfaces. + */ + public boolean extendsOrImplements(Clazz clazz); + + + // Methods for getting specific class members. + + /** + * Returns the field with the given name and descriptor. + */ + Field findField(String name, String descriptor); + + /** + * Returns the method with the given name and descriptor. + */ + Method findMethod(String name, String descriptor); + + + // Methods for accepting various types of visitors. + + /** + * Accepts the given class visitor. + */ + public void accept(ClassVisitor classVisitor); + + /** + * Accepts the given class visitor in the class hierarchy. + * @param visitThisClass specifies whether to visit this class. + * @param visitSuperClass specifies whether to visit the super classes. + * @param visitInterfaces specifies whether to visit the interfaces. + * @param visitSubclasses specifies whether to visit the subclasses. + * @param classVisitor the <code>ClassVisitor</code> that will + * visit the class hierarchy. + */ + public void hierarchyAccept(boolean visitThisClass, + boolean visitSuperClass, + boolean visitInterfaces, + boolean visitSubclasses, + ClassVisitor classVisitor); + + /** + * Lets the given class visitor visit all known subclasses. + * @param classVisitor the <code>ClassVisitor</code> that will visit the + * subclasses. + */ + public void subclassesAccept(ClassVisitor classVisitor); + + /** + * Lets the given constant pool entry visitor visit all constant pool entries + * of this class. + */ + public void constantPoolEntriesAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the constant pool entry + * at the specified index. + */ + public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entry of this class. + */ + public void thisClassConstantAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entry of the super class of this class, if there is one. + */ + public void superClassConstantAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entries for all interfaces of this class. + */ + public void interfaceConstantsAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given member info visitor visit all fields of this class. + */ + public void fieldsAccept(MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit the specified field. + */ + public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit all methods of this class. + */ + public void methodsAccept(MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit the specified method. + */ + public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor); + + /** + * Returns whether the given method may possibly have implementing or + * overriding methods down the class hierarchy. This can only be true + * if the class is not final, and the method is not private, static, or + * final, or a constructor. + * @param method the method that may have implementations. + * @return whether it may have implementations. + */ + public boolean mayHaveImplementations(Method method); + + /** + * Lets the given attribute info visitor visit all attributes of this class. + */ + public void attributesAccept(AttributeVisitor attributeVisitor); +} diff --git a/src/proguard/classfile/Field.java b/src/proguard/classfile/Field.java new file mode 100644 index 0000000..ba1315a --- /dev/null +++ b/src/proguard/classfile/Field.java @@ -0,0 +1,32 @@ +/* + * 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.classfile; + + + +/** + * Representation of a field from a class. + * + * @author Eric Lafortune + */ +public interface Field extends Member +{ +} diff --git a/src/proguard/classfile/LibraryClass.java b/src/proguard/classfile/LibraryClass.java new file mode 100644 index 0000000..0a27593 --- /dev/null +++ b/src/proguard/classfile/LibraryClass.java @@ -0,0 +1,489 @@ +/* + * 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.classfile; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This Clazz is a compact representation of the essential data in a Java class. + * + * @author Eric Lafortune + */ +public class LibraryClass implements Clazz +{ + public int u2accessFlags; + public String thisClassName; + public String superClassName; + public String[] interfaceNames; + public LibraryField[] fields; + public LibraryMethod[] methods; + + /** + * An extra field pointing to the superclass of this class. + * This field is filled out by the {@link ClassSuperHierarchyInitializer}. + */ + public Clazz superClass; + + /** + * An extra field pointing to the interfaces of this class. + * This field is filled out by the {@link ClassSuperHierarchyInitializer}. + */ + public Clazz[] interfaceClasses; + + /** + * An extra field pointing to the subclasses of this class. + * This field is filled out by the {@link ClassSubHierarchyInitializer}. + */ + public Clazz[] subClasses; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an empty LibraryClass. + */ + public LibraryClass() {} + + + /** + * Returns whether this library class is visible to the outside world. + */ + boolean isVisible() + { + return (u2accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0; + } + + + // Implementations for Clazz. + + public int getAccessFlags() + { + return u2accessFlags; + } + + public String getName() + { + return thisClassName; + } + + public String getSuperName() + { + // This may be java/lang/Object, in which case there is no super. + return superClassName; + } + + public int getInterfaceCount() + { + return interfaceClasses.length; + } + + public String getInterfaceName(int index) + { + return interfaceNames[index]; + } + + public int getTag(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getString(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getStringString(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getClassName(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getName(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getType(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + + public void addSubClass(Clazz clazz) + { + if (subClasses == null) + { + subClasses = new Clazz[1]; + } + else + { + // Copy the old elements into new larger array. + Clazz[] temp = new Clazz[subClasses.length+1]; + System.arraycopy(subClasses, 0, temp, 0, subClasses.length); + subClasses = temp; + } + + subClasses[subClasses.length-1] = clazz; + } + + + public Clazz getSuperClass() + { + return superClass; + } + + + public Clazz getInterface(int index) + { + return interfaceClasses[index]; + } + + + public boolean extends_(Clazz clazz) + { + if (this.equals(clazz)) + { + return true; + } + + return superClass != null && + superClass.extends_(clazz); + } + + + public boolean extendsOrImplements(Clazz clazz) + { + if (this.equals(clazz)) + { + return true; + } + + if (superClass != null && + superClass.extendsOrImplements(clazz)) + { + return true; + } + + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null && + interfaceClass.extendsOrImplements(clazz)) + { + return true; + } + } + } + + return false; + } + + + public Field findField(String name, String descriptor) + { + for (int index = 0; index < fields.length; index++) + { + Field field = fields[index]; + if (field != null && + (name == null || field.getName(this).equals(name)) && + (descriptor == null || field.getDescriptor(this).equals(descriptor))) + { + return field; + } + } + + return null; + } + + + public Method findMethod(String name, String descriptor) + { + for (int index = 0; index < methods.length; index++) + { + Method method = methods[index]; + if (method != null && + (name == null || method.getName(this).equals(name)) && + (descriptor == null || method.getDescriptor(this).equals(descriptor))) + { + return method; + } + } + + return null; + } + + + public void accept(ClassVisitor classVisitor) + { + classVisitor.visitLibraryClass(this); + } + + + public void hierarchyAccept(boolean visitThisClass, + boolean visitSuperClass, + boolean visitInterfaces, + boolean visitSubclasses, + ClassVisitor classVisitor) + { + // First visit the current classfile. + if (visitThisClass) + { + accept(classVisitor); + } + + // Then visit its superclass, recursively. + if (visitSuperClass) + { + if (superClass != null) + { + superClass.hierarchyAccept(true, + true, + visitInterfaces, + false, + classVisitor); + } + } + + // Then visit its interfaces, recursively. + if (visitInterfaces) + { + // Visit the interfaces of the superclasses, if we haven't done so yet. + if (!visitSuperClass) + { + if (superClass != null) + { + superClass.hierarchyAccept(false, + false, + true, + false, + classVisitor); + } + } + + // Visit the interfaces. + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null) + { + interfaceClass.hierarchyAccept(true, + false, + true, + false, + classVisitor); + } + } + } + } + + // Then visit its subclasses, recursively. + if (visitSubclasses) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].hierarchyAccept(true, + false, + false, + true, + classVisitor); + } + } + } + } + + + /** + * Lets the given class visitor visit the superclass, if it is known. + * @param classVisitor the <code>ClassVisitor</code> that will visit the + * superclass. + */ + public void superClassAccept(ClassVisitor classVisitor) + { + if (superClass != null) + { + superClass.accept(classVisitor); + } + } + + + /** + * Lets the given class visitor visit all known direct interfaces. + * @param classVisitor the <code>ClassVisitor</code> that will visit the + * interfaces. + */ + public void interfacesAccept(ClassVisitor classVisitor) + { + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null) + { + interfaceClass.accept(classVisitor); + } + } + } + } + + + public void subclassesAccept(ClassVisitor classVisitor) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(classVisitor); + } + } + } + + + public void constantPoolEntriesAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void thisClassConstantAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void superClassConstantAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void interfaceConstantsAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void fieldsAccept(MemberVisitor memberVisitor) + { + for (int index = 0; index < fields.length; index++) + { + Field field = fields[index]; + if (field != null) + { + field.accept(this, memberVisitor); + } + } + } + + + public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor) + { + Field field = findField(name, descriptor); + if (field != null) + { + field.accept(this, memberVisitor); + } + } + + + public void methodsAccept(MemberVisitor memberVisitor) + { + for (int index = 0; index < methods.length; index++) + { + Method method = methods[index]; + if (method != null) + { + method.accept(this, memberVisitor); + } + } + } + + + public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor) + { + Method method = findMethod(name, descriptor); + if (method != null) + { + method.accept(this, memberVisitor); + } + } + + + public boolean mayHaveImplementations(Method method) + { + return + (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 && + (method == null || + ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC | + ClassConstants.INTERNAL_ACC_FINAL)) == 0 && + !method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))); + } + + + public void attributesAccept(AttributeVisitor attributeVisitor) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store attributes"); + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Object. + + public String toString() + { + return "LibraryClass("+getName()+")"; + } +} diff --git a/src/proguard/classfile/LibraryField.java b/src/proguard/classfile/LibraryField.java new file mode 100644 index 0000000..2908c37 --- /dev/null +++ b/src/proguard/classfile/LibraryField.java @@ -0,0 +1,77 @@ +/* + * 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.classfile; + +import proguard.classfile.visitor.*; + +/** + * Representation of a field from a class-file. + * + * @author Eric Lafortune + */ +public class LibraryField extends LibraryMember implements Field +{ + /** + * An extra field pointing to the Clazz object referenced in the + * descriptor string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz referencedClass; + + + /** + * Creates an uninitialized LibraryField. + */ + public LibraryField() + { + } + + + /** + * Creates an initialized LibraryField. + */ + public LibraryField(int u2accessFlags, + String name, + String descriptor) + { + super(u2accessFlags, name, descriptor); + } + + + // Implementations for LibraryMember. + + public void accept(LibraryClass libraryClass, MemberVisitor memberVisitor) + { + memberVisitor.visitLibraryField(libraryClass, this); + } + + + // Implementations for Member. + + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/LibraryMember.java b/src/proguard/classfile/LibraryMember.java new file mode 100644 index 0000000..41ccb60 --- /dev/null +++ b/src/proguard/classfile/LibraryMember.java @@ -0,0 +1,108 @@ +/* + * 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.classfile; + +import proguard.classfile.visitor.MemberVisitor; + +/** + * Representation of a field or method from a library class. + * + * @author Eric Lafortune + */ +public abstract class LibraryMember implements Member +{ + private static final int ACC_VISIBLE = ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PROTECTED; + + + public int u2accessFlags; + public String name; + public String descriptor; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized LibraryMember. + */ + protected LibraryMember() + { + } + + + /** + * Creates an initialized LibraryMember. + */ + protected LibraryMember(int u2accessFlags, + String name, + String descriptor) + { + this.u2accessFlags = u2accessFlags; + this.name = name; + this.descriptor = descriptor; + } + + + /** + * Accepts the given member info visitor. + */ + public abstract void accept(LibraryClass libraryClass, + MemberVisitor memberVisitor); + + + // Implementations for Member. + + public int getAccessFlags() + { + return u2accessFlags; + } + + public String getName(Clazz clazz) + { + return name; + } + + public String getDescriptor(Clazz clazz) + { + return descriptor; + } + + public void accept(Clazz clazz, MemberVisitor memberVisitor) + { + accept((LibraryClass)clazz, memberVisitor); + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/LibraryMethod.java b/src/proguard/classfile/LibraryMethod.java new file mode 100644 index 0000000..a49a5f1 --- /dev/null +++ b/src/proguard/classfile/LibraryMethod.java @@ -0,0 +1,83 @@ +/* + * 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.classfile; + +import proguard.classfile.visitor.*; + +/** + * Representation of a method from a class-file. + * + * @author Eric Lafortune + */ +public class LibraryMethod extends LibraryMember implements Method +{ + /** + * An extra field pointing to the Clazz objects referenced in the + * descriptor string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized LibraryMethod. + */ + public LibraryMethod() + { + } + + + /** + * Creates an initialized LibraryMethod. + */ + public LibraryMethod(int u2accessFlags, + String name, + String descriptor) + { + super(u2accessFlags, name, descriptor); + } + + + // Implementations for LibraryMember. + + public void accept(LibraryClass libraryClass, MemberVisitor memberVisitor) + { + memberVisitor.visitLibraryMethod(libraryClass, this); + } + + + // Implementations for Member. + + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + if (referencedClasses[index] != null) + { + referencedClasses[index].accept(classVisitor); + } + } + } + } +} diff --git a/src/proguard/classfile/Member.java b/src/proguard/classfile/Member.java new file mode 100644 index 0000000..1400b9c --- /dev/null +++ b/src/proguard/classfile/Member.java @@ -0,0 +1,57 @@ +/* + * 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.classfile; + +import proguard.classfile.visitor.*; + +/** + * Representation of a field or method from a class. + * + * @author Eric Lafortune + */ +public interface Member extends VisitorAccepter +{ + /** + * Returns the access flags. + */ + public int getAccessFlags(); + + /** + * Returns the class member name. + */ + public String getName(Clazz clazz); + + /** + * Returns the class member's descriptor. + */ + public String getDescriptor(Clazz clazz); + + /** + * Accepts the given class visitor. + */ + public void accept(Clazz clazz, MemberVisitor memberVisitor); + + /** + * Lets the Clazz objects referenced in the descriptor string + * accept the given visitor. + */ + public void referencedClassesAccept(ClassVisitor classVisitor); +} diff --git a/src/proguard/classfile/Method.java b/src/proguard/classfile/Method.java new file mode 100644 index 0000000..ebcae2b --- /dev/null +++ b/src/proguard/classfile/Method.java @@ -0,0 +1,32 @@ +/* + * 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.classfile; + + + +/** + * Representation of a method from a class. + * + * @author Eric Lafortune + */ +public interface Method extends Member +{ +} diff --git a/src/proguard/classfile/ProgramClass.java b/src/proguard/classfile/ProgramClass.java new file mode 100644 index 0000000..9d0fc0c --- /dev/null +++ b/src/proguard/classfile/ProgramClass.java @@ -0,0 +1,494 @@ +/* + * 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.classfile; + +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.ClassSubHierarchyInitializer; +import proguard.classfile.visitor.*; + +/** + * This Clazz is a complete representation of the data in a Java class. + * + * @author Eric Lafortune + */ +public class ProgramClass implements Clazz +{ + public int u4magic; + public int u4version; + public int u2constantPoolCount; + public Constant[] constantPool; + public int u2accessFlags; + public int u2thisClass; + public int u2superClass; + public int u2interfacesCount; + public int[] u2interfaces; + public int u2fieldsCount; + public ProgramField[] fields; + public int u2methodsCount; + public ProgramMethod[] methods; + public int u2attributesCount; + public Attribute[] attributes; + + /** + * An extra field pointing to the subclasses of this class. + * This field is filled out by the {@link ClassSubHierarchyInitializer}. + */ + public Clazz[] subClasses; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ProgramClass. + */ + public ProgramClass() {} + + + /** + * Returns the Constant at the given index in the constant pool. + */ + public Constant getConstant(int constantIndex) + { + return constantPool[constantIndex]; + } + + + // Implementations for Clazz. + + public int getAccessFlags() + { + return u2accessFlags; + } + + public String getName() + { + return getClassName(u2thisClass); + } + + public String getSuperName() + { + return u2superClass == 0 ? null : getClassName(u2superClass); + } + + public int getInterfaceCount() + { + return u2interfacesCount; + } + + public String getInterfaceName(int index) + { + return getClassName(u2interfaces[index]); + } + + public int getTag(int constantIndex) + { + return constantPool[constantIndex].getTag(); + } + + public String getString(int constantIndex) + { + try + { + return ((Utf8Constant)constantPool[constantIndex]).getString(); + } + catch (ClassCastException ex) + { + new ClassPrinter().visitProgramClass(this); + throw new ClassCastException("Expected Utf8Constant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]"); + } + } + + public String getStringString(int constantIndex) + { + try + { + return ((StringConstant)constantPool[constantIndex]).getString(this); + } + catch (ClassCastException ex) + { + throw new ClassCastException("Expected StringConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]"); + } + } + + public String getClassName(int constantIndex) + { + try + { + return ((ClassConstant)constantPool[constantIndex]).getName(this); + } + catch (ClassCastException ex) + { + throw new ClassCastException("Expected ClassConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]"); + } + } + + public String getName(int constantIndex) + { + try + { + return ((NameAndTypeConstant)constantPool[constantIndex]).getName(this); + } + catch (ClassCastException ex) + { + throw new ClassCastException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]"); + } + } + + public String getType(int constantIndex) + { + try + { + return ((NameAndTypeConstant)constantPool[constantIndex]).getType(this); + } + catch (ClassCastException ex) + { + throw new ClassCastException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]"); + } + } + + + public void addSubClass(Clazz clazz) + { + if (subClasses == null) + { + subClasses = new Clazz[1]; + } + else + { + // Copy the old elements into new larger array. + Clazz[] temp = new Clazz[subClasses.length+1]; + System.arraycopy(subClasses, 0, temp, 0, subClasses.length); + subClasses = temp; + } + + subClasses[subClasses.length-1] = clazz; + } + + + public Clazz getSuperClass() + { + return u2superClass != 0 ? + ((ClassConstant)constantPool[u2superClass]).referencedClass : + null; + } + + + public Clazz getInterface(int index) + { + return ((ClassConstant)constantPool[u2interfaces[index]]).referencedClass; + } + + + public boolean extends_(Clazz clazz) + { + if (this.equals(clazz)) + { + return true; + } + + Clazz superClass = getSuperClass(); + return superClass != null && + superClass.extends_(clazz); + } + + + public boolean extendsOrImplements(Clazz clazz) + { + if (this.equals(clazz)) + { + return true; + } + + Clazz superClass = getSuperClass(); + if (superClass != null && + superClass.extendsOrImplements(clazz)) + { + return true; + } + + for (int index = 0; index < u2interfacesCount; index++) + { + Clazz interfaceClass = getInterface(index); + if (interfaceClass != null && + interfaceClass.extendsOrImplements(clazz)) + { + return true; + } + } + + return false; + } + + + public Field findField(String name, String descriptor) + { + for (int index = 0; index < u2fieldsCount; index++) + { + Field field = fields[index]; + if ((name == null || field.getName(this).equals(name)) && + (descriptor == null || field.getDescriptor(this).equals(descriptor))) + { + return field; + } + } + + return null; + } + + + public Method findMethod(String name, String descriptor) + { + for (int index = 0; index < u2methodsCount; index++) + { + Method method = methods[index]; + if ((name == null || method.getName(this).equals(name)) && + (descriptor == null || method.getDescriptor(this).equals(descriptor))) + { + return method; + } + } + + return null; + } + + + public void accept(ClassVisitor classVisitor) + { + classVisitor.visitProgramClass(this); + } + + + public void hierarchyAccept(boolean visitThisClass, + boolean visitSuperClass, + boolean visitInterfaces, + boolean visitSubclasses, + ClassVisitor classVisitor) + { + // First visit the current classfile. + if (visitThisClass) + { + accept(classVisitor); + } + + // Then visit its superclass, recursively. + if (visitSuperClass) + { + Clazz superClass = getSuperClass(); + if (superClass != null) + { + superClass.hierarchyAccept(true, + true, + visitInterfaces, + false, + classVisitor); + } + } + + // Then visit its interfaces, recursively. + if (visitInterfaces) + { + // Visit the interfaces of the superclasses, if we haven't done so yet. + if (!visitSuperClass) + { + Clazz superClass = getSuperClass(); + if (superClass != null) + { + superClass.hierarchyAccept(false, + false, + true, + false, + classVisitor); + } + } + + // Visit the interfaces. + for (int index = 0; index < u2interfacesCount; index++) + { + Clazz interfaceClass = getInterface(index); + if (interfaceClass != null) + { + interfaceClass.hierarchyAccept(true, + false, + true, + false, + classVisitor); + } + } + } + + // Then visit its subclasses, recursively. + if (visitSubclasses) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + Clazz subClass = subClasses[index]; + subClass.hierarchyAccept(true, + false, + false, + true, + classVisitor); + } + } + } + } + + + public void subclassesAccept(ClassVisitor classVisitor) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(classVisitor); + } + } + } + + + public void constantPoolEntriesAccept(ConstantVisitor constantVisitor) + { + for (int index = 1; index < u2constantPoolCount; index++) + { + if (constantPool[index] != null) + { + constantPool[index].accept(this, constantVisitor); + } + } + } + + + public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor) + { + constantPool[index].accept(this, constantVisitor); + } + + + public void thisClassConstantAccept(ConstantVisitor constantVisitor) + { + constantPool[u2thisClass].accept(this, constantVisitor); + } + + + public void superClassConstantAccept(ConstantVisitor constantVisitor) + { + if (u2superClass != 0) + { + constantPool[u2superClass].accept(this, constantVisitor); + } + } + + + public void interfaceConstantsAccept(ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2interfacesCount; index++) + { + constantPool[u2interfaces[index]].accept(this, constantVisitor); + } + } + + + public void fieldsAccept(MemberVisitor memberVisitor) + { + for (int index = 0; index < u2fieldsCount; index++) + { + fields[index].accept(this, memberVisitor); + } + } + + + public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor) + { + Field field = findField(name, descriptor); + if (field != null) + { + field.accept(this, memberVisitor); + } + } + + + public void methodsAccept(MemberVisitor memberVisitor) + { + for (int index = 0; index < u2methodsCount; index++) + { + methods[index].accept(this, memberVisitor); + } + } + + + public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor) + { + Method method = findMethod(name, descriptor); + if (method != null) + { + method.accept(this, memberVisitor); + } + } + + + public boolean mayHaveImplementations(Method method) + { + return + (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 && + (method == null || + ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC | + ClassConstants.INTERNAL_ACC_FINAL)) == 0 && + !method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))); + } + + + public void attributesAccept(AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + attributes[index].accept(this, attributeVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Object. + + public String toString() + { + return "ProgramClass("+getName()+")"; + } +} diff --git a/src/proguard/classfile/ProgramField.java b/src/proguard/classfile/ProgramField.java new file mode 100644 index 0000000..5991b00 --- /dev/null +++ b/src/proguard/classfile/ProgramField.java @@ -0,0 +1,93 @@ +/* + * 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.classfile; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.visitor.*; + +/** + * Representation of a field from a program class. + * + * @author Eric Lafortune + */ +public class ProgramField extends ProgramMember implements Field +{ + /** + * An extra field pointing to the Clazz object referenced in the + * descriptor string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz referencedClass; + + + /** + * Creates an uninitialized ProgramField. + */ + public ProgramField() + { + } + + + /** + * Creates an initialized ProgramField. + */ + public ProgramField(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes, + Clazz referencedClass) + { + super(u2accessFlags, u2nameIndex, u2descriptorIndex, u2attributesCount, attributes); + + this.referencedClass = referencedClass; + } + + + // Implementations for ProgramMember. + + public void accept(ProgramClass programClass, MemberVisitor memberVisitor) + { + memberVisitor.visitProgramField(programClass, this); + } + + + public void attributesAccept(ProgramClass programClass, AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + attributes[index].accept(programClass, this, attributeVisitor); + } + } + + + // Implementations for Member. + + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/ProgramMember.java b/src/proguard/classfile/ProgramMember.java new file mode 100644 index 0000000..ea6f46d --- /dev/null +++ b/src/proguard/classfile/ProgramMember.java @@ -0,0 +1,168 @@ +/* + * 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.classfile; + + +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.visitor.MemberVisitor; + +/** + * Representation of a field or method from a program class. + * + * @author Eric Lafortune + */ +public abstract class ProgramMember implements Member +{ + public int u2accessFlags; + public int u2nameIndex; + public int u2descriptorIndex; + public int u2attributesCount; + public Attribute[] attributes; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ProgramMember. + */ + protected ProgramMember() + { + } + + + /** + * Creates an initialized ProgramMember. + */ + protected ProgramMember(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes) + { + this.u2accessFlags = u2accessFlags; + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + this.u2attributesCount = u2attributesCount; + this.attributes = attributes; + } + + + /** + * Returns the line number range of the given class member as "m:n", + * if it can find it, or <code>null</code> otherwise. + */ + public String getLineNumberRange(Clazz clazz) + { + CodeAttribute codeAttribute = + (CodeAttribute)getAttribute(clazz, ClassConstants.ATTR_Code); + if (codeAttribute == null) + { + return null; + } + + LineNumberTableAttribute lineNumberTableAttribute = + (LineNumberTableAttribute)codeAttribute.getAttribute(clazz, + ClassConstants.ATTR_LineNumberTable); + if (lineNumberTableAttribute == null) + { + return null; + } + + return "" + + lineNumberTableAttribute.getLineNumber(0) + + ":" + + lineNumberTableAttribute.getLineNumber(Integer.MAX_VALUE); + } + + + /** + * Returns the (first) attribute with the given name. + */ + private Attribute getAttribute(Clazz clazz, String name) + { + for (int index = 0; index < u2attributesCount; index++) + { + Attribute attribute = attributes[index]; + if (attribute.getAttributeName(clazz).equals(name)) + { + return attribute; + } + } + + return null; + } + + + /** + * Accepts the given member info visitor. + */ + public abstract void accept(ProgramClass programClass, + MemberVisitor memberVisitor); + + + + /** + * Lets the given attribute info visitor visit all the attributes of + * this member info. + */ + public abstract void attributesAccept(ProgramClass programClass, + AttributeVisitor attributeVisitor); + + + // Implementations for Member. + + public int getAccessFlags() + { + return u2accessFlags; + } + + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + public String getDescriptor(Clazz clazz) + { + return clazz.getString(u2descriptorIndex); + } + + public void accept(Clazz clazz, MemberVisitor memberVisitor) + { + accept((ProgramClass)clazz, memberVisitor); + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/ProgramMethod.java b/src/proguard/classfile/ProgramMethod.java new file mode 100644 index 0000000..943c3d6 --- /dev/null +++ b/src/proguard/classfile/ProgramMethod.java @@ -0,0 +1,99 @@ +/* + * 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.classfile; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.visitor.*; + +/** + * Representation of a method from a program class. + * + * @author Eric Lafortune + */ +public class ProgramMethod extends ProgramMember implements Method +{ + /** + * An extra field pointing to the Clazz objects referenced in the + * descriptor string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized ProgramMethod. + */ + public ProgramMethod() + { + } + + + /** + * Creates an initialized ProgramMethod. + */ + public ProgramMethod(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes, + Clazz[] referencedClasses) + { + super(u2accessFlags, u2nameIndex, u2descriptorIndex, u2attributesCount, attributes); + + this.referencedClasses = referencedClasses; + } + + + // Implementations for ProgramMember. + + public void accept(ProgramClass programClass, MemberVisitor memberVisitor) + { + memberVisitor.visitProgramMethod(programClass, this); + } + + + public void attributesAccept(ProgramClass programClass, AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + attributes[index].accept(programClass, this, attributeVisitor); + } + } + + + // Implementations for Member. + + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + if (referencedClasses[index] != null) + { + referencedClasses[index].accept(classVisitor); + } + } + } + } +} diff --git a/src/proguard/classfile/VisitorAccepter.java b/src/proguard/classfile/VisitorAccepter.java new file mode 100644 index 0000000..e38f888 --- /dev/null +++ b/src/proguard/classfile/VisitorAccepter.java @@ -0,0 +1,47 @@ +/* + * 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.classfile; + + + + +/** + * This interface is a base interface for visitor accepters. It allows + * visitors to set and get any temporary information they desire on the + * objects they are visiting. Note that every visitor accepter has only one + * such property, so visitors will have to take care not to overwrite each + * other's information, if it is still required. + * + * @author Eric Lafortune + */ +public interface VisitorAccepter +{ + /** + * Gets the visitor information of the visitor accepter. + */ + public Object getVisitorInfo(); + + + /** + * Sets the visitor information of the visitor accepter. + */ + public void setVisitorInfo(Object visitorInfo); +} diff --git a/src/proguard/classfile/attribute/Attribute.java b/src/proguard/classfile/attribute/Attribute.java new file mode 100644 index 0000000..2e16e22 --- /dev/null +++ b/src/proguard/classfile/attribute/Attribute.java @@ -0,0 +1,142 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This abstract class represents an attribute that is attached to a class, + * a class member, or a code attribute. Specific types of attributes are + * subclassed from it. + * + * @author Eric Lafortune + * @noinspection AbstractClassWithoutAbstractMethods + */ +public abstract class Attribute implements VisitorAccepter +{ + public int u2attributeNameIndex; + //public int u4attributeLength; + //public byte info[]; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Create an uninitialized Attribute. + */ + protected Attribute() + { + } + + + /** + * Create an initialized Attribute. + */ + protected Attribute(int u2attributeNameIndex) + { + this.u2attributeNameIndex = u2attributeNameIndex; + } + + + /** + * Returns the String name of the attribute. + */ + public String getAttributeName(Clazz clazz) + { + return clazz.getString(u2attributeNameIndex); + } + + + // Methods to be implemented by extensions, if applicable. + + /** + * Accepts the given visitor. + */ + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + /** + * Accepts the given visitor in the context of the given field. + */ + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + // Delegate the default invocation if the field is null anyway. + if (field == null) + { + accept(clazz, attributeVisitor); + } + else + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + } + + /** + * Accepts the given visitor in the context of the given method. + */ + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + // Delegate the default invocation if the method is null anyway. + if (method == null) + { + accept(clazz, (Field)null, attributeVisitor); + } + else + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + } + + /** + * Accepts the given visitor in the context of the given code attribute. + */ + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + // Delegate the default invocation if the code attribute is null anyway. + if (codeAttribute == null) + { + accept(clazz, method, attributeVisitor); + } + else + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/CodeAttribute.java b/src/proguard/classfile/attribute/CodeAttribute.java new file mode 100644 index 0000000..92ff9ea --- /dev/null +++ b/src/proguard/classfile/attribute/CodeAttribute.java @@ -0,0 +1,202 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Attribute represents a code attribute. + * + * @author Eric Lafortune + */ +public class CodeAttribute extends Attribute +{ + public int u2maxStack; + public int u2maxLocals; + public int u4codeLength; + public byte[] code; + public int u2exceptionTableLength; + public ExceptionInfo[] exceptionTable; + public int u2attributesCount; + public Attribute[] attributes; + + + /** + * Creates an uninitialized CodeAttribute. + */ + public CodeAttribute() + { + } + + + /** + * Creates an initialized CodeAttribute. + */ + public CodeAttribute(int u2attributeNameIndex, + int u2maxStack, + int u2maxLocals, + int u4codeLength, + byte[] code, + int u2exceptionTableLength, + ExceptionInfo[] exceptionTable, + int u2attributesCount, + Attribute[] attributes) + { + super(u2attributeNameIndex); + + this.u2maxStack = u2maxStack; + this.u2maxLocals = u2maxLocals; + this.u4codeLength = u4codeLength; + this.code = code; + this.u2exceptionTableLength = u2exceptionTableLength; + this.exceptionTable = exceptionTable; + this.u2attributesCount = u2attributesCount; + this.attributes = attributes; + } + + + /** + * Returns the (first) attribute with the given name. + */ + public Attribute getAttribute(Clazz clazz, String name) + { + for (int index = 0; index < u2attributesCount; index++) + { + Attribute attribute = attributes[index]; + if (attribute.getAttributeName(clazz).equals(name)) + { + return attribute; + } + } + + return null; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitCodeAttribute(clazz, method, this); + } + + + /** + * Applies the given instruction visitor to all instructions. + */ + public void instructionsAccept(Clazz clazz, Method method, InstructionVisitor instructionVisitor) + { + instructionsAccept(clazz, method, 0, u4codeLength, instructionVisitor); + } + + + /** + * Applies the given instruction visitor to the instruction at the specified + * offset. + */ + public void instructionAccept(Clazz clazz, Method method, int offset, InstructionVisitor instructionVisitor) + { + Instruction instruction = InstructionFactory.create(code, offset); + instruction.accept(clazz, method, this, offset, instructionVisitor); + } + + + /** + * Applies the given instruction visitor to all instructions in the + * specified range of offsets. + */ + public void instructionsAccept(Clazz clazz, Method method, int startOffset, int endOffset, InstructionVisitor instructionVisitor) + { + int offset = startOffset; + + while (offset < endOffset) + { + // Note that the instruction is only volatile. + Instruction instruction = InstructionFactory.create(code, offset); + int instructionLength = instruction.length(offset); + instruction.accept(clazz, method, this, offset, instructionVisitor); + offset += instructionLength; + } + } + + + /** + * Applies the given exception visitor to all exceptions. + */ + public void exceptionsAccept(Clazz clazz, Method method, ExceptionInfoVisitor exceptionInfoVisitor) + { + for (int index = 0; index < u2exceptionTableLength; index++) + { + // We don't need double dispatching here, since there is only one + // type of ExceptionInfo. + exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionTable[index]); + } + } + + + /** + * Applies the given exception visitor to all exceptions that are applicable + * to the instruction at the specified offset. + */ + public void exceptionsAccept(Clazz clazz, Method method, int offset, ExceptionInfoVisitor exceptionInfoVisitor) + { + for (int index = 0; index < u2exceptionTableLength; index++) + { + ExceptionInfo exceptionInfo = exceptionTable[index]; + if (exceptionInfo.isApplicable(offset)) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionInfo); + } + } + } + + + /** + * Applies the given exception visitor to all exceptions that are applicable + * to any of the instructions in the specified range of offsets. + */ + public void exceptionsAccept(Clazz clazz, Method method, int startOffset, int endOffset, ExceptionInfoVisitor exceptionInfoVisitor) + { + for (int index = 0; index < u2exceptionTableLength; index++) + { + ExceptionInfo exceptionInfo = exceptionTable[index]; + if (exceptionInfo.isApplicable(startOffset, endOffset)) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionInfo); + } + } + } + + + /** + * Applies the given attribute visitor to all attributes. + */ + public void attributesAccept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + attributes[index].accept(clazz, method, this, attributeVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/ConstantValueAttribute.java b/src/proguard/classfile/attribute/ConstantValueAttribute.java new file mode 100644 index 0000000..3ae991e --- /dev/null +++ b/src/proguard/classfile/attribute/ConstantValueAttribute.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a constant value attribute. + * + * @author Eric Lafortune + */ +public class ConstantValueAttribute extends Attribute +{ + public int u2constantValueIndex; + + + /** + * Creates an uninitialized ConstantValueAttribute. + */ + public ConstantValueAttribute() + { + } + + + /** + * Creates an initialized ConstantValueAttribute. + */ + public ConstantValueAttribute(int u2attributeNameIndex, + int u2constantValueIndex) + { + super(u2attributeNameIndex); + + this.u2constantValueIndex = u2constantValueIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitConstantValueAttribute(clazz, field, this); + } +} diff --git a/src/proguard/classfile/attribute/DeprecatedAttribute.java b/src/proguard/classfile/attribute/DeprecatedAttribute.java new file mode 100644 index 0000000..4180950 --- /dev/null +++ b/src/proguard/classfile/attribute/DeprecatedAttribute.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a deprecated attribute. + * + * @author Eric Lafortune + */ +public class DeprecatedAttribute extends Attribute +{ + /** + * Creates an uninitialized DeprecatedAttribute. + */ + public DeprecatedAttribute() + { + } + + + /** + * Creates an initialized DeprecatedAttribute. + */ + public DeprecatedAttribute(int u2attributeNameIndex) + { + super(u2attributeNameIndex); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitDeprecatedAttribute(clazz, this); + } + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitDeprecatedAttribute(clazz, field, this); + } + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitDeprecatedAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java new file mode 100644 index 0000000..9275b3a --- /dev/null +++ b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java @@ -0,0 +1,132 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.visitor.*; + +/** + * This Attribute represents an enclosing method attribute. + * + * @author Eric Lafortune + */ +public class EnclosingMethodAttribute extends Attribute +{ + public int u2classIndex; + public int u2nameAndTypeIndex; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + /** + * An extra field optionally pointing to the referenced Method object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + */ + public Method referencedMethod; + + + /** + * Creates an uninitialized EnclosingMethodAttribute. + */ + public EnclosingMethodAttribute() + { + } + + + /** + * Creates an initialized EnclosingMethodAttribute. + */ + public EnclosingMethodAttribute(int u2attributeNameIndex, + int u2classIndex, + int u2nameAndTypeIndex) + { + super(u2attributeNameIndex); + + this.u2classIndex = u2classIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + } + + + /** + * Returns the class name. + */ + public String getClassName(Clazz clazz) + { + return clazz.getClassName(u2classIndex); + } + + /** + * Returns the method/field name. + */ + public String getName(Clazz clazz) + { + return clazz.getName(u2nameAndTypeIndex); + } + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getType(u2nameAndTypeIndex); + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + + + /** + * Lets the referenced class member accept the given visitor. + */ + public void referencedMethodAccept(MemberVisitor memberVisitor) + { + if (referencedMethod != null) + { + referencedMethod.accept(referencedClass, + memberVisitor); + } + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitEnclosingMethodAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/ExceptionInfo.java b/src/proguard/classfile/attribute/ExceptionInfo.java new file mode 100644 index 0000000..082efab --- /dev/null +++ b/src/proguard/classfile/attribute/ExceptionInfo.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.VisitorAccepter; + +/** + * Representation of an Exception table entry. + * + * @author Eric Lafortune + */ +public class ExceptionInfo implements VisitorAccepter +{ + public int u2startPC; + public int u2endPC; + public int u2handlerPC; + public int u2catchType; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ExceptionInfo. + */ + public ExceptionInfo() + { + this(0, 0, 0, 0); + } + + + /** + * Creates an ExceptionInfo with the given properties. + */ + public ExceptionInfo(int u2startPC, + int u2endPC, + int u2handlerPC, + int u2catchType) + { + this.u2startPC = u2startPC; + this.u2endPC = u2endPC; + this.u2handlerPC = u2handlerPC; + this.u2catchType = u2catchType; + } + + + /** + * Returns whether the exception's try block contains the instruction at the + * given offset. + */ + public boolean isApplicable(int instructionOffset) + { + return instructionOffset >= u2startPC && + instructionOffset < u2endPC; + } + + + /** + * Returns whether the exception's try block overlaps with the specified + * block of instructions. + */ + public boolean isApplicable(int startOffset, int endOffset) + { + return u2startPC < endOffset && + u2endPC > startOffset; + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/ExceptionsAttribute.java b/src/proguard/classfile/attribute/ExceptionsAttribute.java new file mode 100644 index 0000000..d22c4a6 --- /dev/null +++ b/src/proguard/classfile/attribute/ExceptionsAttribute.java @@ -0,0 +1,80 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Attribute represents an exceptions attribute. + * + * @author Eric Lafortune + */ +public class ExceptionsAttribute extends Attribute +{ + public int u2exceptionIndexTableLength; + public int[] u2exceptionIndexTable; + + + /** + * Creates an uninitialized ExceptionsAttribute. + */ + public ExceptionsAttribute() + { + } + + + /** + * Creates an initialized ExceptionsAttribute. + */ + public ExceptionsAttribute(int u2attributeNameIndex, + int u2exceptionIndexTableLength, + int[] u2exceptionIndexTable) + { + super(u2attributeNameIndex); + + this.u2exceptionIndexTableLength = u2exceptionIndexTableLength; + this.u2exceptionIndexTable = u2exceptionIndexTable; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitExceptionsAttribute(clazz, method, this); + } + + + /** + * Applies the given constant pool visitor to all exception class pool info + * entries. + */ + public void exceptionEntriesAccept(ProgramClass programClass, ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2exceptionIndexTableLength; index++) + { + programClass.constantPoolEntryAccept(u2exceptionIndexTable[index], + constantVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/InnerClassesAttribute.java b/src/proguard/classfile/attribute/InnerClassesAttribute.java new file mode 100644 index 0000000..2f7e310 --- /dev/null +++ b/src/proguard/classfile/attribute/InnerClassesAttribute.java @@ -0,0 +1,80 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.visitor.*; + +/** + * This Attribute represents an inner classes attribute. + * + * @author Eric Lafortune + */ +public class InnerClassesAttribute extends Attribute +{ + public int u2classesCount; + public InnerClassesInfo[] classes; + + + /** + * Creates an uninitialized InnerClassesAttribute. + */ + public InnerClassesAttribute() + { + } + + + /** + * Creates an initialized InnerClassesAttribute. + */ + public InnerClassesAttribute(int u2attributeNameIndex, + int u2classesCount, + InnerClassesInfo[] classes) + { + super(u2attributeNameIndex); + + this.u2classesCount = u2classesCount; + this.classes = classes; + } + + // + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitInnerClassesAttribute(clazz, this); + } + + + /** + * Applies the given visitor to all inner classes. + */ + public void innerClassEntriesAccept(Clazz clazz, InnerClassesInfoVisitor innerClassesInfoVisitor) + { + for (int index = 0; index < u2classesCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of InnerClassesInfo. + innerClassesInfoVisitor.visitInnerClassesInfo(clazz, classes[index]); + } + } +} diff --git a/src/proguard/classfile/attribute/InnerClassesInfo.java b/src/proguard/classfile/attribute/InnerClassesInfo.java new file mode 100644 index 0000000..1bdd6c3 --- /dev/null +++ b/src/proguard/classfile/attribute/InnerClassesInfo.java @@ -0,0 +1,122 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * Representation of an Inner Classes table entry. + * + * @author Eric Lafortune + */ +public class InnerClassesInfo implements VisitorAccepter +{ + public int u2innerClassIndex; + public int u2outerClassIndex; + public int u2innerNameIndex; + public int u2innerClassAccessFlags; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Returns the inner class index. + */ + protected int getInnerClassIndex() + { + return u2innerClassIndex; + } + + /** + * Returns the name index. + */ + protected int getInnerNameIndex() + { + return u2innerNameIndex; + } + + /** + * Sets the name index. + */ + protected void setInnerNameIndex(int index) + { + u2innerNameIndex = index; + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * inner class, if any. + */ + public void innerClassConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2innerClassIndex != 0) + { + clazz.constantPoolEntryAccept(u2innerClassIndex, + constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * outer class, if any. + */ + public void outerClassConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2outerClassIndex != 0) + { + clazz.constantPoolEntryAccept(u2outerClassIndex, + constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to the Utf8 constant of the + * inner name, if any. + */ + public void innerNameConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2innerNameIndex != 0) + { + clazz.constantPoolEntryAccept(u2innerNameIndex, + constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/src/proguard/classfile/attribute/LineNumberInfo.java new file mode 100644 index 0000000..f58083a --- /dev/null +++ b/src/proguard/classfile/attribute/LineNumberInfo.java @@ -0,0 +1,50 @@ +/* + * 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.classfile.attribute; + +/** + * Representation of an Line Number table entry. + * + * @author Eric Lafortune + */ +public class LineNumberInfo +{ + public int u2startPC; + public int u2lineNumber; + + + /** + * Creates an uninitialized LineNumberInfo. + */ + public LineNumberInfo() + { + } + + + /** + * Creates an initialized LineNumberInfo. + */ + public LineNumberInfo(int u2startPC, int u2lineNumber) + { + this.u2startPC = u2startPC; + this.u2lineNumber = u2lineNumber; + } +} diff --git a/src/proguard/classfile/attribute/LineNumberTableAttribute.java b/src/proguard/classfile/attribute/LineNumberTableAttribute.java new file mode 100644 index 0000000..4d507d9 --- /dev/null +++ b/src/proguard/classfile/attribute/LineNumberTableAttribute.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; + +/** + * This Attribute represents a line number table attribute. + * + * @author Eric Lafortune + */ +public class LineNumberTableAttribute extends Attribute +{ + public int u2lineNumberTableLength; + public LineNumberInfo[] lineNumberTable; + + + /** + * Creates an uninitialized LineNumberTableAttribute. + */ + public LineNumberTableAttribute() + { + } + + + /** + * Creates an initialized LineNumberTableAttribute. + */ + public LineNumberTableAttribute(int u2attributeNameIndex, + int u2lineNumberTableLength, + LineNumberInfo[] lineNumberTable) + { + super(u2attributeNameIndex); + + this.u2lineNumberTableLength = u2lineNumberTableLength; + this.lineNumberTable = lineNumberTable; + } + + + /** + * Returns the line number corresponding to the given byte code program + * counter. + */ + public int getLineNumber(int pc) + { + for (int index = u2lineNumberTableLength-1 ; index >= 0 ; index--) + { + LineNumberInfo info = lineNumberTable[index]; + if (pc >= info.u2startPC) + { + return info.u2lineNumber; + } + } + + return u2lineNumberTableLength > 0 ? + lineNumberTable[0].u2lineNumber : + 0; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitLineNumberTableAttribute(clazz, method, codeAttribute, this); + } + + + /** + * Applies the given visitor to all line numbers. + */ + public void lineNumbersAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfoVisitor lineNumberInfoVisitor) + { + for (int index = 0; index < u2lineNumberTableLength; index++) + { + // We don't need double dispatching here, since there is only one + // type of LineNumberInfo. + lineNumberInfoVisitor.visitLineNumberInfo(clazz, method, codeAttribute, lineNumberTable[index]); + } + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableInfo.java b/src/proguard/classfile/attribute/LocalVariableInfo.java new file mode 100644 index 0000000..4e54c22 --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableInfo.java @@ -0,0 +1,83 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.Clazz; +import proguard.classfile.visitor.ClassVisitor; + +/** + * Representation of an Local Variable table entry. + * + * @author Eric Lafortune + */ +public class LocalVariableInfo +{ + public int u2startPC; + public int u2length; + public int u2nameIndex; + public int u2descriptorIndex; + public int u2index; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + + /** + * Creates an uninitialized LocalVariableInfo. + */ + public LocalVariableInfo() + { + } + + + /** + * Creates an initialized LocalVariableInfo. + */ + public LocalVariableInfo(int u2startPC, + int u2length, + int u2nameIndex, + int u2descriptorIndex, + int u2index) + { + this.u2startPC = u2startPC; + this.u2length = u2length; + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + this.u2index = u2index; + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java new file mode 100644 index 0000000..9c3f115 --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java @@ -0,0 +1,79 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; + +/** + * This Attribute represents a local variable table attribute. + * + * @author Eric Lafortune + */ +public class LocalVariableTableAttribute extends Attribute +{ + public int u2localVariableTableLength; + public LocalVariableInfo[] localVariableTable; + + + /** + * Creates an uninitialized LocalVariableTableAttribute. + */ + public LocalVariableTableAttribute() + { + } + + + /** + * Creates an initialized LocalVariableTableAttribute. + */ + public LocalVariableTableAttribute(int u2attributeNameIndex, + int u2localVariableTableLength, + LocalVariableInfo[] localVariableTable) + { + super(u2attributeNameIndex); + + this.u2localVariableTableLength = u2localVariableTableLength; + this.localVariableTable = localVariableTable; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitLocalVariableTableAttribute(clazz, method, codeAttribute, this); + } + + + /** + * Applies the given visitor to all local variables. + */ + public void localVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfoVisitor localVariableInfoVisitor) + { + for (int index = 0; index < u2localVariableTableLength; index++) + { + // We don't need double dispatching here, since there is only one + // type of LocalVariableInfo. + localVariableInfoVisitor.visitLocalVariableInfo(clazz, method, codeAttribute, localVariableTable[index]); + } + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java new file mode 100644 index 0000000..1b71f35 --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java @@ -0,0 +1,91 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.Clazz; +import proguard.classfile.visitor.ClassVisitor; + +/** + * Representation of an Local Variable table entry. + * + * @author Eric Lafortune + */ +public class LocalVariableTypeInfo +{ + public int u2startPC; + public int u2length; + public int u2nameIndex; + public int u2signatureIndex; + public int u2index; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type string. This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized LocalVariableTypeInfo. + */ + public LocalVariableTypeInfo() + { + } + + + /** + * Creates an initialized LocalVariableTypeInfo. + */ + public LocalVariableTypeInfo(int u2startPC, + int u2length, + int u2nameIndex, + int u2signatureIndex, + int u2index) + { + this.u2startPC = u2startPC; + this.u2length = u2length; + this.u2nameIndex = u2nameIndex; + this.u2signatureIndex = u2signatureIndex; + this.u2index = u2index; + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java new file mode 100644 index 0000000..fd856fe --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -0,0 +1,79 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; + +/** + * This Attribute represents a local variable table type attribute. + * + * @author Eric Lafortune + */ +public class LocalVariableTypeTableAttribute extends Attribute +{ + public int u2localVariableTypeTableLength; + public LocalVariableTypeInfo[] localVariableTypeTable; + + + /** + * Creates an uninitialized LocalVariableTypeTableAttribute. + */ + public LocalVariableTypeTableAttribute() + { + } + + + /** + * Creates an initialized LocalVariableTypeTableAttribute. + */ + public LocalVariableTypeTableAttribute(int u2attributeNameIndex, + int u2localVariableTypeTableLength, + LocalVariableTypeInfo[] localVariableTypeTable) + { + super(u2attributeNameIndex); + + this.u2localVariableTypeTableLength = u2localVariableTypeTableLength; + this.localVariableTypeTable = localVariableTypeTable; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, this); + } + + + /** + * Applies the given visitor to all local variable types. + */ + public void localVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfoVisitor localVariableTypeInfoVisitor) + { + for (int index = 0; index < u2localVariableTypeTableLength; index++) + { + // We don't need double dispatching here, since there is only one + // type of LocalVariableTypeInfo. + localVariableTypeInfoVisitor.visitLocalVariableTypeInfo(clazz, method, codeAttribute, localVariableTypeTable[index]); + } + } +} diff --git a/src/proguard/classfile/attribute/SignatureAttribute.java b/src/proguard/classfile/attribute/SignatureAttribute.java new file mode 100644 index 0000000..c7585fe --- /dev/null +++ b/src/proguard/classfile/attribute/SignatureAttribute.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This Attribute represents a signature attribute. + * + * @author Eric Lafortune + */ +public class SignatureAttribute extends Attribute +{ + public int u2signatureIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * signature string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized SignatureAttribute. + */ + public SignatureAttribute() + { + } + + + /** + * Creates an initialized SignatureAttribute. + */ + public SignatureAttribute(int u2attributeNameIndex, + int u2signatureIndex) + { + super(u2attributeNameIndex); + + this.u2signatureIndex = u2signatureIndex; + } + + + /** + * Lets the Clazz objects referenced in the signature string accept the + * given visitor. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + if (referencedClasses[index] != null) + { + referencedClasses[index].accept(classVisitor); + } + } + } + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSignatureAttribute(clazz, this); + } + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSignatureAttribute(clazz, field, this); + } + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSignatureAttribute(clazz, method, this); + } + } diff --git a/src/proguard/classfile/attribute/SourceDirAttribute.java b/src/proguard/classfile/attribute/SourceDirAttribute.java new file mode 100644 index 0000000..a26e8b1 --- /dev/null +++ b/src/proguard/classfile/attribute/SourceDirAttribute.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a source directory attribute. + * + * @author Eric Lafortune + */ +public class SourceDirAttribute extends Attribute +{ + public int u2sourceDirIndex; + + + /** + * Creates an uninitialized SourceDirAttribute. + */ + public SourceDirAttribute() + { + } + + + /** + * Creates an initialized SourceDirAttribute. + */ + public SourceDirAttribute(int u2attributeNameIndex, + int u2sourceDirIndex) + { + super(u2attributeNameIndex); + + this.u2sourceDirIndex = u2sourceDirIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSourceDirAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/SourceFileAttribute.java b/src/proguard/classfile/attribute/SourceFileAttribute.java new file mode 100644 index 0000000..24269b7 --- /dev/null +++ b/src/proguard/classfile/attribute/SourceFileAttribute.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a source file attribute. + * + * @author Eric Lafortune + */ +public class SourceFileAttribute extends Attribute +{ + public int u2sourceFileIndex; + + + /** + * Creates an uninitialized SourceFileAttribute. + */ + public SourceFileAttribute() + { + } + + + /** + * Creates an initialized SourceFileAttribute. + */ + public SourceFileAttribute(int u2attributeNameIndex, + int u2sourceFileIndex) + { + super(u2attributeNameIndex); + + this.u2sourceFileIndex = u2sourceFileIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSourceFileAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/SyntheticAttribute.java b/src/proguard/classfile/attribute/SyntheticAttribute.java new file mode 100644 index 0000000..6ccb1b5 --- /dev/null +++ b/src/proguard/classfile/attribute/SyntheticAttribute.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a synthetic attribute. + * + * @author Eric Lafortune + */ +public class SyntheticAttribute extends Attribute +{ + /** + * Creates an uninitialized SyntheticAttribute. + */ + public SyntheticAttribute() + { + } + + + /** + * Creates an initialized SyntheticAttribute. + */ + public SyntheticAttribute(int u2attributeNameIndex) + { + super(u2attributeNameIndex); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSyntheticAttribute(clazz, this); + } + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSyntheticAttribute(clazz, field, this); + } + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSyntheticAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/UnknownAttribute.java b/src/proguard/classfile/attribute/UnknownAttribute.java new file mode 100644 index 0000000..2f138bd --- /dev/null +++ b/src/proguard/classfile/attribute/UnknownAttribute.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.attribute; + + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents an unknown attribute. + * + * @author Eric Lafortune + */ +public class UnknownAttribute extends Attribute +{ + public final int u4attributeLength; + public byte[] info; + + + /** + * Creates an uninitialized UnknownAttribute with the given length. + */ + public UnknownAttribute(int attributeLength) + { + u4attributeLength = attributeLength; + } + + + /** + * Creates an initialized UnknownAttribute. + */ + public UnknownAttribute(int u2attributeNameIndex, + int u4attributeLength, + byte[] info) + { + super(u2attributeNameIndex); + + this.u4attributeLength = u4attributeLength; + this.info = info; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitUnknownAttribute(clazz, this); + } + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitUnknownAttribute(clazz, this); + } + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitUnknownAttribute(clazz, this); + } + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitUnknownAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/Annotation.java b/src/proguard/classfile/attribute/annotation/Annotation.java new file mode 100644 index 0000000..41bb8e3 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/Annotation.java @@ -0,0 +1,143 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * Representation of an annotation. + * + * @author Eric Lafortune + */ +public class Annotation implements VisitorAccepter +{ + public int u2typeIndex; + public int u2elementValuesCount; + public ElementValue[] elementValues; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type string. This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized Annotation. + */ + public Annotation() + { + } + + + /** + * Creates an initialized Annotation. + */ + public Annotation(int u2typeIndex, + int u2elementValuesCount, + ElementValue[] elementValues) + { + this.u2typeIndex = u2typeIndex; + this.u2elementValuesCount = u2elementValuesCount; + this.elementValues = elementValues; + } + + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getString(u2typeIndex); + } + + + + /** + * Applies the given visitor to the first referenced class. This is the + * main annotation class. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + Clazz referencedClass = referencedClasses[0]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + /** + * Applies the given visitor to all element value pairs. + */ + public void elementValuesAccept(Clazz clazz, ElementValueVisitor elementValueVisitor) + { + for (int index = 0; index < u2elementValuesCount; index++) + { + elementValues[index].accept(clazz, this, elementValueVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java new file mode 100644 index 0000000..b378cd2 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java @@ -0,0 +1,73 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents an annotation default attribute. + * + * @author Eric Lafortune + */ +public class AnnotationDefaultAttribute extends Attribute +{ + public ElementValue defaultValue; + + + /** + * Creates an uninitialized AnnotationDefaultAttribute. + */ + public AnnotationDefaultAttribute() + { + } + + + /** + * Creates an initialized AnnotationDefaultAttribute. + */ + public AnnotationDefaultAttribute(int u2attributeNameIndex, + ElementValue defaultValue) + { + super(u2attributeNameIndex); + + this.defaultValue = defaultValue; + } + + + /** + * Applies the given visitor to the default element value. + */ + public void defaultValueAccept(Clazz clazz, ElementValueVisitor elementValueVisitor) + { + defaultValue.accept(clazz, null, elementValueVisitor); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java new file mode 100644 index 0000000..29129d0 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java @@ -0,0 +1,76 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.*; + +/** + * This ElementValue represents an annotation element value. + * + * @author Eric Lafortune + */ +public class AnnotationElementValue extends ElementValue +{ + public Annotation annotationValue; + + + /** + * Creates an uninitialized AnnotationElementValue. + */ + public AnnotationElementValue() + { + } + + + /** + * Creates an initialized AnnotationElementValue. + */ + public AnnotationElementValue(int u2elementNameIndex, + Annotation annotationValue) + { + super(u2elementNameIndex); + + this.annotationValue = annotationValue; + } + + + /** + * Applies the given visitor to the annotation. + */ + public void annotationAccept(Clazz clazz, AnnotationVisitor annotationVisitor) + { + annotationVisitor.visitAnnotation(clazz, annotationValue); + } + + + // Implementations for ElementValue. + + public int getTag() + { + return ClassConstants.ELEMENT_VALUE_ANNOTATION; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitAnnotationElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java new file mode 100644 index 0000000..8117077 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor; + +/** + * This Attribute represents an annotations attribute. + * + * @author Eric Lafortune + */ +public abstract class AnnotationsAttribute extends Attribute +{ + public int u2annotationsCount; + public Annotation[] annotations; + + + /** + * Creates an uninitialized AnnotationsAttribute. + */ + protected AnnotationsAttribute() + { + } + + + /** + * Creates an initialized AnnotationsAttribute. + */ + protected AnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex); + + this.u2annotationsCount = u2annotationsCount; + this.annotations = annotations; + } + + + /** + * Applies the given visitor to all class annotations. + */ + public void annotationsAccept(Clazz clazz, AnnotationVisitor annotationVisitor) + { + for (int index = 0; index < u2annotationsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of Annotation. + annotationVisitor.visitAnnotation(clazz, annotations[index]); + } + } + + + /** + * Applies the given visitor to all field annotations. + */ + public void annotationsAccept(Clazz clazz, Field field, AnnotationVisitor annotationVisitor) + { + for (int index = 0; index < u2annotationsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of Annotation. + annotationVisitor.visitAnnotation(clazz, field, annotations[index]); + } + } + + + /** + * Applies the given visitor to all method annotations. + */ + public void annotationsAccept(Clazz clazz, Method method, AnnotationVisitor annotationVisitor) + { + for (int index = 0; index < u2annotationsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of Annotation. + annotationVisitor.visitAnnotation(clazz, method, annotations[index]); + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java new file mode 100644 index 0000000..25b8b9f --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; + +/** + * This ElementValue represents an array element value. + * + * @author Eric Lafortune + */ +public class ArrayElementValue extends ElementValue +{ + public int u2elementValuesCount; + public ElementValue[] elementValues; + + + /** + * Creates an uninitialized ArrayElementValue. + */ + public ArrayElementValue() + { + } + + + /** + * Creates an initialized ArrayElementValue. + */ + public ArrayElementValue(int u2elementNameIndex, + int u2elementValuesCount, + ElementValue[] elementValues) + { + super(u2elementNameIndex); + + this.u2elementValuesCount = u2elementValuesCount; + this.elementValues = elementValues; + } + + + // Implementations for ElementValue. + + public int getTag() + { + return ClassConstants.ELEMENT_VALUE_ARRAY; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitArrayElementValue(clazz, annotation, this); + } + + + /** + * Applies the given visitor to all nested element values. + */ + public void elementValuesAccept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + for (int index = 0; index < u2elementValuesCount; index++) + { + elementValues[index].accept(clazz, annotation, elementValueVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/src/proguard/classfile/attribute/annotation/ClassElementValue.java new file mode 100644 index 0000000..ba51641 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ClassElementValue.java @@ -0,0 +1,95 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ElementValue represents a class element value. + * + * @author Eric Lafortune + */ +public class ClassElementValue extends ElementValue +{ + public int u2classInfoIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type name string. This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized ClassElementValue. + */ + public ClassElementValue() + { + } + + + /** + * Creates an initialized ClassElementValue. + */ + public ClassElementValue(int u2elementNameIndex, + int u2classInfoIndex) + { + super(u2elementNameIndex); + + this.u2classInfoIndex = u2classInfoIndex; + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + // Implementations for ElementValue. + + public int getTag() + { + return ClassConstants.ELEMENT_VALUE_CLASS; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitClassElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java new file mode 100644 index 0000000..3ebe5e5 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; + +/** + * This ElementValue represents a constant element value. + * + * @author Eric Lafortune + */ +public class ConstantElementValue extends ElementValue +{ + public final int u1tag; + public int u2constantValueIndex; + + + /** + * Creates an uninitialized ConstantElementValue. + */ + public ConstantElementValue(int u1tag) + { + this.u1tag = u1tag; + } + + + /** + * Creates an initialized ConstantElementValue. + */ + public ConstantElementValue(int u1tag, + int u2elementNameIndex, + int u2constantValueIndex) + { + super(u2elementNameIndex); + + this.u1tag = u1tag; + this.u2constantValueIndex = u2constantValueIndex; + } + + + // Implementations for ElementValue. + + public int getTag() + { + return u1tag; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitConstantElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/src/proguard/classfile/attribute/annotation/ElementValue.java new file mode 100644 index 0000000..39f8953 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ElementValue.java @@ -0,0 +1,126 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This abstract class represents an element value that is attached to an + * annotation or an annotation default. Specific types of element values are + * subclassed from it. + * + * @author Eric Lafortune + */ +public abstract class ElementValue implements VisitorAccepter +{ + /** + * An extra field for the optional element name. It is used in element value + * pairs of annotations. Otherwise, it is 0. + */ + public int u2elementNameIndex; + + /** + * An extra field pointing to the referenced <code>Clazz</code> + * object, if applicable. This field is typically filled out by the + * <code>{@link proguard.classfile.util.ClassReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the referenced <code>Method</code> + * object, if applicable. This field is typically filled out by the + * <code>{@link proguard.classfile.util.ClassReferenceInitializer}</code>. + */ + public Method referencedMethod; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ElementValue. + */ + protected ElementValue() + { + } + + + /** + * Creates an initialized ElementValue. + */ + protected ElementValue(int u2elementNameIndex) + { + this.u2elementNameIndex = u2elementNameIndex; + } + + + /** + * Returns the element name. + */ + public String getMethodName(Clazz clazz) + { + return clazz.getString(u2elementNameIndex); + } + + + // Abstract methods to be implemented by extensions. + + /** + * Returns the tag of this element value. + */ + public abstract int getTag(); + + + /** + * Accepts the given visitor. + */ + public abstract void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor); + + + + /** + * Applies the given visitor to the referenced method. + */ + public void referencedMethodAccept(MemberVisitor memberVisitor) + { + if (referencedMethod != null) + { + referencedMethod.accept(referencedClass, memberVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java new file mode 100644 index 0000000..d46bb7f --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java @@ -0,0 +1,99 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ElementValue represents an enumeration constant element value. + * + * @author Eric Lafortune + */ +public class EnumConstantElementValue extends ElementValue +{ + public int u2typeNameIndex; + public int u2constantNameIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type name string. This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized EnumConstantElementValue. + */ + public EnumConstantElementValue() + { + } + + + /** + * Creates an initialized EnumConstantElementValue. + */ + public EnumConstantElementValue(int u2elementNameIndex, + int u2typeNameIndex, + int u2constantNameIndex) + { + super(u2elementNameIndex); + + this.u2typeNameIndex = u2typeNameIndex; + this.u2constantNameIndex = u2constantNameIndex; + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + // Implementations for ElementValue. + + public int getTag() + { + return ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitEnumConstantElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java new file mode 100644 index 0000000..3c700c8 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java @@ -0,0 +1,83 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor; + +/** + * This Attribute represents a runtime parameter annotations attribute. + * + * @author Eric Lafortune + */ +public abstract class ParameterAnnotationsAttribute extends Attribute +{ + public int u2parametersCount; + public int[] u2parameterAnnotationsCount; + public Annotation[][] parameterAnnotations; + + + /** + * Creates an uninitialized ParameterAnnotationsAttribute. + */ + protected ParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized ParameterAnnotationsAttribute. + */ + protected ParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex); + + this.u2parametersCount = u2parametersCount; + this.u2parameterAnnotationsCount = u2parameterAnnotationsCount; + this.parameterAnnotations = parameterAnnotations; + } + + + /** + * Applies the given visitor to all annotations. + */ + public void annotationsAccept(Clazz clazz, Method method, AnnotationVisitor annotationVisitor) + { + // Loop over all parameters. + for (int parameterIndex = 0; parameterIndex < u2parametersCount; parameterIndex++) + { + int annotationsCount = u2parameterAnnotationsCount[parameterIndex]; + Annotation[] annotations = parameterAnnotations[parameterIndex]; + + // Loop over all parameter annotations. + for (int index = 0; index < annotationsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of Annotation. + annotationVisitor.visitAnnotation(clazz, method, parameterIndex, annotations[index]); + } + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java new file mode 100644 index 0000000..9c8180c --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java @@ -0,0 +1,70 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime invisible annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeInvisibleAnnotationsAttribute extends AnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeInvisibleAnnotationsAttribute. + */ + public RuntimeInvisibleAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeInvisibleAnnotationsAttribute. + */ + public RuntimeInvisibleAnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex, u2annotationsCount, annotations); + } + + +// Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, this); + } + + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, field, this); + } + + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java new file mode 100644 index 0000000..7e41656 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime invisible parameter annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeInvisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeInvisibleParameterAnnotationsAttribute. + */ + public RuntimeInvisibleParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeInvisibleParameterAnnotationsAttribute. + */ + public RuntimeInvisibleParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex, + u2parametersCount, + u2parameterAnnotationsCount, + parameterAnnotations); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java new file mode 100644 index 0000000..380c52e --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java @@ -0,0 +1,70 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime visible annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeVisibleAnnotationsAttribute extends AnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeVisibleAnnotationsAttribute. + */ + public RuntimeVisibleAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeVisibleAnnotationsAttribute. + */ + public RuntimeVisibleAnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex, u2annotationsCount, annotations); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, this); + } + + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, field, this); + } + + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java new file mode 100644 index 0000000..626fbda --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute.annotation; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime visible parameter annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeVisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeVisibleParameterAnnotationsAttribute. + */ + public RuntimeVisibleParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeVisibleParameterAnnotationsAttribute. + */ + public RuntimeVisibleParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex, + u2parametersCount, + u2parameterAnnotationsCount, + parameterAnnotations); + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/package.html b/src/proguard/classfile/attribute/annotation/package.html new file mode 100644 index 0000000..6aacff3 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/package.html @@ -0,0 +1,4 @@ +<body> +This package contains classes to represent the annotation attributes inside +class files. +</body> diff --git a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java new file mode 100644 index 0000000..bce7170 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given AnnotationVisitor visit all Annotation + * objects of the attributes it visits. + * + * @author Eric Lafortune + */ +public class AllAnnotationVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final AnnotationVisitor annotationVisitor; + + + public AllAnnotationVisitor(AnnotationVisitor annotationVisitor) + { + this.annotationVisitor = annotationVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, annotationVisitor); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, field, annotationVisitor); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, annotationVisitor); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, field, annotationVisitor); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Visit the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor); + } +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java new file mode 100644 index 0000000..7a1d7c6 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.annotation.Annotation; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This AnnotationVisitor delegates all visits to a given ClassVisitor. + * The latter visits the class of each visited annotation, although + * never twice in a row. + * + * @author Eric Lafortune + */ +public class AnnotatedClassVisitor +extends SimplifiedVisitor +implements AnnotationVisitor +{ + private final ClassVisitor classVisitor; + + private Clazz lastVisitedClass; + + + public AnnotatedClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + if (!clazz.equals(lastVisitedClass)) + { + clazz.accept(classVisitor); + + lastVisitedClass = clazz; + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java new file mode 100644 index 0000000..c206c16 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.Annotation; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; + + +/** + * This AnnotationVisitor delegates all visits to a given MemberVisitor. + * The latter visits the class member of each visited class member annotation + * or method parameter annotation, although never twice in a row. + * + * @author Eric Lafortune + */ +public class AnnotationToMemberVisitor +extends SimplifiedVisitor +implements AnnotationVisitor +{ + private final MemberVisitor memberVisitor; + + private Member lastVisitedMember; + + + public AnnotationToMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Member member, Annotation annotation) + { + if (!member.equals(lastVisitedMember)) + { + member.accept(clazz, memberVisitor); + + lastVisitedMember = member; + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java new file mode 100644 index 0000000..d869fd2 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java @@ -0,0 +1,102 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.Annotation; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.util.*; + +/** + * This <code>AnnotationVisitor</code> delegates its visits to another given + * <code>AnnotationVisitor</code>, but only when the visited annotation has + * a type that matches a given regular expression. + * + * @author Eric Lafortune + */ +public class AnnotationTypeFilter +extends SimplifiedVisitor +implements AnnotationVisitor +{ + private final StringMatcher regularExpressionMatcher; + private final AnnotationVisitor annotationVisitor; + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which annotation + * type names will be matched. + * @param annotationVisitor the <code>annotationVisitor</code> to which + * visits will be delegated. + */ + public AnnotationTypeFilter(String regularExpression, + AnnotationVisitor annotationVisitor) + { + this.regularExpressionMatcher = new ListParser(new ClassNameParser()).parse(regularExpression); + this.annotationVisitor = annotationVisitor; + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + if (accepted(annotation.getType(clazz))) + { + annotationVisitor.visitAnnotation(clazz, annotation); + } + } + + + public void visitAnnotation(Clazz clazz, Field field, Annotation annotation) + { + if (accepted(annotation.getType(clazz))) + { + annotationVisitor.visitAnnotation(clazz, field, annotation); + } + } + + + public void visitAnnotation(Clazz clazz, Method method, Annotation annotation) + { + if (accepted(annotation.getType(clazz))) + { + annotationVisitor.visitAnnotation(clazz, method, annotation); + } + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + if (accepted(annotation.getType(clazz))) + { + annotationVisitor.visitAnnotation(clazz, method, parameterIndex, annotation); + } + } + + + // Small utility methods. + + private boolean accepted(String name) + { + return regularExpressionMatcher.matches(name); + } +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java new file mode 100644 index 0000000..16b2a56 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java @@ -0,0 +1,40 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.Annotation; + +/** + * This interface specifies the methods for a visitor of + * <code>Annotation</code> objects. Note that there is only a single + * implementation of <code>Annotation</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface AnnotationVisitor +{ + public void visitAnnotation(Clazz clazz, Annotation annotation); + public void visitAnnotation(Clazz clazz, Field field, Annotation annotation); + public void visitAnnotation(Clazz clazz, Method method, Annotation annotation); + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation); +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java new file mode 100644 index 0000000..112084a --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java @@ -0,0 +1,51 @@ +/* + * 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.classfile.attribute.annotation.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.annotation.*; + +/** + * This interface specifies the methods for a visitor of <code>ElementValue</code> + * objects. + * + * @author Eric Lafortune + */ +public interface ElementValueVisitor +{ + public void visitConstantElementValue( Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue); + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue); + public void visitClassElementValue( Clazz clazz, Annotation annotation, ClassElementValue classElementValue); + public void visitAnnotationElementValue( Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue); + public void visitArrayElementValue( Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue); + +// public void visitConstantElementValue( Clazz clazz, Field field, Annotation annotation, ConstantElementValue constantElementValue); +// public void visitEnumConstantElementValue(Clazz clazz, Field field, Annotation annotation, EnumConstantElementValue enumConstantElementValue); +// public void visitClassElementValue( Clazz clazz, Field field, Annotation annotation, ClassElementValue classElementValue); +// public void visitAnnotationElementValue( Clazz clazz, Field field, Annotation annotation, AnnotationElementValue annotationElementValue); +// public void visitArrayElementValue( Clazz clazz, Field field, Annotation annotation, ArrayElementValue arrayElementValue); +// +// public void visitConstantElementValue( Clazz clazz, Method method, Annotation annotation, ConstantElementValue constantElementValue); +// public void visitEnumConstantElementValue(Clazz clazz, Method method, Annotation annotation, EnumConstantElementValue enumConstantElementValue); +// public void visitClassElementValue( Clazz clazz, Method method, Annotation annotation, ClassElementValue classElementValue); +// public void visitAnnotationElementValue( Clazz clazz, Method method, Annotation annotation, AnnotationElementValue annotationElementValue); +// public void visitArrayElementValue( Clazz clazz, Method method, Annotation annotation, ArrayElementValue arrayElementValue); +} diff --git a/src/proguard/classfile/attribute/annotation/visitor/package.html b/src/proguard/classfile/attribute/annotation/visitor/package.html new file mode 100644 index 0000000..10d0648 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/package.html @@ -0,0 +1,3 @@ +<body> +This package contains visitors for annotation attributes and their components. +</body> diff --git a/src/proguard/classfile/attribute/package.html b/src/proguard/classfile/attribute/package.html new file mode 100644 index 0000000..d17caaa --- /dev/null +++ b/src/proguard/classfile/attribute/package.html @@ -0,0 +1,3 @@ +<body> +This package contains classes to represent the attributes inside class files. +</body> diff --git a/src/proguard/classfile/attribute/preverification/DoubleType.java b/src/proguard/classfile/attribute/preverification/DoubleType.java new file mode 100644 index 0000000..d574dcb --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/DoubleType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Double type. + * + * @author Eric Lafortune + */ +public class DoubleType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return DOUBLE_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitDoubleType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackDoubleType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesDoubleType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "d"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/FloatType.java b/src/proguard/classfile/attribute/preverification/FloatType.java new file mode 100644 index 0000000..2f24720 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/FloatType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Float type. + * + * @author Eric Lafortune + */ +public class FloatType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return FLOAT_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitFloatType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackFloatType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesFloatType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "f"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/FullFrame.java b/src/proguard/classfile/attribute/preverification/FullFrame.java new file mode 100644 index 0000000..adf5684 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/FullFrame.java @@ -0,0 +1,202 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.*; + +/** + * This StackMapFrame represents a "full frame". + * + * @author Eric Lafortune + */ +public class FullFrame extends StackMapFrame +{ + public int variablesCount; + public VerificationType[] variables; + public int stackCount; + public VerificationType[] stack; + + + /** + * Creates an uninitialized FullFrame. + */ + public FullFrame() + { + } + + + /** + * Creates a FullFrame with the given variables and stack. + */ + public FullFrame(int offsetDelta, + VerificationType[] variables, + VerificationType[] stack) + { + this(offsetDelta, + variables.length, + variables, + stack.length, + stack); + } + + + /** + * Creates a FullFrame with the given variables and stack. + */ + public FullFrame(int offsetDelta, + int variablesCount, + VerificationType[] variables, + int stackCount, + VerificationType[] stack) + { + this.u2offsetDelta = offsetDelta; + this.variablesCount = variablesCount; + this.variables = variables; + this.stackCount = stackCount; + this.stack = stack; + } + + + /** + * Applies the given verification type visitor to all variables. + */ + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor) + { + for (int index = 0; index < variablesCount; index++) + { + variables[index].variablesAccept(clazz, method, codeAttribute, offset, index, verificationTypeVisitor); + } + } + + + /** + * Applies the given verification type visitor to all stack. + */ + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor) + { + for (int index = 0; index < stackCount; index++) + { + stack[index].stackAccept(clazz, method, codeAttribute, offset, index, verificationTypeVisitor); + } + } + + + // Implementations for StackMapFrame. + + public int getTag() + { + return FULL_FRAME; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor) + { + stackMapFrameVisitor.visitFullFrame(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + FullFrame other = (FullFrame)object; + + if (this.u2offsetDelta != other.u2offsetDelta || + this.variablesCount != other.variablesCount || + this.stackCount != other.stackCount) + { + return false; + } + + for (int index = 0; index < variablesCount; index++) + { + VerificationType thisType = this.variables[index]; + VerificationType otherType = other.variables[index]; + + if (!thisType.equals(otherType)) + { + return false; + } + } + + for (int index = 0; index < stackCount; index++) + { + VerificationType thisType = this.stack[index]; + VerificationType otherType = other.stack[index]; + + if (!thisType.equals(otherType)) + { + return false; + } + } + + return true; + } + + + public int hashCode() + { + int hashCode = super.hashCode(); + + for (int index = 0; index < variablesCount; index++) + { + hashCode ^= variables[index].hashCode(); + } + + for (int index = 0; index < stackCount; index++) + { + hashCode ^= stack[index].hashCode(); + } + + return hashCode; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(super.toString()).append("Var: "); + + for (int index = 0; index < variablesCount; index++) + { + buffer = buffer.append('[') + .append(variables[index].toString()) + .append(']'); + } + + buffer.append(", Stack: "); + + for (int index = 0; index < stackCount; index++) + { + buffer = buffer.append('[') + .append(stack[index].toString()) + .append(']'); + } + + return buffer.toString(); + } +} diff --git a/src/proguard/classfile/attribute/preverification/IntegerType.java b/src/proguard/classfile/attribute/preverification/IntegerType.java new file mode 100644 index 0000000..55e3abe --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/IntegerType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Integer type. + * + * @author Eric Lafortune + */ +public class IntegerType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return INTEGER_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitIntegerType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackIntegerType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesIntegerType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "i"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java new file mode 100644 index 0000000..fcc8e0a --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java @@ -0,0 +1,103 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor; + +/** + * This StackMapFrame represents an "chop frame". + * + * @author Eric Lafortune + */ +public class LessZeroFrame extends StackMapFrame +{ + public int choppedVariablesCount; + + + /** + * Creates an uninitialized LessZeroFrame. + */ + public LessZeroFrame() + { + } + + + /** + * Creates a LessZeroFrame with the given tag. + */ + public LessZeroFrame(int tag) + { + choppedVariablesCount = LESS_ZERO_FRAME + 3 - tag; + } + + + /** + * Creates a LessZeroFrame with the given number of chopped variables. + */ + public LessZeroFrame(byte choppedVariablesCount) + { + this.choppedVariablesCount = (int)choppedVariablesCount; + } + + + // Implementations for StackMapFrame. + + public int getTag() + { + return LESS_ZERO_FRAME + 3 - choppedVariablesCount; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor) + { + stackMapFrameVisitor.visitLessZeroFrame(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + LessZeroFrame other = (LessZeroFrame)object; + + return this.u2offsetDelta == other.u2offsetDelta && + this.choppedVariablesCount != other.choppedVariablesCount; + } + + + public int hashCode() + { + return super.hashCode() ^ choppedVariablesCount; + } + + + public String toString() + { + return super.toString()+"Var: (chopped "+choppedVariablesCount+"), Stack: (empty)"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/LongType.java b/src/proguard/classfile/attribute/preverification/LongType.java new file mode 100644 index 0000000..9b14dd6 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/LongType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Long type. + * + * @author Eric Lafortune + */ +public class LongType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return LONG_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitLongType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackLongType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesLongType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "l"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java new file mode 100644 index 0000000..881f188 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java @@ -0,0 +1,161 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.*; + +/** + * This StackMapFrame represents an "append frame". + * + * @author Eric Lafortune + */ +public class MoreZeroFrame extends StackMapFrame +{ + public int additionalVariablesCount; + public VerificationType[] additionalVariables; + + + /** + * Creates an uninitialized MoreZeroFrame. + */ + public MoreZeroFrame() + { + } + + + /** + * Creates a MoreZeroFrame with the given tag. + */ + public MoreZeroFrame(int tag) + { + additionalVariablesCount = tag + 1 - MORE_ZERO_FRAME; + } + + + /** + * Creates a MoreZeroFrame with the given additional variables. + */ + public MoreZeroFrame(VerificationType[] additionalVariables) + { + this(additionalVariables.length, additionalVariables); + } + + + /** + * Creates a MoreZeroFrame with the given additional variables. + */ + public MoreZeroFrame(int additionalVariablesCount, + VerificationType[] additionalVariables) + { + this.additionalVariablesCount = additionalVariablesCount; + this.additionalVariables = additionalVariables; + } + + + /** + * Applies the given verification type visitor to all variables. + */ + public void additionalVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor) + { + for (int index = 0; index < additionalVariablesCount; index++) + { + additionalVariables[index].accept(clazz, method, codeAttribute, offset, verificationTypeVisitor); + } + } + + + // Implementations for StackMapFrame. + + public int getTag() + { + return MORE_ZERO_FRAME + additionalVariablesCount - 1; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor) + { + stackMapFrameVisitor.visitMoreZeroFrame(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + MoreZeroFrame other = (MoreZeroFrame)object; + + if (this.u2offsetDelta != other.u2offsetDelta || + this.additionalVariablesCount != other.additionalVariablesCount) + { + return false; + } + + for (int index = 0; index < additionalVariablesCount; index++) + { + VerificationType thisType = this.additionalVariables[index]; + VerificationType otherType = other.additionalVariables[index]; + + if (!thisType.equals(otherType)) + { + return false; + } + } + + return true; + } + + + public int hashCode() + { + int hashCode = super.hashCode(); + + for (int index = 0; index < additionalVariablesCount; index++) + { + hashCode ^= additionalVariables[index].hashCode(); + } + + return hashCode; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(super.toString()).append("Var: ..."); + + for (int index = 0; index < additionalVariablesCount; index++) + { + buffer = buffer.append('[') + .append(additionalVariables[index].toString()) + .append(']'); + } + + buffer.append(", Stack: (empty)"); + + return buffer.toString(); + } +} diff --git a/src/proguard/classfile/attribute/preverification/NullType.java b/src/proguard/classfile/attribute/preverification/NullType.java new file mode 100644 index 0000000..f35cefd --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/NullType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Null type. + * + * @author Eric Lafortune + */ +public class NullType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return NULL_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitNullType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackNullType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesNullType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "n"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/ObjectType.java b/src/proguard/classfile/attribute/preverification/ObjectType.java new file mode 100644 index 0000000..fbdeec7 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/ObjectType.java @@ -0,0 +1,107 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents an Object type. + * + * @author Eric Lafortune + */ +public class ObjectType extends VerificationType +{ + public int u2classIndex; + + + + /** + * Creates an uninitialized ObjectType. + */ + public ObjectType() + { + } + + + /** + * Creates an ObjectType that points to the given class constant. + */ + public ObjectType(int u2classIndex) + { + this.u2classIndex = u2classIndex; + } + + + // Implementations for VerificationType. + + public int getTag() + { + return OBJECT_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitObjectType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackObjectType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesObjectType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + ObjectType other = (ObjectType)object; + + return this.u2classIndex == other.u2classIndex; + } + + + public int hashCode() + { + return super.hashCode() ^ + u2classIndex; + } + + + public String toString() + { + return "a:" + u2classIndex; + } +} diff --git a/src/proguard/classfile/attribute/preverification/SameOneFrame.java b/src/proguard/classfile/attribute/preverification/SameOneFrame.java new file mode 100644 index 0000000..db6747b --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/SameOneFrame.java @@ -0,0 +1,115 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.*; + +/** + * This StackMapFrame represents a "same locals 1 stack item frame" or a + * "same locals 1 stack item frame extended". + * + * @author Eric Lafortune + */ +public class SameOneFrame extends StackMapFrame +{ + public VerificationType stackItem; + + + /** + * Creates an uninitialized SameOneFrame. + */ + public SameOneFrame() + { + } + + + /** + * Creates a SameOneFrame with the given tag. + */ + public SameOneFrame(int tag) + { + u2offsetDelta = tag - SAME_ONE_FRAME; + } + + + /** + * Creates a SameOneFrame with the given stack verification type. + */ + public SameOneFrame(VerificationType stackItem) + { + this.stackItem = stackItem; + } + + + /** + * Applies the given verification type visitor to the stack item. + */ + public void stackItemAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor) + { + stackItem.accept(clazz, method, codeAttribute, offset, verificationTypeVisitor); + } + + + // Implementations for StackMapFrame. + + public int getTag() + { + return u2offsetDelta < 64 ? + SAME_ONE_FRAME + u2offsetDelta : + SAME_ONE_FRAME_EXTENDED; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor) + { + stackMapFrameVisitor.visitSameOneFrame(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + SameOneFrame other = (SameOneFrame)object; + + return this.u2offsetDelta == other.u2offsetDelta && + this.stackItem.equals(other.stackItem); + } + + + public int hashCode() + { + return super.hashCode() ^ stackItem.hashCode(); + } + + + public String toString() + { + return super.toString()+"Var: ..., Stack: ["+stackItem.toString()+"]"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java new file mode 100644 index 0000000..64b17f5 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java @@ -0,0 +1,74 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor; + +/** + * This StackMapFrame represents a "same frame" or a "same frame extended". + * + * @author Eric Lafortune + * @noinspection PointlessArithmeticExpression + */ +public class SameZeroFrame extends StackMapFrame +{ + /** + * Creates an uninitialized SameZeroFrame. + */ + public SameZeroFrame() + { + } + + + /** + * Creates a SameZeroFrame with the given tag. + */ + public SameZeroFrame(int tag) + { + u2offsetDelta = tag - SAME_ZERO_FRAME; + } + + + // Implementations for StackMapFrame. + + public int getTag() + { + return u2offsetDelta < 64 ? + SAME_ZERO_FRAME + u2offsetDelta : + SAME_ZERO_FRAME_EXTENDED; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor) + { + stackMapFrameVisitor.visitSameZeroFrame(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public String toString() + { + return super.toString()+"Var: ..., Stack: (empty)"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java new file mode 100644 index 0000000..db53ff1 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java @@ -0,0 +1,91 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents an exceptions attribute. + * + * @author Eric Lafortune + */ +public class StackMapAttribute extends Attribute +{ + public int u2stackMapFramesCount; + public FullFrame[] stackMapFrames; + + + /** + * Creates an uninitialized ExceptionsAttribute. + */ + public StackMapAttribute() + { + } + + + /** + * Creates a StackMapTableAttribute with the given stack map frames. + */ + public StackMapAttribute(FullFrame[] stackMapFrames) + { + this(stackMapFrames.length, stackMapFrames); + } + + + /** + * Creates a StackMapTableAttribute with the given stack map frames. + */ + public StackMapAttribute(int stackMapFramesCount, + FullFrame[] stackMapFrames) + { + this.u2stackMapFramesCount = stackMapFramesCount; + this.stackMapFrames = stackMapFrames; + } + + + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitStackMapAttribute(clazz, method, codeAttribute, this); + } + + + /** + * Applies the given stack map frame visitor to all stack map frames. + */ + public void stackMapFramesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapFrameVisitor stackMapFrameVisitor) + { + for (int index = 0; index < u2stackMapFramesCount; index++) + { + FullFrame stackMapFrame = stackMapFrames[index]; + + // We don't need double dispatching here, since there is only one + // type of StackMapFrame. + stackMapFrameVisitor.visitFullFrame(clazz, method, codeAttribute, stackMapFrame.getOffsetDelta(), stackMapFrame); + } + } +} diff --git a/src/proguard/classfile/attribute/preverification/StackMapFrame.java b/src/proguard/classfile/attribute/preverification/StackMapFrame.java new file mode 100644 index 0000000..aa3e1f2 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/StackMapFrame.java @@ -0,0 +1,117 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor; + +/** + * This abstract class represents a stack map frame. Specific types + * of entries are subclassed from it. + * + * @author Eric Lafortune + */ +public abstract class StackMapFrame implements VisitorAccepter +{ + public static final int SAME_ZERO_FRAME = 0; + public static final int SAME_ONE_FRAME = 64; + public static final int SAME_ONE_FRAME_EXTENDED = 247; + public static final int LESS_ZERO_FRAME = 248; + public static final int SAME_ZERO_FRAME_EXTENDED = 251; + public static final int MORE_ZERO_FRAME = 252; + public static final int FULL_FRAME = 255; + + + public int u2offsetDelta; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + + /** + * Returns the bytecode offset delta relative to the previous stack map + * frame. + */ + public int getOffsetDelta() + { + return u2offsetDelta; + } + + + // Abstract methods to be implemented by extensions. + + /** + * Returns the stack map frame tag that specifies the entry type. + */ + public abstract int getTag(); + + + /** + * Accepts the given visitor. + */ + public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor); + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + StackMapFrame other = (StackMapFrame)object; + + return this.u2offsetDelta == other.u2offsetDelta; + } + + + public int hashCode() + { + return getClass().hashCode() ^ + u2offsetDelta; + } + + + public String toString() + { + return "[" + u2offsetDelta + "] "; + } +} diff --git a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java new file mode 100644 index 0000000..0cddf70 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java @@ -0,0 +1,93 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a stack map table attribute. + * + * @author Eric Lafortune + */ +public class StackMapTableAttribute extends Attribute +{ + public int u2stackMapFramesCount; + public StackMapFrame[] stackMapFrames; + + + /** + * Creates an uninitialized StackMapTableAttribute. + */ + public StackMapTableAttribute() + { + } + + + /** + * Creates a StackMapTableAttribute with the given stack map frames. + */ + public StackMapTableAttribute(StackMapFrame[] stackMapFrames) + { + this(stackMapFrames.length, stackMapFrames); + } + + + /** + * Creates a StackMapTableAttribute with the given stack map frames. + */ + public StackMapTableAttribute(int stackMapFramesCount, + StackMapFrame[] stackMapFrames) + { + this.u2stackMapFramesCount = stackMapFramesCount; + this.stackMapFrames = stackMapFrames; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitStackMapTableAttribute(clazz, method, codeAttribute, this); + } + + + /** + * Applies the given stack map frame visitor to all stack map frames. + */ + public void stackMapFramesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapFrameVisitor stackMapFrameVisitor) + { + int offset = 0; + + for (int index = 0; index < u2stackMapFramesCount; index++) + { + StackMapFrame stackMapFrame = stackMapFrames[index]; + + // Note that the byte code offset is computed differently for the + // first stack map frame. + offset += stackMapFrame.getOffsetDelta() + (index == 0 ? 0 : 1); + + stackMapFrame.accept(clazz, method, codeAttribute, offset, stackMapFrameVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/preverification/TopType.java b/src/proguard/classfile/attribute/preverification/TopType.java new file mode 100644 index 0000000..bde8dda --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/TopType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Top type. + * + * @author Eric Lafortune + */ +public class TopType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return TOP_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitTopType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackTopType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesTopType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "T"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java new file mode 100644 index 0000000..dc4654f --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a UninitializedThis type. + * + * @author Eric Lafortune + */ +public class UninitializedThisType extends VerificationType +{ + // Implementations for VerificationType. + + public int getTag() + { + return UNINITIALIZED_THIS_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitUninitializedThisType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackUninitializedThisType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesUninitializedThisType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public String toString() + { + return "u:this"; + } +} diff --git a/src/proguard/classfile/attribute/preverification/UninitializedType.java b/src/proguard/classfile/attribute/preverification/UninitializedType.java new file mode 100644 index 0000000..a495f1f --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/UninitializedType.java @@ -0,0 +1,106 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This VerificationType represents a Uninitialized type. + * + * @author Eric Lafortune + */ +public class UninitializedType extends VerificationType +{ + public int u2newInstructionOffset; + + + /** + * Creates an uninitialized UninitializedType. + */ + public UninitializedType() + { + } + + + /** + * Creates an UninitializedType pointing to the given 'new' instruction. + */ + public UninitializedType(int u2newInstructionOffset) + { + this.u2newInstructionOffset = u2newInstructionOffset; + } + + + // Implementations for VerificationType. + + public int getTag() + { + return UNINITIALIZED_TYPE; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitUninitializedType(clazz, method, codeAttribute, instructionOffset, this); + } + + + public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitStackUninitializedType(clazz, method, codeAttribute, instructionOffset, stackIndex, this); + } + + + public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor) + { + verificationTypeVisitor.visitVariablesUninitializedType(clazz, method, codeAttribute, instructionOffset, variableIndex, this); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (!super.equals(object)) + { + return false; + } + + UninitializedType other = (UninitializedType)object; + + return this.u2newInstructionOffset == other.u2newInstructionOffset; + } + + + public int hashCode() + { + return super.hashCode() ^ + u2newInstructionOffset; + } + + + public String toString() + { + return "u:" + u2newInstructionOffset; + } +} diff --git a/src/proguard/classfile/attribute/preverification/VerificationType.java b/src/proguard/classfile/attribute/preverification/VerificationType.java new file mode 100644 index 0000000..f33d511 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/VerificationType.java @@ -0,0 +1,103 @@ +/* + * 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.classfile.attribute.preverification; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor; + +/** + * This abstract class represents a verification type of a local variable or + * a stack element. Specific verification types are subclassed from it. + * + * @author Eric Lafortune + */ +public abstract class VerificationType implements VisitorAccepter +{ + public static final int TOP_TYPE = 0; + public static final int INTEGER_TYPE = 1; + public static final int FLOAT_TYPE = 2; + public static final int DOUBLE_TYPE = 3; + public static final int LONG_TYPE = 4; + public static final int NULL_TYPE = 5; + public static final int UNINITIALIZED_THIS_TYPE = 6; + public static final int OBJECT_TYPE = 7; + public static final int UNINITIALIZED_TYPE = 8; + + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Returns the tag of the verification type. + */ + public abstract int getTag(); + + + /** + * Accepts the given visitor in the context of a method's code, either on + * a stack or as a variable. + */ + public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor); + + + /** + * Accepts the given visitor in the context of a stack in a method's code . + */ + public abstract void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor); + + + /** + * Accepts the given visitor in the context of a variable in a method's code. + */ + public abstract void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor); + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } +} diff --git a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java new file mode 100644 index 0000000..f8ef7e0 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java @@ -0,0 +1,112 @@ +/* + * 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.classfile.attribute.preverification; + +/** + * This class provides methods to create and reuse IntegerType objects. + * + * @author Eric Lafortune + */ +public class VerificationTypeFactory +{ + // Shared copies of Type objects, to avoid creating a lot of objects. + static final IntegerType INTEGER_TYPE = new IntegerType(); + static final LongType LONG_TYPE = new LongType(); + static final FloatType FLOAT_TYPE = new FloatType(); + static final DoubleType DOUBLE_TYPE = new DoubleType(); + static final TopType TOP_TYPE = new TopType(); + static final NullType NULL_TYPE = new NullType(); + static final UninitializedThisType UNINITIALIZED_THIS_TYPE = new UninitializedThisType(); + + + /** + * Creates a new IntegerType. + */ + public static IntegerType createIntegerType() + { + return INTEGER_TYPE; + } + + /** + * Creates a new LongType. + */ + public static LongType createLongType() + { + return LONG_TYPE; + } + + /** + * Creates a new FloatType. + */ + public static FloatType createFloatType() + { + return FLOAT_TYPE; + } + + /** + * Creates a new DoubleType. + */ + public static DoubleType createDoubleType() + { + return DOUBLE_TYPE; + } + + /** + * Creates a new TopType. + */ + public static TopType createTopType() + { + return TOP_TYPE; + } + + /** + * Creates a new NullType. + */ + public static NullType createNullType() + { + return NULL_TYPE; + } + + /** + * Creates a new UninitializedThisType. + */ + public static UninitializedThisType createUninitializedThisType() + { + return UNINITIALIZED_THIS_TYPE; + } + + /** + * Creates a new UninitializedType for an instance that was created at + * the given offset. + */ + public static UninitializedType createUninitializedType(int newInstructionOffset) + { + return new UninitializedType(newInstructionOffset); + } + + /** + * Creates a new ObjectType of the given type. + */ + public static ObjectType createObjectType(int classIndex) + { + return new ObjectType(classIndex); + } +} diff --git a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java new file mode 100644 index 0000000..7db246c --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java @@ -0,0 +1,40 @@ +/* + * 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.classfile.attribute.preverification.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.*; + +/** + * This interface specifies the methods for a visitor of + * <code>StackMapFrame</code> objects. + * + * @author Eric Lafortune + */ +public interface StackMapFrameVisitor +{ + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame); + public void visitSameOneFrame( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame); + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame); + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame); + public void visitFullFrame( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame); +} diff --git a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java new file mode 100644 index 0000000..e9931f8 --- /dev/null +++ b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java @@ -0,0 +1,65 @@ +/* + * 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.classfile.attribute.preverification.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.preverification.*; + +/** + * This interface specifies the methods for a visitor of + * <code>VerificationType</code> objects. There a methods for stack entries + * and methods for variable entries. + * + * @author Eric Lafortune + */ +public interface VerificationTypeVisitor +{ + public void visitIntegerType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType); + public void visitFloatType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType); + public void visitLongType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType); + public void visitDoubleType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType); + public void visitTopType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType); + public void visitObjectType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType); + public void visitNullType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType); + public void visitUninitializedType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType); + public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType); + + public void visitStackIntegerType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType); + public void visitStackFloatType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType); + public void visitStackLongType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType); + public void visitStackDoubleType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType); + public void visitStackTopType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType); + public void visitStackObjectType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType); + public void visitStackNullType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType); + public void visitStackUninitializedType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType); + public void visitStackUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType); + + public void visitVariablesIntegerType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType); + public void visitVariablesFloatType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType); + public void visitVariablesLongType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType); + public void visitVariablesDoubleType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType); + public void visitVariablesTopType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType); + public void visitVariablesObjectType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType); + public void visitVariablesNullType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType); + public void visitVariablesUninitializedType( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType); + public void visitVariablesUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType); +} diff --git a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java new file mode 100644 index 0000000..61b0f1a --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java @@ -0,0 +1,117 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor, MemberVisitor, and AttributeVisitor lets a given + * AttributeVisitor visit all Attribute objects of the program classes, + * program class members, or code attributes, respectively, that it visits. + * + * @author Eric Lafortune + */ +public class AllAttributeVisitor +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + private final boolean deep; + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new shallow AllAttributeVisitor. + * @param attributeVisitor the AttributeVisitor to which visits will be + * delegated. + */ + public AllAttributeVisitor(AttributeVisitor attributeVisitor) + { + this(false, attributeVisitor); + } + + + /** + * Creates a new optionally deep AllAttributeVisitor. + * @param deep specifies whether the attributes contained + * further down the class structure should be + * visited too. + * @param attributeVisitor the AttributeVisitor to which visits will be + * delegated. + */ + public AllAttributeVisitor(boolean deep, + AttributeVisitor attributeVisitor) + { + this.deep = deep; + this.attributeVisitor = attributeVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.attributesAccept(attributeVisitor); + + // Visit the attributes further down the class structure, if required. + if (deep) + { + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + programClass.attributesAccept(this); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + programMember.attributesAccept(programClass, attributeVisitor); + + // Visit the attributes further down the member structure, if required. + if (deep) + { + programMember.attributesAccept(programClass, this); + } + } + + + public void visitLibraryMember(LibraryClass programClass, LibraryMember programMember) {} + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.attributesAccept(clazz, method, attributeVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java new file mode 100644 index 0000000..839e104 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java @@ -0,0 +1,55 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given ExceptionInfoVisitor visit all exceptions + * objects of the CodeAttribute objects it visits. + * + * @author Eric Lafortune + */ +public class AllExceptionInfoVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + public AllExceptionInfoVisitor(ExceptionInfoVisitor exceptionInfoVisitor) + { + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.exceptionsAccept(clazz, method, exceptionInfoVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java new file mode 100644 index 0000000..aa81ce0 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java @@ -0,0 +1,345 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.util.StringMatcher; + +/** + * This AttributeVisitor delegates its visits another AttributeVisitor, but + * only when the visited attribute has a name that passes a given string + * matcher. + * + * @author Eric Lafortune + */ +public class AttributeNameFilter +implements AttributeVisitor +{ + private final StringMatcher stringMatcher; + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new AttributeNameFilter. + * @param stringMatcher the string matcher that will check the attribute + * names. + * @param attributeVisitor the <code>AttributeVisitor</code> to which + * visits will be delegated. + */ + public AttributeNameFilter(StringMatcher stringMatcher, + AttributeVisitor attributeVisitor) + { + this.stringMatcher = stringMatcher; + this.attributeVisitor = attributeVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + if (accepted(clazz, unknownAttribute)) + { + unknownAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + if (accepted(clazz, sourceFileAttribute)) + { + sourceFileAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + if (accepted(clazz, sourceDirAttribute)) + { + sourceDirAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + if (accepted(clazz, innerClassesAttribute)) + { + innerClassesAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + if (accepted(clazz, enclosingMethodAttribute)) + { + enclosingMethodAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + if (accepted(clazz, deprecatedAttribute)) + { + deprecatedAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + if (accepted(clazz, deprecatedAttribute)) + { + deprecatedAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + if (accepted(clazz, deprecatedAttribute)) + { + deprecatedAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + if (accepted(clazz, syntheticAttribute)) + { + syntheticAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + if (accepted(clazz, syntheticAttribute)) + { + syntheticAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + if (accepted(clazz, syntheticAttribute)) + { + syntheticAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + if (accepted(clazz, signatureAttribute)) + { + signatureAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + if (accepted(clazz, signatureAttribute)) + { + signatureAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + if (accepted(clazz, signatureAttribute)) + { + signatureAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + if (accepted(clazz, constantValueAttribute)) + { + constantValueAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + if (accepted(clazz, exceptionsAttribute)) + { + exceptionsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (accepted(clazz, codeAttribute)) + { + codeAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + if (accepted(clazz, stackMapAttribute)) + { + stackMapAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + if (accepted(clazz, stackMapTableAttribute)) + { + stackMapTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + if (accepted(clazz, lineNumberTableAttribute)) + { + lineNumberTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + if (accepted(clazz, localVariableTableAttribute)) + { + localVariableTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + if (accepted(clazz, localVariableTypeTableAttribute)) + { + localVariableTypeTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeVisibleAnnotationsAttribute)) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeVisibleAnnotationsAttribute)) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeVisibleAnnotationsAttribute)) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeInvisibleAnnotationsAttribute)) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeInvisibleAnnotationsAttribute)) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (accepted(clazz, runtimeInvisibleAnnotationsAttribute)) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + if (accepted(clazz, runtimeVisibleParameterAnnotationsAttribute)) + { + runtimeVisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + if (accepted(clazz, runtimeInvisibleParameterAnnotationsAttribute)) + { + runtimeInvisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + if (accepted(clazz, annotationDefaultAttribute)) + { + annotationDefaultAttribute.accept(clazz, method, attributeVisitor); + } + } + + + // Small utility methods. + + private boolean accepted(Clazz clazz, Attribute attribute) + { + return stringMatcher.matches(attribute.getAttributeName(clazz)); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java new file mode 100644 index 0000000..e8f226b --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java @@ -0,0 +1,89 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; + +/** + * This interface specifies the methods for a visitor of <code>Attribute</code> + * objects. + * + * @author Eric Lafortune + */ +public interface AttributeVisitor +{ + // Attributes that are attached to classes. + + public void visitUnknownAttribute( Clazz clazz, UnknownAttribute unknownAttribute); + public void visitSourceFileAttribute( Clazz clazz, SourceFileAttribute sourceFileAttribute); + public void visitSourceDirAttribute( Clazz clazz, SourceDirAttribute sourceDirAttribute); + public void visitInnerClassesAttribute( Clazz clazz, InnerClassesAttribute innerClassesAttribute); + public void visitEnclosingMethodAttribute( Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute); + + // Attributes that are attached to classes, fields, and methods. + + public void visitDeprecatedAttribute( Clazz clazz, DeprecatedAttribute deprecatedAttribute); + public void visitDeprecatedAttribute( Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute); + public void visitDeprecatedAttribute( Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute); + + public void visitSyntheticAttribute( Clazz clazz, SyntheticAttribute syntheticAttribute); + public void visitSyntheticAttribute( Clazz clazz, Field field, SyntheticAttribute syntheticAttribute); + public void visitSyntheticAttribute( Clazz clazz, Method method, SyntheticAttribute syntheticAttribute); + + public void visitSignatureAttribute( Clazz clazz, SignatureAttribute signatureAttribute); + public void visitSignatureAttribute( Clazz clazz, Field field, SignatureAttribute signatureAttribute); + public void visitSignatureAttribute( Clazz clazz, Method method, SignatureAttribute signatureAttribute); + + // Attributes that are attached to fields. + + public void visitConstantValueAttribute( Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute); + + // Attributes that are attached to methods. + + public void visitExceptionsAttribute( Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute); + public void visitCodeAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute); + + // Attributes that are attached to code attributes. + + public void visitStackMapAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute); + public void visitStackMapTableAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute); + public void visitLineNumberTableAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute); + public void visitLocalVariableTableAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute); + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute); + + // Annotation attributes. + + public void visitRuntimeVisibleAnnotationsAttribute( Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute); + public void visitRuntimeVisibleAnnotationsAttribute( Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute); + public void visitRuntimeVisibleAnnotationsAttribute( Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute); + + public void visitRuntimeInvisibleAnnotationsAttribute( Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute); + public void visitRuntimeInvisibleAnnotationsAttribute( Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute); + public void visitRuntimeInvisibleAnnotationsAttribute( Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute); + + public void visitRuntimeVisibleParameterAnnotationsAttribute( Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute); + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute); + + public void visitAnnotationDefaultAttribute( Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute); +} diff --git a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java new file mode 100644 index 0000000..7c85e53 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + +/** + * This interface specifies the methods for a visitor of + * <code>ExceptionInfo</code> objects. Note that there is only a single + * implementation of <code>ExceptionInfo</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface ExceptionInfoVisitor +{ + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java new file mode 100644 index 0000000..91267b0 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.InnerClassesInfo; + + +/** + * This interface specifies the methods for a visitor of + * <code>InnerClassesInfo</code> objects. Note that there is only a single + * implementation of <code>InnerClassesInfo</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface InnerClassesInfoVisitor +{ + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java new file mode 100644 index 0000000..e59ed7b --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>LineNumberInfo</code> objects. Note that there is only a single + * implementation of <code>LineNumberInfo</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LineNumberInfoVisitor +{ + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java new file mode 100644 index 0000000..8647cb3 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>LocalVariableInfo</code> objects. Note that there is only a single + * implementation of <code>LocalVariableInfo</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LocalVariableInfoVisitor +{ + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java new file mode 100644 index 0000000..9ad38e0 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>LocalVariableTypeInfo</code> objects. Note that there is only a single + * implementation of <code>LocalVariableTypeInfo</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LocalVariableTypeInfoVisitor +{ + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java new file mode 100644 index 0000000..870ba94 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java @@ -0,0 +1,356 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; + +/** + * This AttributeVisitor delegates all visits to each AttributeVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiAttributeVisitor implements AttributeVisitor +{ + private AttributeVisitor[] attributeVisitors; + + + public MultiAttributeVisitor() + { + } + + + public MultiAttributeVisitor(AttributeVisitor[] attributeVisitors) + { + this.attributeVisitors = attributeVisitors; + } + + + public void addAttributeVisitor(AttributeVisitor attributeVisitor) + { + incrementArraySize(); + + attributeVisitors[attributeVisitors.length - 1] = attributeVisitor; + } + + + private void incrementArraySize() + { + if (attributeVisitors == null) + { + attributeVisitors = new AttributeVisitor[1]; + } + else + { + AttributeVisitor[] newAttributeVisitors = + new AttributeVisitor[attributeVisitors.length + 1]; + System.arraycopy(attributeVisitors, 0, + newAttributeVisitors, 0, + attributeVisitors.length); + attributeVisitors = newAttributeVisitors; + } + } + + + // Implementations for AttributeVisitor. + + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitUnknownAttribute(clazz, unknownAttribute); + } + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSourceFileAttribute(clazz, sourceFileAttribute); + } + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSourceDirAttribute(clazz, sourceDirAttribute); + } + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitInnerClassesAttribute(clazz, innerClassesAttribute); + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitEnclosingMethodAttribute(clazz, enclosingMethodAttribute); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitDeprecatedAttribute(clazz, deprecatedAttribute); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSyntheticAttribute(clazz, syntheticAttribute); + } + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSignatureAttribute(clazz, syntheticAttribute); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitDeprecatedAttribute(clazz, field, deprecatedAttribute); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSyntheticAttribute(clazz, field, syntheticAttribute); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSignatureAttribute(clazz, field, syntheticAttribute); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitDeprecatedAttribute(clazz, method, deprecatedAttribute); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSyntheticAttribute(clazz, method, syntheticAttribute); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute syntheticAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitSignatureAttribute(clazz, method, syntheticAttribute); + } + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitConstantValueAttribute(clazz, field, constantValueAttribute); + } + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitExceptionsAttribute(clazz, method, exceptionsAttribute); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute); + } + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute); + } + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute); + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute); + } + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); + } + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute); + } + } +} diff --git a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java new file mode 100644 index 0000000..92099f9 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java @@ -0,0 +1,351 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.obfuscate.AttributeShrinker; + +/** + * This AttributeVisitor delegates its visits to one of two other + * AttributeVisitor instances, depending on whether the visited attribute + * is strictly required or not. + * + * @see AttributeShrinker + * + * @author Eric Lafortune + */ +public class RequiredAttributeFilter +implements AttributeVisitor +{ + private final AttributeVisitor requiredAttributeVisitor; + private final AttributeVisitor optionalAttributeVisitor; + + + /** + * Creates a new RequiredAttributeFilter for visiting required attributes. + * @param requiredAttributeVisitor the visitor that will visit required + * attributes. + */ + public RequiredAttributeFilter(AttributeVisitor requiredAttributeVisitor) + { + this(requiredAttributeVisitor, null); + } + + + /** + * Creates a new RequiredAttributeFilter for visiting required and + * optional attributes. + * @param requiredAttributeVisitor the visitor that will visit required + * attributes. + * @param optionalAttributeVisitor the visitor that will visit optional + * attributes. + */ + public RequiredAttributeFilter(AttributeVisitor requiredAttributeVisitor, + AttributeVisitor optionalAttributeVisitor) + { + this.requiredAttributeVisitor = requiredAttributeVisitor; + this.optionalAttributeVisitor = optionalAttributeVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + if (optionalAttributeVisitor != null) + { + unknownAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + if (optionalAttributeVisitor != null) + { + sourceFileAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + if (optionalAttributeVisitor != null) + { + sourceDirAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + if (optionalAttributeVisitor != null) + { + innerClassesAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + if (optionalAttributeVisitor != null) + { + enclosingMethodAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + if (optionalAttributeVisitor != null) + { + deprecatedAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + if (optionalAttributeVisitor != null) + { + deprecatedAttribute.accept(clazz, field, optionalAttributeVisitor); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + if (optionalAttributeVisitor != null) + { + deprecatedAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + if (optionalAttributeVisitor != null) + { + syntheticAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + if (optionalAttributeVisitor != null) + { + syntheticAttribute.accept(clazz, field, optionalAttributeVisitor); + } + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + if (optionalAttributeVisitor != null) + { + syntheticAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + if (optionalAttributeVisitor != null) + { + signatureAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + if (optionalAttributeVisitor != null) + { + signatureAttribute.accept(clazz, field, optionalAttributeVisitor); + } + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + if (optionalAttributeVisitor != null) + { + signatureAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + if (requiredAttributeVisitor != null) + { + constantValueAttribute.accept(clazz, field, requiredAttributeVisitor); + } + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + if (optionalAttributeVisitor != null) + { + exceptionsAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (requiredAttributeVisitor != null) + { + codeAttribute.accept(clazz, method, requiredAttributeVisitor); + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + if (optionalAttributeVisitor != null) + { + stackMapAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor); + } + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + if (requiredAttributeVisitor != null) + { + stackMapTableAttribute.accept(clazz, method, codeAttribute, requiredAttributeVisitor); + } + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + if (optionalAttributeVisitor != null) + { + lineNumberTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + if (optionalAttributeVisitor != null) + { + localVariableTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor); + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + if (optionalAttributeVisitor != null) + { + localVariableTypeTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, field, optionalAttributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, optionalAttributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, field, optionalAttributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeVisibleParameterAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + if (optionalAttributeVisitor != null) + { + runtimeInvisibleParameterAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + if (optionalAttributeVisitor != null) + { + annotationDefaultAttribute.accept(clazz, method, optionalAttributeVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java new file mode 100644 index 0000000..401f188 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java @@ -0,0 +1,379 @@ +/* + * 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.classfile.attribute.visitor; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassPrinter; +import proguard.classfile.attribute.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor computes the stack sizes at all instruction offsets + * of the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class StackSizeComputer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ExceptionInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private boolean[] evaluated = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private int[] stackSizes = new int[ClassConstants.TYPICAL_CODE_LENGTH]; + + private boolean exitInstructionBlock; + + private int stackSize; + private int maxStackSize; + + + /** + * Returns whether the instruction at the given offset is reachable in the + * most recently visited code attribute. + */ + public boolean isReachable(int instructionOffset) + { + return evaluated[instructionOffset]; + } + + + /** + * Returns the stack size at the given instruction offset of the most + * recently visited code attribute. + */ + public int getStackSize(int instructionOffset) + { + if (!evaluated[instructionOffset]) + { + throw new IllegalArgumentException("Unknown stack size at unreachable instruction offset ["+instructionOffset+"]"); + } + + return stackSizes[instructionOffset]; + } + + + /** + * Returns the maximum stack size of the most recently visited code attribute. + */ + public int getMaxStackSize() + { + return maxStackSize; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { +// DEBUG = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + // TODO: Remove this when the code has stabilized. + // Catch any unexpected exceptions from the actual visiting method. + try + { + // Process the code. + visitCodeAttribute0(clazz, method, codeAttribute); + } + catch (RuntimeException ex) + { + System.err.println("Unexpected error while computing stack sizes:"); + System.err.println(" Class = ["+clazz.getName()+"]"); + System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + + if (DEBUG) + { + method.accept(clazz, new ClassPrinter()); + } + + throw ex; + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("StackSizeComputer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Try to reuse the previous array. + int codeLength = codeAttribute.u4codeLength; + if (evaluated.length < codeLength) + { + evaluated = new boolean[codeLength]; + stackSizes = new int[codeLength]; + } + else + { + for (int index = 0; index < codeLength; index++) + { + evaluated[index] = false; + } + } + + // The initial stack is always empty. + stackSize = 0; + maxStackSize = 0; + + // Evaluate the instruction block starting at the entry point of the method. + evaluateInstructionBlock(clazz, method, codeAttribute, 0); + + // Evaluate the exception handlers. + codeAttribute.exceptionsAccept(clazz, method, this); + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + byte opcode = simpleInstruction.opcode; + + // Some simple instructions exit from the current instruction block. + exitInstructionBlock = + opcode == InstructionConstants.OP_IRETURN || + opcode == InstructionConstants.OP_LRETURN || + opcode == InstructionConstants.OP_FRETURN || + opcode == InstructionConstants.OP_DRETURN || + opcode == InstructionConstants.OP_ARETURN || + opcode == InstructionConstants.OP_RETURN || + opcode == InstructionConstants.OP_ATHROW; + } + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Constant pool instructions never end the current instruction block. + exitInstructionBlock = false; + } + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + byte opcode = variableInstruction.opcode; + + // The ret instruction end the current instruction block. + exitInstructionBlock = + opcode == InstructionConstants.OP_RET; + } + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + byte opcode = branchInstruction.opcode; + + // Evaluate the target instruction blocks. + evaluateInstructionBlock(clazz, + method, + codeAttribute, + offset + + branchInstruction.branchOffset); + + // Evaluate the instructions after a subroutine branch. + if (opcode == InstructionConstants.OP_JSR || + opcode == InstructionConstants.OP_JSR_W) + { + // We assume subroutine calls (jsr and jsr_w instructions) don't + // change the stack, other than popping the return value. + stackSize -= 1; + + evaluateInstructionBlock(clazz, + method, + codeAttribute, + offset + branchInstruction.length(offset)); + } + + // Some branch instructions always end the current instruction block. + exitInstructionBlock = + opcode == InstructionConstants.OP_GOTO || + opcode == InstructionConstants.OP_GOTO_W || + opcode == InstructionConstants.OP_JSR || + opcode == InstructionConstants.OP_JSR_W; + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Evaluate the target instruction blocks. + + // Loop over all jump offsets. + int[] jumpOffsets = switchInstruction.jumpOffsets; + + for (int index = 0; index < jumpOffsets.length; index++) + { + // Evaluate the jump instruction block. + evaluateInstructionBlock(clazz, + method, + codeAttribute, + offset + jumpOffsets[index]); + } + + // Also evaluate the default instruction block. + evaluateInstructionBlock(clazz, + method, + codeAttribute, + offset + switchInstruction.defaultOffset); + + // The switch instruction always ends the current instruction block. + exitInstructionBlock = true; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (DEBUG) + { + System.out.println("Exception:"); + } + + // The stack size when entering the exception handler is always 1. + stackSize = 1; + + // Evaluate the instruction block starting at the entry point of the + // exception handler. + evaluateInstructionBlock(clazz, + method, + codeAttribute, + exceptionInfo.u2handlerPC); + } + + + // Small utility methods. + + /** + * Evaluates a block of instructions that hasn't been handled before, + * starting at the given offset and ending at a branch instruction, a return + * instruction, or a throw instruction. Branch instructions are handled + * recursively. + */ + private void evaluateInstructionBlock(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int instructionOffset) + { + if (DEBUG) + { + if (evaluated[instructionOffset]) + { + System.out.println("-- (instruction block at "+instructionOffset+" already evaluated)"); + } + else + { + System.out.println("-- instruction block:"); + } + } + + // Remember the initial stack size. + int initialStackSize = stackSize; + + // Remember the maximum stack size. + if (maxStackSize < stackSize) + { + maxStackSize = stackSize; + } + + // Evaluate any instructions that haven't been evaluated before. + while (!evaluated[instructionOffset]) + { + // Mark the instruction as evaluated. + evaluated[instructionOffset] = true; + + Instruction instruction = InstructionFactory.create(codeAttribute.code, + instructionOffset); + + if (DEBUG) + { + int stackPushCount = instruction.stackPushCount(clazz); + int stackPopCount = instruction.stackPopCount(clazz); + System.out.println("["+instructionOffset+"]: "+ + stackSize+" - "+ + stackPopCount+" + "+ + stackPushCount+" = "+ + (stackSize+stackPushCount-stackPopCount)+": "+ + instruction.toString(instructionOffset)); + } + + // Compute the instruction's effect on the stack size. + stackSize -= instruction.stackPopCount(clazz); + + if (stackSize < 0) + { + throw new IllegalArgumentException("Stack size becomes negative after instruction "+ + instruction.toString(instructionOffset)+" in ["+ + clazz.getName()+"."+ + method.getName(clazz)+ + method.getDescriptor(clazz)+"]"); + } + + stackSizes[instructionOffset] = + stackSize += instruction.stackPushCount(clazz); + + // Remember the maximum stack size. + if (maxStackSize < stackSize) + { + maxStackSize = stackSize; + } + + // Remember the next instruction offset. + int nextInstructionOffset = instructionOffset + + instruction.length(instructionOffset); + + // Visit the instruction, in order to handle branches. + instruction.accept(clazz, method, codeAttribute, instructionOffset, this); + + // Stop evaluating after a branch. + if (exitInstructionBlock) + { + break; + } + + // Continue with the next instruction. + instructionOffset = nextInstructionOffset; + + if (DEBUG) + { + if (evaluated[instructionOffset]) + { + System.out.println("-- (instruction at "+instructionOffset+" already evaluated)"); + } + } + } + + // Restore the stack size for possible subsequent instruction blocks. + this.stackSize = initialStackSize; + } +} diff --git a/src/proguard/classfile/attribute/visitor/package.html b/src/proguard/classfile/attribute/visitor/package.html new file mode 100644 index 0000000..056244a --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/package.html @@ -0,0 +1,3 @@ +<body> +This package contains visitors for attributes and their components. +</body> diff --git a/src/proguard/classfile/constant/ClassConstant.java b/src/proguard/classfile/constant/ClassConstant.java new file mode 100644 index 0000000..d217bf6 --- /dev/null +++ b/src/proguard/classfile/constant/ClassConstant.java @@ -0,0 +1,105 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This Constant represents a class constant in the constant pool. + * + * @author Eric Lafortune + */ +public class ClassConstant extends Constant +{ + public int u2nameIndex; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the java.lang.Class Clazz object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>.. + */ + public Clazz javaLangClassClass; + + + /** + * Creates an uninitialized ClassConstant. + */ + public ClassConstant() + { + } + + + /** + * Creates a new ClassConstant with the given name index. + * @param u2nameIndex the index of the name in the constant pool. + * @param referencedClass the referenced class. + */ + public ClassConstant(int u2nameIndex, + Clazz referencedClass) + { + this.u2nameIndex = u2nameIndex; + this.referencedClass = referencedClass; + } + + + /** + * Returns the name. + */ + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Class; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitClassConstant(clazz, this); + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/constant/Constant.java b/src/proguard/classfile/constant/Constant.java new file mode 100644 index 0000000..30ce5df --- /dev/null +++ b/src/proguard/classfile/constant/Constant.java @@ -0,0 +1,68 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This abstract class represents an entry in the ConstantPool. Specific types + * of entries are subclassed from it. + * + * @author Eric Lafortune + */ +public abstract class Constant implements VisitorAccepter +{ + //public int u1tag; + //public byte info[]; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + // Abstract methods to be implemented by extensions. + + /** + * Returns the constant pool info tag that specifies the entry type. + */ + public abstract int getTag(); + + + /** + * Accepts the given visitor. + */ + public abstract void accept(Clazz clazz, ConstantVisitor constantVisitor); + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/constant/DoubleConstant.java b/src/proguard/classfile/constant/DoubleConstant.java new file mode 100644 index 0000000..61779b5 --- /dev/null +++ b/src/proguard/classfile/constant/DoubleConstant.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a double constant in the constant pool. + * + * @author Eric Lafortune + */ +public class DoubleConstant extends Constant +{ + public double f8value; + + + /** + * Creates an uninitialized DoubleConstant. + */ + public DoubleConstant() + { + } + + + /** + * Creates a new DoubleConstant with the given double value. + */ + public DoubleConstant(double value) + { + f8value = value; + } + + + /** + * Returns the double value of this DoubleConstant. + */ + public double getValue() + { + return f8value; + } + + + /** + * Sets the double value of this DoubleConstant. + */ + public void setValue(double value) + { + f8value = value; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Double; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitDoubleConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/FieldrefConstant.java b/src/proguard/classfile/constant/FieldrefConstant.java new file mode 100644 index 0000000..d4afce5 --- /dev/null +++ b/src/proguard/classfile/constant/FieldrefConstant.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a field reference constant in the constant pool. + * + * @author Eric Lafortune + */ +public class FieldrefConstant extends RefConstant +{ + /** + * Creates an uninitialized FieldrefConstant. + */ + public FieldrefConstant() + { + } + + + /** + * Creates a new FieldrefConstant with the given name and type indices. + * @param u2classIndex the index of the class in the constant pool. + * @param u2nameAndTypeIndex the index of the name and type entry in the constant pool. + * @param referencedClass the referenced class. + * @param referencedMember the referenced member info. + */ + public FieldrefConstant(int u2classIndex, + int u2nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + this.u2classIndex = u2classIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + this.referencedClass = referencedClass; + this.referencedMember = referencedMember; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Fieldref; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitFieldrefConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/FloatConstant.java b/src/proguard/classfile/constant/FloatConstant.java new file mode 100644 index 0000000..578f567 --- /dev/null +++ b/src/proguard/classfile/constant/FloatConstant.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a float constant in the constant pool. + * + * @author Eric Lafortune + */ +public class FloatConstant extends Constant +{ + public float f4value; + + + /** + * Creates an uninitialized FloatConstant. + */ + public FloatConstant() + { + } + + + /** + * Creates a new FloatConstant with the given float value. + */ + public FloatConstant(float value) + { + f4value = value; + } + + + /** + * Returns the float value of this FloatConstant. + */ + public float getValue() + { + return f4value; + } + + + /** + * Sets the float value of this FloatConstant. + */ + public void setValue(float value) + { + f4value = value; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Float; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitFloatConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/IntegerConstant.java b/src/proguard/classfile/constant/IntegerConstant.java new file mode 100644 index 0000000..8a476c6 --- /dev/null +++ b/src/proguard/classfile/constant/IntegerConstant.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a integer constant in the constant pool. + * + * @author Eric Lafortune + */ +public class IntegerConstant extends Constant +{ + public int u4value; + + + /** + * Creates an uninitialized IntegerConstant. + */ + public IntegerConstant() + { + } + + + /** + * Creates a new IntegerConstant with the given integer value. + */ + public IntegerConstant(int value) + { + u4value = value; + } + + + /** + * Returns the integer value of this IntegerConstant. + */ + public int getValue() + { + return u4value; + } + + + /** + * Sets the integer value of this IntegerConstant. + */ + public void setValue(int value) + { + u4value = value; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Integer; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitIntegerConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java new file mode 100644 index 0000000..ddee42f --- /dev/null +++ b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a interface method reference constant in the constant pool. + * + * @author Eric Lafortune + */ +public class InterfaceMethodrefConstant extends RefConstant +{ + /** + * Creates an uninitialized InterfaceMethodrefConstant. + */ + public InterfaceMethodrefConstant() + { + } + + + /** + * Creates a new InterfaceMethodrefConstant with the given name and type indices. + * @param u2classIndex the index of the class in the constant pool. + * @param u2nameAndTypeIndex the index of the name and type entry in the constant pool. + * @param referencedClass the referenced class. + * @param referencedMember the referenced member info. + */ + public InterfaceMethodrefConstant(int u2classIndex, + int u2nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + this.u2classIndex = u2classIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + this.referencedClass = referencedClass; + this.referencedMember = referencedMember; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_InterfaceMethodref; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitInterfaceMethodrefConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/LongConstant.java b/src/proguard/classfile/constant/LongConstant.java new file mode 100644 index 0000000..ea66e07 --- /dev/null +++ b/src/proguard/classfile/constant/LongConstant.java @@ -0,0 +1,82 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a long constant in the constant pool. + * + * @author Eric Lafortune + */ +public class LongConstant extends Constant +{ + public long u8value; + + + /** + * Creates an uninitialized LongConstant. + */ + public LongConstant() + { + } + + + /** + * Creates a new LongConstant with the given long value. + */ + public LongConstant(long value) + { + u8value = value; + } + + + /** + * Returns the long value of this LongConstant. + */ + public long getValue() + { + return u8value; + } + + + /** + * Sets the long value of this LongConstant. + */ + public void setValue(long value) + { + u8value = value; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Long; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitLongConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/MethodrefConstant.java b/src/proguard/classfile/constant/MethodrefConstant.java new file mode 100644 index 0000000..858eec9 --- /dev/null +++ b/src/proguard/classfile/constant/MethodrefConstant.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a method reference constant in the constant pool. + * + * @author Eric Lafortune + */ +public class MethodrefConstant extends RefConstant +{ + /** + * Creates an uninitialized MethodrefConstant. + */ + public MethodrefConstant() + { + } + + + /** + * Creates a new MethodrefConstant with the given name and type indices. + * @param u2classIndex the index of the class in the constant pool. + * @param u2nameAndTypeIndex the index of the name and type entry in the constant pool. + * @param referencedClass the referenced class. + * @param referencedMember the referenced member info. + */ + public MethodrefConstant(int u2classIndex, + int u2nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + this.u2classIndex = u2classIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + this.referencedClass = referencedClass; + this.referencedMember = referencedMember; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Methodref; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitMethodrefConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/NameAndTypeConstant.java b/src/proguard/classfile/constant/NameAndTypeConstant.java new file mode 100644 index 0000000..e83d2f1 --- /dev/null +++ b/src/proguard/classfile/constant/NameAndTypeConstant.java @@ -0,0 +1,119 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a name and type constant in the constant pool. + * + * @author Eric Lafortune + */ +public class NameAndTypeConstant extends Constant +{ + public int u2nameIndex; + public int u2descriptorIndex; + + + /** + * Creates an uninitialized NameAndTypeConstant. + */ + public NameAndTypeConstant() + { + } + + + /** + * Creates a new NameAndTypeConstant with the given name and type indices. + * @param u2nameIndex the index of the name in the constant pool. + * @param u2descriptorIndex the index of the descriptor in the constant + * pool. + */ + public NameAndTypeConstant(int u2nameIndex, + int u2descriptorIndex) + { + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + } + + + /** + * Returns the name index. + */ + protected int getNameIndex() + { + return u2nameIndex; + } + + /** + * Sets the name index. + */ + protected void setNameIndex(int index) + { + u2nameIndex = index; + } + + /** + * Returns the descriptor index. + */ + protected int getDescriptorIndex() + { + return u2descriptorIndex; + } + + /** + * Sets the descriptor index. + */ + protected void setDescriptorIndex(int index) + { + u2descriptorIndex = index; + } + + /** + * Returns the name. + */ + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getString(u2descriptorIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_NameAndType; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitNameAndTypeConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/RefConstant.java b/src/proguard/classfile/constant/RefConstant.java new file mode 100644 index 0000000..4e4d019 --- /dev/null +++ b/src/proguard/classfile/constant/RefConstant.java @@ -0,0 +1,130 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +/** + * This Constant represents a ref constant in the constant pool. + * + * @author Eric Lafortune + */ +public abstract class RefConstant extends Constant +{ + public int u2classIndex; + public int u2nameAndTypeIndex; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + /** + * An extra field optionally pointing to the referenced Member object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>. + */ + public Member referencedMember; + + + protected RefConstant() + { + } + + + /** + * Returns the class index. + */ + public int getClassIndex() + { + return u2classIndex; + } + + /** + * Returns the name-and-type index. + */ + public int getNameAndTypeIndex() + { + return u2nameAndTypeIndex; + } + + /** + * Sets the name-and-type index. + */ + public void setNameAndTypeIndex(int index) + { + u2nameAndTypeIndex = index; + } + + /** + * Returns the class name. + */ + public String getClassName(Clazz clazz) + { + return clazz.getClassName(u2classIndex); + } + + /** + * Returns the method/field name. + */ + public String getName(Clazz clazz) + { + return clazz.getName(u2nameAndTypeIndex); + } + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getType(u2nameAndTypeIndex); + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + + + /** + * Lets the referenced class member accept the given visitor. + */ + public void referencedMemberAccept(MemberVisitor memberVisitor) + { + if (referencedMember != null) + { + referencedMember.accept(referencedClass, + memberVisitor); + } + } +} diff --git a/src/proguard/classfile/constant/StringConstant.java b/src/proguard/classfile/constant/StringConstant.java new file mode 100644 index 0000000..9a8d453 --- /dev/null +++ b/src/proguard/classfile/constant/StringConstant.java @@ -0,0 +1,135 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.*; + +/** + * This Constant represents a string constant in the constant pool. + * + * @author Eric Lafortune + */ +public class StringConstant extends Constant +{ + public int u2stringIndex; + + /** + * An extra field pointing to the referenced Clazz object, if this + * string is being used in Class.forName(), .class, or + * Class.getDeclaredField/Method constructs. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.DynamicClassReferenceInitializer + * DynamicClassReferenceInitializer}</code> or by the <code>{@link + * proguard.classfile.util.DynamicMemberReferenceInitializer + * DynamicMemberReferenceInitializer}</code>. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the referenced Member object, if this + * string is being used in Class.getDeclaredField/Method constructs. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.DynamicMemberReferenceInitializer + * DynamicMemberReferenceInitializer}</code>. + */ + public Member referencedMember; + + /** + * An extra field pointing to the java.lang.String Clazz object. + * This field is typically filled out by the <code>{@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}</code>.. + */ + public Clazz javaLangStringClass; + + + /** + * Creates an uninitialized StringConstant. + */ + public StringConstant() + { + } + + + /** + * Creates a new StringConstant with the given string index. + * @param u2stringIndex the index of the string in the constant pool. + * @param referencedClass the referenced class, if any. + * @param referenceMember the referenced class member, if any. + */ + public StringConstant(int u2stringIndex, + Clazz referencedClass, + Member referenceMember) + { + this.u2stringIndex = u2stringIndex; + this.referencedClass = referencedClass; + this.referencedMember = referenceMember; + } + + + /** + * Returns the string value. + */ + public String getString(Clazz clazz) + { + return clazz.getString(u2stringIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_String; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitStringConstant(clazz, this); + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null && + referencedMember == null) + { + referencedClass.accept(classVisitor); + } + } + + + /** + * Lets the referenced member accept the given visitor. + */ + public void referencedMemberAccept(MemberVisitor memberVisitor) + { + if (referencedMember != null) + { + referencedMember.accept(referencedClass, memberVisitor); + } + } +} diff --git a/src/proguard/classfile/constant/Utf8Constant.java b/src/proguard/classfile/constant/Utf8Constant.java new file mode 100644 index 0000000..ae419c9 --- /dev/null +++ b/src/proguard/classfile/constant/Utf8Constant.java @@ -0,0 +1,285 @@ +/* + * 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.classfile.constant; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +import java.io.UnsupportedEncodingException; + +/** + * This Constant represents a UTF-8 constant in the constant pool. + * + * @author Eric Lafortune + */ +public class Utf8Constant extends Constant +{ + private static final char TWO_BYTE_LIMIT = 0x80; + private static final int TWO_BYTE_CONSTANT1 = 0xc0; + private static final int TWO_BYTE_CONSTANT2 = 0x80; + private static final int TWO_BYTE_SHIFT1 = 6; + private static final int TWO_BYTE_MASK1 = 0x1f; + private static final int TWO_BYTE_MASK2 = 0x3f; + + private static final char THREE_BYTE_LIMIT = 0x800; + private static final int THREE_BYTE_CONSTANT1 = 0xe0; + private static final int THREE_BYTE_CONSTANT2 = 0x80; + private static final int THREE_BYTE_CONSTANT3 = 0x80; + private static final int THREE_BYTE_SHIFT1 = 12; + private static final int THREE_BYTE_SHIFT2 = 6; + private static final int THREE_BYTE_MASK1 = 0x0f; + private static final int THREE_BYTE_MASK2 = 0x3f; + private static final int THREE_BYTE_MASK3 = 0x3f; + + + // There are a lot of Utf8Constant objects, so we're optimising their storage. + // Initially, we're storing the UTF-8 bytes in a byte array. + // When the corresponding String is requested, we ditch the array and just + // store the String. + + //private int u2length; + private byte[] bytes; + + private String string; + + + /** + * Creates an uninitialized Utf8Constant. + * + */ + public Utf8Constant() + { + } + + + /** + * Creates a Utf8Constant containing the given string. + */ + public Utf8Constant(String string) + { + this.bytes = null; + this.string = string; + } + + + /** + * Initializes the UTF-8 data with an array of bytes. + */ + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + this.string = null; + } + + + /** + * Returns the UTF-8 data as an array of bytes. + */ + public byte[] getBytes() + { + try + { + switchToByteArrayRepresentation(); + } + catch (UnsupportedEncodingException ex) + { + throw new RuntimeException(ex.getMessage()); + } + + return bytes; + } + + + /** + * Initializes the UTF-8 data with a String. + */ + public void setString(String utf8String) + { + this.bytes = null; + this.string = utf8String; + } + + + /** + * Returns the UTF-8 data as a String. + */ + public String getString() + { + try + { + switchToStringRepresentation(); + } + catch (UnsupportedEncodingException ex) + { + throw new RuntimeException(ex.getMessage()); + } + + return string; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Utf8; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitUtf8Constant(clazz, this); + } + + + // Small utility methods. + + /** + * Switches to a byte array representation of the UTF-8 data. + */ + private void switchToByteArrayRepresentation() throws UnsupportedEncodingException + { + if (bytes == null) + { + bytes = getByteArrayRepresentation(string); + string = null; + } + } + + + /** + * Switches to a String representation of the UTF-8 data. + */ + private void switchToStringRepresentation() throws UnsupportedEncodingException + { + if (string == null) + { + string = getStringRepresentation(bytes); + bytes = null; + } + } + + + /** + * Returns the modified UTF-8 byte array representation of the given string. + */ + private byte[] getByteArrayRepresentation(String string) throws UnsupportedEncodingException + { + // We're computing the byte array ourselves, because the implementation + // of String.getBytes("UTF-8") has a bug, at least up to JRE 1.4.2. + // Also note the special treatment of the 0 character. + + // Compute the byte array length. + int byteLength = 0; + int stringLength = string.length(); + for (int stringIndex = 0; stringIndex < stringLength; stringIndex++) + { + char c = string.charAt(stringIndex); + + // The character is represented by one, two, or three bytes. + byteLength += c == 0 ? 2 : + c < TWO_BYTE_LIMIT ? 1 : + c < THREE_BYTE_LIMIT ? 2 : + 3; + } + + // Allocate the byte array with the computed length. + byte[] bytes = new byte[byteLength]; + + // Fill out the array. + int byteIndex = 0; + for (int stringIndex = 0; stringIndex < stringLength; stringIndex++) + { + char c = string.charAt(stringIndex); + if (c == 0) + { + // The 0 character gets a two-byte representation in classes. + bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT1; + bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT2; + } + else if (c < TWO_BYTE_LIMIT) + { + // The character is represented by a single byte. + bytes[byteIndex++] = (byte)c; + } + else if (c < THREE_BYTE_LIMIT) + { + // The character is represented by two bytes. + bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT1 | ((c >>> TWO_BYTE_SHIFT1) & TWO_BYTE_MASK1)); + bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT2 | ( c & TWO_BYTE_MASK2)); + } + else + { + // The character is represented by three bytes. + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT1 | ((c >>> THREE_BYTE_SHIFT1) & THREE_BYTE_MASK1)); + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT2 | ((c >>> THREE_BYTE_SHIFT2) & THREE_BYTE_MASK2)); + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT3 | ( c & THREE_BYTE_MASK3)); + } + } + + return bytes; + } + + + /** + * Returns the String representation of the given modified UTF-8 byte array. + */ + private String getStringRepresentation(byte[] bytes) throws UnsupportedEncodingException + { + // We're computing the string ourselves, because the implementation + // of "new String(bytes)" doesn't honor the special treatment of + // the 0 character in JRE 1.6_u11. + + // Allocate the byte array with the computed length. + char[] chars = new char[bytes.length]; + + // Fill out the array. + int charIndex = 0; + int byteIndex = 0; + while (byteIndex < bytes.length) + { + + int b = bytes[byteIndex++] & 0xff; + + // Depending on the flag bits in the first byte, the character + // is represented by a single byte, by two bytes, or by three + // bytes. We're not checking the redundant flag bits in the + // second byte and the third byte. + try + { + chars[charIndex++] = + (char)(b < TWO_BYTE_CONSTANT1 ? b : + + b < THREE_BYTE_CONSTANT1 ? ((b & TWO_BYTE_MASK1) << TWO_BYTE_SHIFT1) | + ((bytes[byteIndex++] & TWO_BYTE_MASK2) ) : + + ((b & THREE_BYTE_MASK1) << THREE_BYTE_SHIFT1) | + ((bytes[byteIndex++] & THREE_BYTE_MASK2) << THREE_BYTE_SHIFT2) | + ((bytes[byteIndex++] & THREE_BYTE_MASK3) )); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new UnsupportedEncodingException("Missing UTF-8 bytes after initial byte [0x"+Integer.toHexString(b)+"] in string ["+new String(chars, 0, charIndex)+"]"); + } + } + + return new String(chars, 0, charIndex); + } +} diff --git a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java new file mode 100644 index 0000000..d2d3c2c --- /dev/null +++ b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java @@ -0,0 +1,53 @@ +/* + * 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.classfile.constant.visitor; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This ClassVisitor lets a given ConstantVisitor visit all constant pool + * entries of the program classes it visits. + * + * @author Eric Lafortune + */ +public class AllConstantVisitor implements ClassVisitor +{ + private final ConstantVisitor constantVisitor; + + + public AllConstantVisitor(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.constantPoolEntriesAccept(constantVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) {} +} diff --git a/src/proguard/classfile/constant/visitor/ConstantVisitor.java b/src/proguard/classfile/constant/visitor/ConstantVisitor.java new file mode 100644 index 0000000..6cae352 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/ConstantVisitor.java @@ -0,0 +1,46 @@ +/* + * 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.classfile.constant.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.constant.*; + + +/** + * This interface specifies the methods for a visitor of <code>Constant</code> + * objects. + * + * @author Eric Lafortune + */ +public interface ConstantVisitor +{ + public void visitIntegerConstant( Clazz clazz, IntegerConstant integerConstant); + public void visitLongConstant( Clazz clazz, LongConstant longConstant); + public void visitFloatConstant( Clazz clazz, FloatConstant floatConstant); + public void visitDoubleConstant( Clazz clazz, DoubleConstant doubleConstant); + public void visitStringConstant( Clazz clazz, StringConstant stringConstant); + public void visitUtf8Constant( Clazz clazz, Utf8Constant utf8Constant); + public void visitFieldrefConstant( Clazz clazz, FieldrefConstant fieldrefConstant); + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant); + public void visitMethodrefConstant( Clazz clazz, MethodrefConstant methodrefConstant); + public void visitClassConstant( Clazz clazz, ClassConstant classConstant); + public void visitNameAndTypeConstant( Clazz clazz, NameAndTypeConstant nameAndTypeConstant); +} diff --git a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java new file mode 100644 index 0000000..fbb3e52 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java @@ -0,0 +1,69 @@ +/* + * 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.classfile.constant.visitor; + +import proguard.classfile.*; +import proguard.classfile.editor.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>ConstantVisitor</code> delegates its visits to class constants + * to another given <code>ConstantVisitor</code>, except for one given class. + * + * @author Eric Lafortune + */ +public class ExceptClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final String exceptClassName; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ClassNameFilter. + * @param exceptClassName the name of the class that will not be visited. + * @param constantVisitor the <code>ConstantVisitor</code> to which visits + * will be delegated. + */ + public ExceptClassConstantFilter(String exceptClassName, + ConstantVisitor constantVisitor) + { + this.exceptClassName = exceptClassName; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (!classConstant.getName(clazz).equals(exceptClassName)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/constant/visitor/package.html b/src/proguard/classfile/constant/visitor/package.html new file mode 100644 index 0000000..e20f48e --- /dev/null +++ b/src/proguard/classfile/constant/visitor/package.html @@ -0,0 +1,3 @@ +<body> +This package contains visitors for class constants. +</body> diff --git a/src/proguard/classfile/editor/AccessFixer.java b/src/proguard/classfile/editor/AccessFixer.java new file mode 100644 index 0000000..7d6274e --- /dev/null +++ b/src/proguard/classfile/editor/AccessFixer.java @@ -0,0 +1,164 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This ConstantVisitor fixes the access modifiers of all classes and class + * members that are referenced by the constants that it visits. + * + * @author Eric Lafortune + */ +public class AccessFixer +extends SimplifiedVisitor +implements ConstantVisitor, + ClassVisitor, + MemberVisitor +{ + private MyReferencedClassFinder referencedClassFinder = new MyReferencedClassFinder(); + + private Clazz referencingClass; + private Clazz referencedClass; + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + referencingClass = clazz; + referencedClass = stringConstant.referencedClass; + + // Make sure the access flags of the referenced class or class member, + // if any, are acceptable. + stringConstant.referencedClassAccept(this); + stringConstant.referencedMemberAccept(this); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + referencingClass = clazz; + + // Remember the specified class, since it might be different from + // the referenced class that acutally contains the class member. + clazz.constantPoolEntryAccept(refConstant.u2classIndex, referencedClassFinder); + + // Make sure the access flags of the referenced class member are + // acceptable. + refConstant.referencedMemberAccept(this); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + referencingClass = clazz; + + // Make sure the access flags of the referenced class are acceptable. + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + int currentAccessFlags = programClass.getAccessFlags(); + int currentAccessLevel = AccessUtil.accessLevel(currentAccessFlags); + + // Compute the required access level. + Clazz referencingClass = this.referencingClass; + int requiredAccessLevel = + inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE : + AccessUtil.PUBLIC; + + // Fix the class access flags if necessary. + if (currentAccessLevel < requiredAccessLevel) + { + programClass.u2accessFlags = + AccessUtil.replaceAccessFlags(currentAccessFlags, + AccessUtil.accessFlags(requiredAccessLevel)); + } + } + + + // Implementations for MemberVisitor. + + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) {} + + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + int currentAccessFlags = programMember.getAccessFlags(); + int currentAccessLevel = AccessUtil.accessLevel(currentAccessFlags); + + // Compute the required access level. + int requiredAccessLevel = + programClass.equals(referencingClass) ? AccessUtil.PRIVATE : + inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE : + referencedClass.extends_(referencingClass) && + referencingClass.extends_(programClass) ? AccessUtil.PROTECTED : + AccessUtil.PUBLIC; + + // Fix the class member access flags if necessary. + if (currentAccessLevel < requiredAccessLevel) + { + programMember.u2accessFlags = + AccessUtil.replaceAccessFlags(currentAccessFlags, + AccessUtil.accessFlags(requiredAccessLevel)); + } + } + + + /** + * This ConstantVisitor returns the referenced class of the class constant + * that it visits. + */ + private class MyReferencedClassFinder + extends SimplifiedVisitor + implements ConstantVisitor + { + // Implementations for ConstantVisitor. + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + referencedClass = classConstant.referencedClass; + } + } + + + // Small utility methods. + + private boolean inSamePackage(ProgramClass class1, Clazz class2) + { + return ClassUtil.internalPackageName(class1.getName()).equals( + ClassUtil.internalPackageName(class2.getName())); + } +} diff --git a/src/proguard/classfile/editor/AnnotationAdder.java b/src/proguard/classfile/editor/AnnotationAdder.java new file mode 100644 index 0000000..359164a --- /dev/null +++ b/src/proguard/classfile/editor/AnnotationAdder.java @@ -0,0 +1,153 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AnnotationVisitor adds all annotations that it visits to the given + * target annotation element value, target annotation attribute, or target + * parameter annotation attribute. + * + * @author Eric Lafortune + */ +public class AnnotationAdder +extends SimplifiedVisitor +implements AnnotationVisitor +{ + private static final ElementValue[] EMPTY_ELEMENT_VALUES = new ElementValue[0]; + + + private final ProgramClass targetClass; + private final AnnotationElementValue targetAnnotationElementValue; + private final AnnotationsAttributeEditor annotationsAttributeEditor; + private final ParameterAnnotationsAttributeEditor parameterAnnotationsAttributeEditor; + + private final ConstantAdder constantAdder; + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target annotation element value. + */ + public AnnotationAdder(ProgramClass targetClass, + AnnotationElementValue targetAnnotationElementValue) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = targetAnnotationElementValue; + this.annotationsAttributeEditor = null; + this.parameterAnnotationsAttributeEditor = null; + + constantAdder = new ConstantAdder(targetClass); + } + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target annotations attribute. + */ + public AnnotationAdder(ProgramClass targetClass, + AnnotationsAttribute targetAnnotationsAttribute) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = null; + this.annotationsAttributeEditor = new AnnotationsAttributeEditor(targetAnnotationsAttribute); + this.parameterAnnotationsAttributeEditor = null; + + constantAdder = new ConstantAdder(targetClass); + } + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target parameter annotations attribute. + */ + public AnnotationAdder(ProgramClass targetClass, + ParameterAnnotationsAttribute targetParameterAnnotationsAttribute) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = null; + this.annotationsAttributeEditor = null; + this.parameterAnnotationsAttributeEditor = new ParameterAnnotationsAttributeEditor(targetParameterAnnotationsAttribute); + + constantAdder = new ConstantAdder(targetClass); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + Annotation newAnnotation = + new Annotation(constantAdder.addConstant(clazz, annotation.u2typeIndex), + 0, + annotation.u2elementValuesCount > 0 ? + new ElementValue[annotation.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + // TODO: Clone array. + newAnnotation.referencedClasses = annotation.referencedClasses; + + // Add the element values. + annotation.elementValuesAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotation, + false)); + + // What's the target? + if (targetAnnotationElementValue != null) + { + // Simply set the completed annotation. + targetAnnotationElementValue.annotationValue = newAnnotation; + } + else + { + // Add the completed annotation. + annotationsAttributeEditor.addAnnotation(newAnnotation); + } + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + Annotation newAnnotation = + new Annotation(constantAdder.addConstant(clazz, annotation.u2typeIndex), + 0, + annotation.u2elementValuesCount > 0 ? + new ElementValue[annotation.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + // TODO: Clone array. + newAnnotation.referencedClasses = annotation.referencedClasses; + + // Add the element values. + annotation.elementValuesAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotation, + false)); + + // Add the completed annotation. + parameterAnnotationsAttributeEditor.addAnnotation(parameterIndex, newAnnotation); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java new file mode 100644 index 0000000..bf8852c --- /dev/null +++ b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.annotation.*; + +/** + * This class can add annotations to a given annotations attribute. + * Annotations to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class AnnotationsAttributeEditor +{ + private AnnotationsAttribute targetAnnotationsAttribute; + + + /** + * Creates a new AnnotationsAttributeEditor that will edit annotations in + * the given annotations attribute. + */ + public AnnotationsAttributeEditor(AnnotationsAttribute targetAnnotationsAttribute) + { + this.targetAnnotationsAttribute = targetAnnotationsAttribute; + } + + + /** + * Adds a given annotation to the annotations attribute. + */ + public void addAnnotation(Annotation annotation) + { + int annotationsCount = targetAnnotationsAttribute.u2annotationsCount; + Annotation[] annotations = targetAnnotationsAttribute.annotations; + + // Make sure there is enough space for the new annotation. + if (annotations.length <= annotationsCount) + { + targetAnnotationsAttribute.annotations = new Annotation[annotationsCount+1]; + System.arraycopy(annotations, 0, + targetAnnotationsAttribute.annotations, 0, + annotationsCount); + annotations = targetAnnotationsAttribute.annotations; + } + + // Add the annotation. + annotations[targetAnnotationsAttribute.u2annotationsCount++] = annotation; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/AttributeAdder.java b/src/proguard/classfile/editor/AttributeAdder.java new file mode 100644 index 0000000..2b610b7 --- /dev/null +++ b/src/proguard/classfile/editor/AttributeAdder.java @@ -0,0 +1,457 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor adds all attributes that it visits to the given + * target class, class member, or attribute. + * + * @author Eric Lafortune + */ +public class AttributeAdder +extends SimplifiedVisitor +implements AttributeVisitor +{ + private static final byte[] EMPTY_BYTES = new byte[0]; + private static final int[] EMPTY_INTS = new int[0]; + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + private static final ExceptionInfo[] EMPTY_EXCEPTIONS = new ExceptionInfo[0]; + + + private final ProgramClass targetClass; + private final ProgramMember targetMember; + private final CodeAttribute targetCodeAttribute; + private final boolean replaceAttributes; + + private final ConstantAdder constantAdder; + private final AttributesEditor attributesEditor; + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target class. + */ + public AttributeAdder(ProgramClass targetClass, + boolean replaceAttributes) + { + this(targetClass, null, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target class member. + */ + public AttributeAdder(ProgramClass targetClass, + ProgramMember targetMember, + boolean replaceAttributes) + { + this(targetClass, targetMember, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target attribute. + */ + public AttributeAdder(ProgramClass targetClass, + ProgramMember targetMember, + CodeAttribute targetCodeAttribute, + boolean replaceAttributes) + { + this.targetClass = targetClass; + this.targetMember = targetMember; + this.targetCodeAttribute = targetCodeAttribute; + this.replaceAttributes = replaceAttributes; + + constantAdder = new ConstantAdder(targetClass); + attributesEditor = new AttributesEditor(targetClass, + targetMember, + targetCodeAttribute, + replaceAttributes); + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // Create a copy of the attribute. + UnknownAttribute newUnknownAttribute = + new UnknownAttribute(constantAdder.addConstant(clazz, unknownAttribute.u2attributeNameIndex), + unknownAttribute.u4attributeLength, + unknownAttribute.info); + + // Add it to the target class. + attributesEditor.addAttribute(newUnknownAttribute); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + // Create a copy of the attribute. + SourceFileAttribute newSourceFileAttribute = + new SourceFileAttribute(constantAdder.addConstant(clazz, sourceFileAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, sourceFileAttribute.u2sourceFileIndex)); + + // Add it to the target class. + attributesEditor.addAttribute(newSourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + // Create a copy of the attribute. + SourceDirAttribute newSourceDirAttribute = + new SourceDirAttribute(constantAdder.addConstant(clazz, sourceDirAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, sourceDirAttribute.u2sourceDirIndex)); + + // Add it to the target class. + attributesEditor.addAttribute(newSourceDirAttribute); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // TODO: Implement method. + // Note that the attribute may already be present. +// // Create a copy of the attribute. +// InnerClassesAttribute newInnerClassesAttribute = +// new InnerClassesAttribute(constantAdder.addConstant(clazz, innerClassesAttribute.u2attributeNameIndex), +// 0, +// null); +// +// // Add it to the target class. +// attributesEditor.addClassAttribute(newInnerClassesAttribute); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + // Create a copy of the attribute. + EnclosingMethodAttribute newEnclosingMethodAttribute = + new EnclosingMethodAttribute(constantAdder.addConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, enclosingMethodAttribute.u2classIndex), + enclosingMethodAttribute.u2nameAndTypeIndex == 0 ? 0 : + constantAdder.addConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex)); + + newEnclosingMethodAttribute.referencedClass = enclosingMethodAttribute.referencedClass; + newEnclosingMethodAttribute.referencedMethod = enclosingMethodAttribute.referencedMethod; + + // Add it to the target class. + attributesEditor.addAttribute(newEnclosingMethodAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + // Create a copy of the attribute. + DeprecatedAttribute newDeprecatedAttribute = + new DeprecatedAttribute(constantAdder.addConstant(clazz, deprecatedAttribute.u2attributeNameIndex)); + + // Add it to the target. + attributesEditor.addAttribute(newDeprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + // Create a copy of the attribute. + SyntheticAttribute newSyntheticAttribute = + new SyntheticAttribute(constantAdder.addConstant(clazz, syntheticAttribute.u2attributeNameIndex)); + + // Add it to the target. + attributesEditor.addAttribute(newSyntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Create a copy of the attribute. + SignatureAttribute newSignatureAttribute = + new SignatureAttribute(constantAdder.addConstant(clazz, signatureAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, signatureAttribute.u2signatureIndex)); + + newSignatureAttribute.referencedClasses = signatureAttribute.referencedClasses; + + // Add it to the target. + attributesEditor.addAttribute(newSignatureAttribute); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + // Create a copy of the attribute. + ConstantValueAttribute newConstantValueAttribute = + new ConstantValueAttribute(constantAdder.addConstant(clazz, constantValueAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, constantValueAttribute.u2constantValueIndex)); + + // Add it to the target field. + attributesEditor.addAttribute(newConstantValueAttribute); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + // Create a new exceptions attribute. + ExceptionsAttribute newExceptionsAttribute = + new ExceptionsAttribute(constantAdder.addConstant(clazz, exceptionsAttribute.u2attributeNameIndex), + 0, + exceptionsAttribute.u2exceptionIndexTableLength > 0 ? + new int[exceptionsAttribute.u2exceptionIndexTableLength] : + EMPTY_INTS); + + // Add the exceptions. + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, + new ExceptionAdder(targetClass, + newExceptionsAttribute)); + + // Add it to the target method. + attributesEditor.addAttribute(newExceptionsAttribute); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Create a new code attribute. + CodeAttribute newCodeAttribute = + new CodeAttribute(constantAdder.addConstant(clazz, codeAttribute.u2attributeNameIndex), + codeAttribute.u2maxStack, + codeAttribute.u2maxLocals, + 0, + EMPTY_BYTES, + 0, + codeAttribute.u2exceptionTableLength > 0 ? + new ExceptionInfo[codeAttribute.u2exceptionTableLength] : + EMPTY_EXCEPTIONS, + 0, + codeAttribute.u2attributesCount > 0 ? + new Attribute[codeAttribute.u2attributesCount] : + EMPTY_ATTRIBUTES); + + CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); + + // Add the instructions. + codeAttribute.instructionsAccept(clazz, + method, + new InstructionAdder(targetClass, + codeAttributeComposer)); + + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + + // Add the exceptions. + codeAttribute.exceptionsAccept(clazz, + method, + new ExceptionInfoAdder(targetClass, + codeAttributeComposer)); + + codeAttributeComposer.endCodeFragment(); + + // Add the attributes. + codeAttribute.attributesAccept(clazz, + method, + new AttributeAdder(targetClass, + targetMember, + newCodeAttribute, + replaceAttributes)); + + // Apply these changes to the new code attribute. + codeAttributeComposer.visitCodeAttribute(targetClass, + (Method)targetMember, + newCodeAttribute); + + // Add the completed code attribute to the target method. + attributesEditor.addAttribute(newCodeAttribute); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // TODO: Implement method. + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // TODO: Implement method. + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Create a new line number table attribute. + LineNumberTableAttribute newLineNumberTableAttribute = + new LineNumberTableAttribute(constantAdder.addConstant(clazz, lineNumberTableAttribute.u2attributeNameIndex), + 0, + new LineNumberInfo[lineNumberTableAttribute.u2lineNumberTableLength]); + + // Add the line numbers. + lineNumberTableAttribute.lineNumbersAccept(clazz, + method, + codeAttribute, + new LineNumberInfoAdder(newLineNumberTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLineNumberTableAttribute); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Create a new local variable table attribute. + LocalVariableTableAttribute newLocalVariableTableAttribute = + new LocalVariableTableAttribute(constantAdder.addConstant(clazz, localVariableTableAttribute.u2attributeNameIndex), + 0, + new LocalVariableInfo[localVariableTableAttribute.u2localVariableTableLength]); + + // Add the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, + method, + codeAttribute, + new LocalVariableInfoAdder(targetClass, newLocalVariableTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLocalVariableTableAttribute); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Create a new local variable type table attribute. + LocalVariableTypeTableAttribute newLocalVariableTypeTableAttribute = + new LocalVariableTypeTableAttribute(constantAdder.addConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex), + 0, + new LocalVariableTypeInfo[localVariableTypeTableAttribute.u2localVariableTypeTableLength]); + + // Add the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, + method, + codeAttribute, + new LocalVariableTypeInfoAdder(targetClass, newLocalVariableTypeTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLocalVariableTypeTableAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeVisibleAnnotationsAttribute newAnnotationsAttribute = + new RuntimeVisibleAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeVisibleAnnotationsAttribute.u2attributeNameIndex), + 0, + new Annotation[runtimeVisibleAnnotationsAttribute.u2annotationsCount]); + + // Add the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeInvisibleAnnotationsAttribute newAnnotationsAttribute = + new RuntimeInvisibleAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeInvisibleAnnotationsAttribute.u2attributeNameIndex), + 0, + new Annotation[runtimeInvisibleAnnotationsAttribute.u2annotationsCount]); + + // Add the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationsAttribute); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeVisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute = + new RuntimeVisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeVisibleParameterAnnotationsAttribute.u2attributeNameIndex), + 0, + new int[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount], + new Annotation[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount][]); + + // Add the annotations. + runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, + method, + new AnnotationAdder(targetClass, + newParameterAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newParameterAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeInvisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute = + new RuntimeInvisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeInvisibleParameterAnnotationsAttribute.u2attributeNameIndex), + 0, + new int[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount], + new Annotation[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount][]); + + // Add the annotations. + runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz, + method, + new AnnotationAdder(targetClass, + newParameterAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newParameterAnnotationsAttribute); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Create a new annotation default attribute. + AnnotationDefaultAttribute newAnnotationDefaultAttribute = + new AnnotationDefaultAttribute(constantAdder.addConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex), + null); + + // Add the annotations. + annotationDefaultAttribute.defaultValueAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotationDefaultAttribute, + false)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationDefaultAttribute); + } +} diff --git a/src/proguard/classfile/editor/AttributeSorter.java b/src/proguard/classfile/editor/AttributeSorter.java new file mode 100644 index 0000000..d8e3367 --- /dev/null +++ b/src/proguard/classfile/editor/AttributeSorter.java @@ -0,0 +1,89 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * This ClassVisitor sorts the attributes of the classes that it visits. + * The sorting order is based on the types of the attributes. + * + * @author Eric Lafortune + */ +public class AttributeSorter +extends SimplifiedVisitor +implements ClassVisitor, MemberVisitor, AttributeVisitor, Comparator +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Sort the attributes. + Arrays.sort(programClass.attributes, 0, programClass.u2attributesCount, this); + + // Sort the attributes of the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Sort the attributes. + Arrays.sort(programMember.attributes, 0, programMember.u2attributesCount, this); + + // Sort the attributes of the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Sort the attributes. + Arrays.sort(codeAttribute.attributes, 0, codeAttribute.u2attributesCount, this); + } + + + // Implementations for Comparator. + + public int compare(Object object1, Object object2) + { + Attribute attribute1 = (Attribute)object1; + Attribute attribute2 = (Attribute)object2; + + return attribute1.u2attributeNameIndex < attribute2.u2attributeNameIndex ? -1 : + attribute1.u2attributeNameIndex > attribute2.u2attributeNameIndex ? 1 : + 0; + } +} diff --git a/src/proguard/classfile/editor/AttributesEditor.java b/src/proguard/classfile/editor/AttributesEditor.java new file mode 100644 index 0000000..10846cc --- /dev/null +++ b/src/proguard/classfile/editor/AttributesEditor.java @@ -0,0 +1,269 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + +/** + * This class can add and delete attributes to and from classes, fields, + * methods, and code attributes. Attributes to be added must be filled out + * beforehand, including their references to the constant pool. Existing + * attributes of the same type are always replaced. + * + * @author Eric Lafortune + */ +public class AttributesEditor +{ + private final ProgramClass targetClass; + private final ProgramMember targetMember; + private final CodeAttribute targetAttribute; + private final boolean replaceAttributes; + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target class. + */ + public AttributesEditor(ProgramClass targetClass, + boolean replaceAttributes) + { + this(targetClass, null, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target class member. + */ + public AttributesEditor(ProgramClass targetClass, + ProgramMember targetMember, + boolean replaceAttributes) + { + this(targetClass, targetMember, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target code attribute. + */ + public AttributesEditor(ProgramClass targetClass, + ProgramMember targetMember, + CodeAttribute targetAttribute, + boolean replaceAttributes) + { + this.targetClass = targetClass; + this.targetMember = targetMember; + this.targetAttribute = targetAttribute; + this.replaceAttributes = replaceAttributes; + } + + + /** + * Adds the given attribute to the target. + */ + public void addAttribute(Attribute attribute) + { + // What's the target? + if (targetAttribute != null) + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attribute)) + { + // Otherwise append the attribute. + targetAttribute.attributes = + addAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attribute); + + targetAttribute.u2attributesCount++; + } + } + else if (targetMember != null) + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attribute)) + { + // Otherwise append the attribute. + targetMember.attributes = + addAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attribute); + + targetMember.u2attributesCount++; + } + } + else + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attribute)) + { + // Otherwise append the attribute. + targetClass.attributes = + addAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attribute); + + targetClass.u2attributesCount++; + } + } + } + + + /** + * Deletes the specified attribute from the target. + */ + public void deleteAttribute(String attributeName) + { + // What's the target? + if (targetAttribute != null) + { + targetAttribute.u2attributesCount = + deleteAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attributeName); + } + else if (targetMember != null) + { + targetMember.u2attributesCount = + deleteAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attributeName); + } + else + { + targetClass.u2attributesCount = + deleteAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attributeName); + } + } + + + // Small utility methods. + + /** + * Tries put the given attribute in place of an existing attribute of the + * same name, returning whether it was present. + */ + private boolean replaceAttribute(int attributesCount, + Attribute[] attributes, + Attribute attribute) + { + // Find the attribute with the same name. + int index = findAttribute(attributesCount, + attributes, + attribute.getAttributeName(targetClass)); + if (index < 0) + { + return false; + } + + attributes[index] = attribute; + + return true; + } + + + /** + * Appends the given attribute to the given array of attributes, creating a + * new array if necessary. + */ + private Attribute[] addAttribute(int attributesCount, + Attribute[] attributes, + Attribute attribute) + { + // Is the array too small to contain the additional attribute? + if (attributes.length <= attributesCount) + { + // Create a new array and copy the attributes into it. + Attribute[] newAttributes = new Attribute[attributesCount + 1]; + System.arraycopy(attributes, 0, + newAttributes, 0, + attributesCount); + attributes = newAttributes; + } + + // Append the attribute. + attributes[attributesCount] = attribute; + + return attributes; + } + + + /** + * Deletes the attributes with the given name from the given array of + * attributes, returning the new number of attributes. + */ + private int deleteAttribute(int attributesCount, + Attribute[] attributes, + String attributeName) + { + // Find the attribute. + int index = findAttribute(attributesCount, + attributes, + attributeName); + if (index < 0) + { + return attributesCount; + } + + // Shift the other attributes in the array. + System.arraycopy(attributes, index + 1, + attributes, index, + attributesCount - index - 1); + + // Clear the last entry in the array. + attributes[--attributesCount] = null; + + return attributesCount; + } + + + /** + * Finds the index of the attribute with the given name in the given + * array of attributes. + */ + private int findAttribute(int attributesCount, + Attribute[] attributes, + String attributeName) + { + for (int index = 0; index < attributesCount; index++) + { + if (attributes[index].getAttributeName(targetClass).equals(attributeName)) + { + return index; + } + } + + return -1; + } +} diff --git a/src/proguard/classfile/editor/ClassEditor.java b/src/proguard/classfile/editor/ClassEditor.java new file mode 100644 index 0000000..e503ea3 --- /dev/null +++ b/src/proguard/classfile/editor/ClassEditor.java @@ -0,0 +1,255 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; + +/** + * This class can add interfaces and class members to a given class. + * Elements to be added must be filled out beforehand, including their + * references to the constant pool. + * + * @author Eric Lafortune + */ +public class ClassEditor +{ + private static final boolean DEBUG = false; + + private ProgramClass targetClass; + + + /** + * Creates a new ClassEditor that will edit elements in the given + * target class. + */ + public ClassEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Adds the given interface. + */ + public void addInterface(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + // Make sure there is enough space for the new interface. + if (interfaces.length <= interfacesCount) + { + targetClass.u2interfaces = new int[interfacesCount+1]; + System.arraycopy(interfaces, 0, + targetClass.u2interfaces, 0, + interfacesCount); + interfaces = targetClass.u2interfaces; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding interface ["+targetClass.getClassName(interfaceConstantIndex)+"]"); + } + + // Add the interface. + interfaces[targetClass.u2interfacesCount++] = interfaceConstantIndex; + } + + /** + * Removes the given interface. + */ + public void removeInterface(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + int interfaceIndex = findInterfaceIndex(interfaceConstantIndex); + + // Shift the interface entries. + System.arraycopy(interfaces, interfaceIndex+1, + interfaces, interfaceIndex, + interfacesCount - interfaceIndex - 1); + + // Clear the last entry. + interfaces[--targetClass.u2interfacesCount] = 0; + } + + + /** + * Finds the index of the given interface in the target class. + */ + + private int findInterfaceIndex(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + for (int index = 0; index < interfacesCount; index++) + { + if (interfaces[index] == interfaceConstantIndex) + { + return index; + } + } + + return interfacesCount; + } + + + /** + * Adds the given field. + */ + public void addField(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + // Make sure there is enough space for the new field. + if (fields.length <= fieldsCount) + { + targetClass.fields = new ProgramField[fieldsCount+1]; + System.arraycopy(fields, 0, + targetClass.fields, 0, + fieldsCount); + fields = targetClass.fields; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding field ["+field.getName(targetClass)+" "+field.getDescriptor(targetClass)+"]"); + } + + // Add the field. + fields[targetClass.u2fieldsCount++] = field; + } + + + /** + * Removes the given field. Note that removing a field that is still being + * referenced can cause unpredictable effects. + */ + public void removeField(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + int fieldIndex = findFieldIndex(field); + + // Shift the field entries. + System.arraycopy(fields, fieldIndex+1, + fields, fieldIndex, + fieldsCount - fieldIndex - 1); + + // Clear the last entry. + fields[--targetClass.u2fieldsCount] = null; + } + + + /** + * Finds the index of the given field in the target class. + */ + + private int findFieldIndex(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + for (int index = 0; index < fieldsCount; index++) + { + if (fields[index].equals(field)) + { + return index; + } + } + + return fieldsCount; + } + + + /** + * Adds the given method. + */ + public void addMethod(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + // Make sure there is enough space for the new method. + if (methods.length <= methodsCount) + { + targetClass.methods = new ProgramMethod[methodsCount+1]; + System.arraycopy(methods, 0, + targetClass.methods, 0, + methodsCount); + methods = targetClass.methods; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding method ["+method.getName(targetClass)+method.getDescriptor(targetClass)+"]"); + } + + // Add the method. + methods[targetClass.u2methodsCount++] = method; + } + + + /** + * Removes the given method. Note that removing a method that is still being + * referenced can cause unpredictable effects. + */ + public void removeMethod(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + int methodIndex = findMethodIndex(method); + + // Shift the method entries. + System.arraycopy(methods, methodIndex+1, + methods, methodIndex, + methodsCount - methodIndex - 1); + + // Clear the last entry. + methods[--targetClass.u2methodsCount] = null; + } + + + /** + * Finds the index of the given method in the target class. + */ + + private int findMethodIndex(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + for (int index = 0; index < methodsCount; index++) + { + if (methods[index].equals(method)) + { + return index; + } + } + + return methodsCount; + } +} diff --git a/src/proguard/classfile/editor/ClassElementSorter.java b/src/proguard/classfile/editor/ClassElementSorter.java new file mode 100644 index 0000000..3256c88 --- /dev/null +++ b/src/proguard/classfile/editor/ClassElementSorter.java @@ -0,0 +1,52 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.ProgramClass; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor sorts the various elements of the classes that it visits: + * interfaces, constants, fields, methods, and attributes. + * + * @author Eric Lafortune + */ +public class ClassElementSorter +extends SimplifiedVisitor +implements ClassVisitor +{ + private final ClassVisitor interfaceSorter = new InterfaceSorter(); + private final ClassVisitor constantPoolSorter = new ConstantPoolSorter(); +// private ClassVisitor classMemberSorter = new ClassMemberSorter(); + private final ClassVisitor attributeSorter = new AttributeSorter(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.accept(constantPoolSorter); + programClass.accept(interfaceSorter); +// programClass.accept(classMemberSorter); + programClass.accept(attributeSorter); + } +} diff --git a/src/proguard/classfile/editor/ClassMemberSorter.java b/src/proguard/classfile/editor/ClassMemberSorter.java new file mode 100644 index 0000000..f31fcd0 --- /dev/null +++ b/src/proguard/classfile/editor/ClassMemberSorter.java @@ -0,0 +1,69 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.*; + +/** + * This ClassVisitor sorts the class members of the classes that it visits. + * The sorting order is based on the access flags, the names, and the + * descriptors. + * + * @author Eric Lafortune + */ +public class ClassMemberSorter implements ClassVisitor, Comparator +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Sort the fields. + Arrays.sort(programClass.fields, 0, programClass.u2fieldsCount, this); + + // Sort the methods. + Arrays.sort(programClass.methods, 0, programClass.u2methodsCount, this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for Comparator. + + public int compare(Object object1, Object object2) + { + ProgramMember member1 = (ProgramMember)object1; + ProgramMember member2 = (ProgramMember)object2; + + return member1.u2accessFlags < member2.u2accessFlags ? -1 : + member1.u2accessFlags > member2.u2accessFlags ? 1 : + member1.u2nameIndex < member2.u2nameIndex ? -1 : + member1.u2nameIndex > member2.u2nameIndex ? 1 : + member1.u2descriptorIndex < member2.u2descriptorIndex ? -1 : + member1.u2descriptorIndex > member2.u2descriptorIndex ? 1 : + 0; + } +} diff --git a/src/proguard/classfile/editor/ClassReferenceFixer.java b/src/proguard/classfile/editor/ClassReferenceFixer.java new file mode 100644 index 0000000..9857903 --- /dev/null +++ b/src/proguard/classfile/editor/ClassReferenceFixer.java @@ -0,0 +1,546 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +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 fixes references of constant pool entries, fields, + * methods, and attributes to classes whose names have changed. Descriptors + * of member references are not updated yet. + * + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class ClassReferenceFixer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final boolean ensureUniqueMemberNames; + + + /** + * Creates a new ClassReferenceFixer. + * @param ensureUniqueMemberNames specifies whether class members whose + * descriptor changes should get new, unique + * names, in order to avoid naming conflicts + * with similar methods. + */ + public ClassReferenceFixer(boolean ensureUniqueMemberNames) + { + this.ensureUniqueMemberNames = ensureUniqueMemberNames; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Fix the constant pool. + programClass.constantPoolEntriesAccept(this); + + // Fix class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Fix the attributes. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Fix class members. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Has the descriptor changed? + String descriptor = programField.getDescriptor(programClass); + String newDescriptor = newDescriptor(descriptor, + programField.referencedClass); + + if (!descriptor.equals(newDescriptor)) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the descriptor. + programField.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Update the name, if requested. + if (ensureUniqueMemberNames) + { + String name = programField.getName(programClass); + String newName = newUniqueMemberName(name, descriptor); + programField.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + } + } + + // Fix the attributes. + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Has the descriptor changed? + String descriptor = programMethod.getDescriptor(programClass); + String newDescriptor = newDescriptor(descriptor, + programMethod.referencedClasses); + + if (!descriptor.equals(newDescriptor)) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the descriptor. + programMethod.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Update the name, if requested. + if (ensureUniqueMemberNames) + { + String name = programMethod.getName(programClass); + String newName = newUniqueMemberName(name, descriptor); + programMethod.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + } + } + + // Fix the attributes. + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Has the descriptor changed? + String descriptor = libraryField.getDescriptor(libraryClass); + String newDescriptor = newDescriptor(descriptor, + libraryField.referencedClass); + + // Update the descriptor. + libraryField.descriptor = newDescriptor; + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Has the descriptor changed? + String descriptor = libraryMethod.getDescriptor(libraryClass); + String newDescriptor = newDescriptor(descriptor, + libraryMethod.referencedClasses); + + // Update the descriptor. + libraryMethod.descriptor = newDescriptor; + } + + + // 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; + Member referencedMember = stringConstant.referencedMember; + if (referencedClass != null && + referencedMember == null) + { + // Reconstruct the new class name. + String externalClassName = stringConstant.getString(clazz); + String internalClassName = ClassUtil.internalClassName(externalClassName); + String newInternalClassName = newClassName(internalClassName, + referencedClass); + + // Update the String entry if required. + if (!newInternalClassName.equals(internalClassName)) + { + String newExternalClassName = ClassUtil.externalClassName(newInternalClassName); + + // Refer to a new Utf8 entry. + stringConstant.u2stringIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newExternalClassName); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Do we know the referenced class? + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + // Has the class name changed? + String className = classConstant.getName(clazz); + String newClassName = newClassName(className, referencedClass); + if (!className.equals(newClassName)) + { + // Refer to a new Utf8 entry. + classConstant.u2nameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName); + } + } + } + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Fix the inner class names. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Fix the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Fix the types of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Fix the signatures of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Compute the new signature. + String signature = clazz.getString(signatureAttribute.u2signatureIndex); + String newSignature = newDescriptor(signature, + signatureAttribute.referencedClasses); + + if (!signature.equals(newSignature)) + { + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + } + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Fix the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Fix the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Fix the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // Fix the inner class name. + int innerClassIndex = innerClassesInfo.u2innerClassIndex; + int innerNameIndex = innerClassesInfo.u2innerNameIndex; + if (innerClassIndex != 0 && + innerNameIndex != 0) + { + String newInnerName = clazz.getClassName(innerClassIndex); + int index = newInnerName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR); + if (index >= 0) + { + innerClassesInfo.u2innerNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newInnerName.substring(index + 1)); + } + } + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Has the descriptor changed? + String descriptor = clazz.getString(localVariableInfo.u2descriptorIndex); + String newDescriptor = newDescriptor(descriptor, + localVariableInfo.referencedClass); + + if (!descriptor.equals(newDescriptor)) + { + // Refer to a new Utf8 entry. + localVariableInfo.u2descriptorIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor); + } + } + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Has the signature changed? + String signature = clazz.getString(localVariableTypeInfo.u2signatureIndex); + String newSignature = newDescriptor(signature, + localVariableTypeInfo.referencedClasses); + + if (!signature.equals(newSignature)) + { + localVariableTypeInfo.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + } + } + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Compute the new type name. + String typeName = clazz.getString(annotation.u2typeIndex); + String newTypeName = newDescriptor(typeName, + annotation.referencedClasses); + + if (!typeName.equals(newTypeName)) + { + // Refer to a new Utf8 entry. + annotation.u2typeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName); + } + + // Fix the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Compute the new type name. + String typeName = clazz.getString(enumConstantElementValue.u2typeNameIndex); + String newTypeName = newDescriptor(typeName, + enumConstantElementValue.referencedClasses); + + if (!typeName.equals(newTypeName)) + { + // Refer to a new Utf8 entry. + enumConstantElementValue.u2typeNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName); + } + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Compute the new class name. + String className = clazz.getString(classElementValue.u2classInfoIndex); + String newClassName = newDescriptor(className, + classElementValue.referencedClasses); + + if (!className.equals(newClassName)) + { + // Refer to a new Utf8 entry. + classElementValue.u2classInfoIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName); + } + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Fix the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Fix the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + private static String newDescriptor(String descriptor, + Clazz referencedClass) + { + // If there is no referenced class, the descriptor won't change. + if (referencedClass == null) + { + return descriptor; + } + + // Unravel and reconstruct the class element of the descriptor. + DescriptorClassEnumeration descriptorClassEnumeration = + new DescriptorClassEnumeration(descriptor); + + StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); + newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff()); + + // Only if the descriptor contains a class name (e.g. with an array of + // primitive types), the descriptor can change. + if (descriptorClassEnumeration.hasMoreClassNames()) + { + String className = descriptorClassEnumeration.nextClassName(); + String fluff = descriptorClassEnumeration.nextFluff(); + + String newClassName = newClassName(className, + referencedClass); + + newDescriptorBuffer.append(newClassName); + newDescriptorBuffer.append(fluff); + } + + return newDescriptorBuffer.toString(); + } + + + private static String newDescriptor(String descriptor, + Clazz[] referencedClasses) + { + // If there are no referenced classes, the descriptor won't change. + if (referencedClasses == null || + referencedClasses.length == 0) + { + return descriptor; + } + + // Unravel and reconstruct the class elements of the descriptor. + DescriptorClassEnumeration descriptorClassEnumeration = + new DescriptorClassEnumeration(descriptor); + + StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); + newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff()); + + int index = 0; + while (descriptorClassEnumeration.hasMoreClassNames()) + { + String className = descriptorClassEnumeration.nextClassName(); + boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName(); + String fluff = descriptorClassEnumeration.nextFluff(); + + String newClassName = newClassName(className, + referencedClasses[index++]); + + // Strip the outer class name again, if it's an inner class. + if (isInnerClassName) + { + newClassName = + newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1); + } + + newDescriptorBuffer.append(newClassName); + newDescriptorBuffer.append(fluff); + } + + return newDescriptorBuffer.toString(); + } + + + /** + * Returns a unique class member name, based on the given name and descriptor. + */ + private String newUniqueMemberName(String name, String descriptor) + { + return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + ClassConstants.INTERNAL_METHOD_NAME_INIT : + name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + } + + + /** + * Returns the new class name based on the given class name and the new + * name of the given referenced class. Class names of array types + * are handled properly. + */ + private static String newClassName(String className, + Clazz referencedClass) + { + // If there is no referenced class, the class name won't change. + if (referencedClass == null) + { + return className; + } + + // Reconstruct the class name. + String newClassName = referencedClass.getName(); + + // Is it an array type? + if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + // Add the array prefixes and suffix "[L...;". + newClassName = + className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) + + newClassName + + ClassConstants.INTERNAL_TYPE_CLASS_END; + } + + return newClassName; + } +} diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java new file mode 100644 index 0000000..e783203 --- /dev/null +++ b/src/proguard/classfile/editor/CodeAttributeComposer.java @@ -0,0 +1,845 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor accumulates instructions and exceptions, and then + * copies them into code attributes that it visits. + * + * @author Eric Lafortune + */ +public class CodeAttributeComposer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = true; + //*/ + + + private static final int MAXIMUM_LEVELS = 32; + private static final int INVALID = -1; + + + private boolean allowExternalExceptionHandlers; + + private int maximumCodeLength; + private int codeLength; + private int exceptionTableLength; + private int level = -1; + + private byte[] code = new byte[ClassConstants.TYPICAL_CODE_LENGTH]; + private int[] oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; + + private final int[] codeFragmentOffsets = new int[MAXIMUM_LEVELS]; + private final int[] codeFragmentLengths = new int[MAXIMUM_LEVELS]; + private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH + 1]; + + private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH]; + + private int expectedStackMapFrameOffset; + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); +// private final InstructionWriter instructionWriter = new InstructionWriter(); + + + /** + * Creates a new CodeAttributeComposer that doesn't allow external exception + * handlers. + */ + public CodeAttributeComposer() + { + this(false); + } + + + /** + * Creates a new CodeAttributeComposer that optionally allows external + * exception handlers. + */ + public CodeAttributeComposer(boolean allowExternalExceptionHandlers) + { + this.allowExternalExceptionHandlers = allowExternalExceptionHandlers; + } + + + /** + * Starts a new code definition. + */ + public void reset() + { + maximumCodeLength = 0; + codeLength = 0; + exceptionTableLength = 0; + level = -1; + } + + + /** + * Starts a new code fragment. Branch instructions that are added are + * assumed to be relative within such code fragments. + * @param maximumCodeFragmentLength the maximum length of the code that will + * be added as part of this fragment. + */ + public void beginCodeFragment(int maximumCodeFragmentLength) + { + level++; + + if (level >= MAXIMUM_LEVELS) + { + throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]"); + } + +// // TODO: Figure out some length. +// if (level == 0) +// { +// // Prepare for possible widening of instructions. +// instructionWriter.reset(2 * maximumCodeFragmentLength); +// } + + // Make sure there is sufficient space for adding the code fragment. + maximumCodeLength += maximumCodeFragmentLength; + + ensureCodeLength(maximumCodeLength); + + // Try to reuse the previous array for this code fragment. + if (instructionOffsetMap[level].length <= maximumCodeFragmentLength) + { + instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1]; + } + + // Initialize the offset map. + for (int index = 0; index <= maximumCodeFragmentLength; index++) + { + instructionOffsetMap[level][index] = INVALID; + } + + // Remember the location of the code fragment. + codeFragmentOffsets[level] = codeLength; + codeFragmentLengths[level] = maximumCodeFragmentLength; + } + + + /** + * Appends the given instruction with the given old offset. + * @param oldInstructionOffset the old offset of the instruction, to which + * branches and other references in the current + * code fragment are pointing. + * @param instruction the instruction to be appended. + */ + public void appendInstruction(int oldInstructionOffset, + Instruction instruction) + { + if (DEBUG) + { + println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset)); + } + + // Make sure the code array is large enough. + int newCodeLength = codeLength + instruction.length(codeLength); + + ensureCodeLength(newCodeLength); + + // Remember the old offset of the appended instruction. + oldInstructionOffsets[codeLength] = oldInstructionOffset; + + // Write the instruction. +// instruction.accept(null, +// null, +// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), +// codeLength, +// instructionWriter); + instruction.write(code, codeLength); + + // Fill out the new offset of the appended instruction. + instructionOffsetMap[level][oldInstructionOffset] = codeLength; + + // Continue appending at the next instruction offset. + codeLength = newCodeLength; + } + + + /** + * Appends the given label with the given old offset. + * @param oldInstructionOffset the old offset of the label, to which + * branches and other references in the current + * code fragment are pointing. + */ + public void appendLabel(int oldInstructionOffset) + { + if (DEBUG) + { + println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)"); + } + + // Fill out the new offset of the appended instruction. + instructionOffsetMap[level][oldInstructionOffset] = codeLength; + } + + + /** + * Appends the given exception to the exception table. + * @param exceptionInfo the exception to be appended. + */ + public void appendException(ExceptionInfo exceptionInfo) + { + if (DEBUG) + { + print(" ", "Exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); + } + + // Remap the exception right away. + visitExceptionInfo(null, null, null, exceptionInfo); + + if (DEBUG) + { + System.out.println(" -> ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); + } + + // Don't add the exception if its instruction range is empty. + if (exceptionInfo.u2startPC == exceptionInfo.u2endPC) + { + if (DEBUG) + { + println(" ", " (not added because of empty instruction range)"); + } + + return; + } + + // Make sure there is sufficient space in the exception table. + if (exceptionTable.length <= exceptionTableLength) + { + ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1]; + System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength); + exceptionTable = newExceptionTable; + } + + // Add the exception. + exceptionTable[exceptionTableLength++] = exceptionInfo; + } + + + /** + * Wraps up the current code fragment, continuing with the previous one on + * the stack. + */ + public void endCodeFragment() + { + if (level < 0) + { + throw new IllegalArgumentException("Code fragment not begun ["+level+"]"); + } + + // Remap the instructions of the code fragment. + int instructionOffset = codeFragmentOffsets[level]; + while (instructionOffset < codeLength) + { + // Get the next instruction. + Instruction instruction = InstructionFactory.create(code, instructionOffset); + + // Does this instruction still have to be remapped? + if (oldInstructionOffsets[instructionOffset] >= 0) + { + // Adapt the instruction for its new offset. + instruction.accept(null, null, null, instructionOffset, this); + + // Write the instruction back. +// instruction.accept(null, +// null, +// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), +// instructionOffset, +// instructionWriter); + instruction.write(code, instructionOffset); + + // Don't remap this instruction again. + oldInstructionOffsets[instructionOffset] = -1; + } + + // Continue remapping at the next instruction offset. + instructionOffset += instruction.length(instructionOffset); + } + + // Correct the estimated maximum code length, now that we know the + // actual length of this code fragment. + maximumCodeLength += codeLength - codeFragmentOffsets[level] - + codeFragmentLengths[level]; + + // Try to remap the exception handlers that couldn't be remapped before. + if (allowExternalExceptionHandlers) + { + for (int index = 0; index < exceptionTableLength; index++) + { + ExceptionInfo exceptionInfo = exceptionTable[index]; + + // Unmapped exception handlers are still negated. + int handlerPC = -exceptionInfo.u2handlerPC; + if (handlerPC > 0) + { + if (remappableInstructionOffset(handlerPC)) + { + exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC); + } + else if (level == 0) + { + throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]"); + } + } + } + } + + level--; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("CodeAttributeComposer: putting results in ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + + if (level != -1) + { + throw new IllegalArgumentException("Code fragment not ended ["+level+"]"); + } + + level++; + + // Make sure the code attribute has sufficient space for the composed + // code. + if (codeAttribute.u4codeLength < codeLength) + { + codeAttribute.code = new byte[codeLength]; + } + + // Copy the composed code over into the code attribute. + codeAttribute.u4codeLength = codeLength; + System.arraycopy(code, 0, codeAttribute.code, 0, codeLength); + + // Remove exceptions with empty code blocks (done before). + //exceptionTableLength = + // removeEmptyExceptions(exceptionTable, exceptionTableLength); + + // Make sure the exception table has sufficient space for the composed + // exceptions. + if (codeAttribute.exceptionTable.length < exceptionTableLength) + { + codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength]; + } + + // Copy the exception table. + codeAttribute.u2exceptionTableLength = exceptionTableLength; + System.arraycopy(exceptionTable, 0, codeAttribute.exceptionTable, 0, exceptionTableLength); + + // Update the maximum stack size and local variable frame size. + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + + // Remap the line number table and the local variable table. + codeAttribute.attributesAccept(clazz, method, this); + + // Remap the exception table. + //codeAttribute.exceptionsAccept(clazz, method, this); + + // Remove exceptions with empty code blocks (done before). + //codeAttribute.u2exceptionTableLength = + // removeEmptyExceptions(codeAttribute.exceptionTable, + // codeAttribute.u2exceptionTableLength); + +// // Make sure instructions are widened if necessary. +// instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); + + level--; + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // Remap all stack map entries. + expectedStackMapFrameOffset = -1; + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // Remap all stack map table entries. + expectedStackMapFrameOffset = 0; + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Remap all line number table entries. + lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); + + // Remove line numbers with empty code blocks. + lineNumberTableAttribute.u2lineNumberTableLength = + removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, + lineNumberTableAttribute.u2lineNumberTableLength, + codeAttribute.u4codeLength); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Remap all local variable table entries. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables with empty code blocks. + localVariableTableAttribute.u2localVariableTableLength = + removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength, + codeAttribute.u2maxLocals); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Remap all local variable table entries. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables with empty code blocks. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength, + codeAttribute.u2maxLocals); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + // Adjust the branch offset. + branchInstruction.branchOffset = remapBranchOffset(offset, + branchInstruction.branchOffset); + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Adjust the default jump offset. + switchInstruction.defaultOffset = remapBranchOffset(offset, + switchInstruction.defaultOffset); + + // Adjust the jump offsets. + remapJumpOffsets(offset, + switchInstruction.jumpOffsets); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Remap the code offsets. Note that the instruction offset map also has + // an entry for the first offset after the code, for u2endPC. + exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC); + exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC); + + // See if we can remap the handler right away. Unmapped exception + // handlers are negated, in order to mark them as external. + int handlerPC = exceptionInfo.u2handlerPC; + exceptionInfo.u2handlerPC = + !allowExternalExceptionHandlers || + remappableInstructionOffset(handlerPC) ? + remapInstructionOffset(handlerPC) : + -handlerPC; + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + // Remap the stack map frame offset. + int stackMapFrameOffset = remapInstructionOffset(offset); + + int offsetDelta = stackMapFrameOffset; + + // Compute the offset delta if the frame is part of a stack map frame + // table (for JDK 6.0) instead of a stack map (for Java Micro Edition). + if (expectedStackMapFrameOffset >= 0) + { + offsetDelta -= expectedStackMapFrameOffset; + + expectedStackMapFrameOffset = stackMapFrameOffset + 1; + } + + stackMapFrame.u2offsetDelta = offsetDelta; + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); + + // Remap the verification type offset. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); + + // Remap the verification type offsets. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); + + // Remap the verification type offsets. + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + // Remap the offset of the 'new' instruction. + uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Remap the code offset. + lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Remap the code offset and length. + // TODO: The local variable frame might not be strictly preserved. + int startPC = remapInstructionOffset(localVariableInfo.u2startPC); + int endPC = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length); + + localVariableInfo.u2startPC = startPC; + localVariableInfo.u2length = endPC - startPC; + } + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Remap the code offset and length. + // TODO: The local variable frame might not be strictly preserved. + int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC); + int endPC = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length); + + localVariableTypeInfo.u2startPC = startPC; + localVariableTypeInfo.u2length = endPC - startPC; + } + + + // Small utility methods. + + /** + * Make sure the code arrays have at least the given size. + */ + private void ensureCodeLength(int newCodeLength) + { + if (code.length < newCodeLength) + { + // Add 20% to avoid extending the arrays too often. + newCodeLength = newCodeLength * 6 / 5; + + byte[] newCode = new byte[newCodeLength]; + System.arraycopy(code, 0, newCode, 0, codeLength); + code = newCode; + + int[] newOldInstructionOffsets = new int[newCodeLength]; + System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength); + oldInstructionOffsets = newOldInstructionOffsets; + } + } + + + /** + * Adjusts the given jump offsets for the instruction at the given offset. + */ + private void remapJumpOffsets(int offset, int[] jumpOffsets) + { + for (int index = 0; index < jumpOffsets.length; index++) + { + jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]); + } + } + + + /** + * Computes the new branch offset for the instruction at the given new offset + * with the given old branch offset. + */ + private int remapBranchOffset(int newInstructionOffset, int branchOffset) + { + if (newInstructionOffset < 0 || + newInstructionOffset > codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]"); + } + + int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset]; + + return remapInstructionOffset(oldInstructionOffset + branchOffset) - + remapInstructionOffset(oldInstructionOffset); + } + + + /** + * Computes the new instruction offset for the instruction at the given old + * offset. + */ + private int remapInstructionOffset(int oldInstructionOffset) + { + if (oldInstructionOffset < 0 || + oldInstructionOffset > codeFragmentLengths[level]) + { + throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level); + } + + int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset]; + if (newInstructionOffset == INVALID) + { + throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level); + } + + return newInstructionOffset; + } + + + /** + * Returns whether the given old instruction offset can be remapped at the + */ + private boolean remappableInstructionOffset(int oldInstructionOffset) + { + return + oldInstructionOffset <= codeFragmentLengths[level] && + instructionOffsetMap[level][oldInstructionOffset] > INVALID; + } + + + /** + * Returns the given list of exceptions, without the ones that have empty + * code blocks. + */ + private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, + int exceptionInfoCount) + { + // Overwrite all empty exceptions. + int newIndex = 0; + for (int index = 0; index < exceptionInfoCount; index++) + { + ExceptionInfo exceptionInfo = exceptionInfos[index]; + if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) + { + exceptionInfos[newIndex++] = exceptionInfo; + } + } + + // Clear the unused array entries. + for (int index = newIndex; index < exceptionInfoCount; index++) + { + exceptionInfos[index] = null; + } + + return newIndex; + } + + + /** + * Returns the given list of line numbers, without the ones that have empty + * code blocks or that exceed the code size. + */ + private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, + int lineNumberInfoCount, + int codeLength) + { + // Overwrite all empty line number entries. + int newIndex = 0; + for (int index = 0; index < lineNumberInfoCount; index++) + { + LineNumberInfo lineNumberInfo = lineNumberInfos[index]; + int startPC = lineNumberInfo.u2startPC; + if (startPC < codeLength && + (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) + { + lineNumberInfos[newIndex++] = lineNumberInfo; + } + } + + // Clear the unused array entries. + for (int index = newIndex; index < lineNumberInfoCount; index++) + { + lineNumberInfos[index] = null; + } + + return newIndex; + } + + + /** + * Returns the given list of local variables, without the ones that have empty + * code blocks or that exceed the actual number of local variables. + */ + private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount, + int maxLocals) + { + // Overwrite all empty local variable entries. + int newIndex = 0; + for (int index = 0; index < localVariableInfoCount; index++) + { + LocalVariableInfo localVariableInfo = localVariableInfos[index]; + if (localVariableInfo.u2length > 0 && + localVariableInfo.u2index < maxLocals) + { + localVariableInfos[newIndex++] = localVariableInfo; + } + } + + // Clear the unused array entries. + for (int index = newIndex; index < localVariableInfoCount; index++) + { + localVariableInfos[index] = null; + } + + return newIndex; + } + + + /** + * Returns the given list of local variable types, without the ones that + * have empty code blocks or that exceed the actual number of local variables. + */ + private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount, + int maxLocals) + { + // Overwrite all empty local variable type entries. + int newIndex = 0; + for (int index = 0; index < localVariableTypeInfoCount; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; + if (localVariableTypeInfo.u2length > 0 && + localVariableTypeInfo.u2index < maxLocals) + { + localVariableTypeInfos[newIndex++] = localVariableTypeInfo; + } + } + + // Clear the unused array entries. + for (int index = newIndex; index < localVariableTypeInfoCount; index++) + { + localVariableTypeInfos[index] = null; + } + + return newIndex; + } + + + private void println(String string1, String string2) + { + print(string1, string2); + + System.out.println(); + } + + private void print(String string1, String string2) + { + System.out.print(string1); + + for (int index = 0; index < level; index++) + { + System.out.print(" "); + } + + System.out.print(string2); + } + + + public static void main(String[] args) + { + CodeAttributeComposer composer = new CodeAttributeComposer(); + + composer.beginCodeFragment(4); + composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0)); + composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0)); + composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1)); + + composer.beginCodeFragment(4); + composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1)); + composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0)); + composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5)); + composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3)); + composer.endCodeFragment(); + + composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN)); + composer.endCodeFragment(); + } +} diff --git a/src/proguard/classfile/editor/CodeAttributeEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java new file mode 100644 index 0000000..9658c98 --- /dev/null +++ b/src/proguard/classfile/editor/CodeAttributeEditor.java @@ -0,0 +1,1163 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassPrinter; + +/** + * This AttributeVisitor accumulates specified changes to code, and then applies + * these accumulated changes to the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class CodeAttributeEditor +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + private boolean updateFrameSizes; + + private int codeLength; + private boolean modified; + private boolean simple; + + /*private*/public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; + /*private*/public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; + /*private*/public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; + /*private*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + + private int[] instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH]; + private int newOffset; + private boolean lengthIncreased; + + private int expectedStackMapFrameOffset; + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); + private final InstructionWriter instructionWriter = new InstructionWriter(); + + + public CodeAttributeEditor() + { + this(true); + } + + + public CodeAttributeEditor(boolean updateFrameSizes) + { + this.updateFrameSizes = updateFrameSizes; + } + + + /** + * Resets the accumulated code changes. + * @param codeLength the length of the code that will be edited next. + */ + public void reset(int codeLength) + { + this.codeLength = codeLength; + + // Try to reuse the previous arrays. + if (preInsertions.length < codeLength) + { + preInsertions = new Instruction[codeLength]; + replacements = new Instruction[codeLength]; + postInsertions = new Instruction[codeLength]; + deleted = new boolean[codeLength]; + } + else + { + for (int index = 0; index < codeLength; index++) + { + preInsertions[index] = null; + replacements[index] = null; + postInsertions[index] = null; + deleted[index] = false; + } + } + + modified = false; + simple = true; + + } + + + /** + * Remembers to place the given instruction right before the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instruction the new instruction. + */ + public void insertBeforeInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + preInsertions[instructionOffset] = instruction; + + modified = true; + simple = false; + + } + + + /** + * Remembers to place the given instructions right before the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instructions the new instructions. + */ + public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + preInsertions[instructionOffset] = new CompositeInstruction(instructions); + + modified = true; + simple = false; + + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instruction. + * @param instructionOffset the offset of the instruction to be replaced. + * @param instruction the new instruction. + */ + public void replaceInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + replacements[instructionOffset] = instruction; + + modified = true; + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instructions. + * @param instructionOffset the offset of the instruction to be replaced. + * @param instructions the new instructions. + */ + public void replaceInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + replacements[instructionOffset] = new CompositeInstruction(instructions); + + modified = true; + } + + + /** + * Remembers to place the given instruction right after the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instruction the new instruction. + */ + public void insertAfterInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + postInsertions[instructionOffset] = instruction; + + modified = true; + simple = false; + } + + + /** + * Remembers to place the given instructions right after the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instructions the new instructions. + */ + public void insertAfterInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + postInsertions[instructionOffset] = new CompositeInstruction(instructions); + + modified = true; + simple = false; + } + + + /** + * Remembers to delete the instruction at the given offset. + * @param instructionOffset the offset of the instruction to be deleted. + */ + public void deleteInstruction(int instructionOffset) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + deleted[instructionOffset] = true; + + modified = true; + simple = false; + } + + + /** + * Remembers not to delete the instruction at the given offset. + * @param instructionOffset the offset of the instruction not to be deleted. + */ + public void undeleteInstruction(int instructionOffset) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + deleted[instructionOffset] = false; + } + + + /** + * Returns whether the instruction at the given offset has been modified + * in any way. + */ + public boolean isModified(int instructionOffset) + { + return preInsertions[instructionOffset] != null || + replacements[instructionOffset] != null || + postInsertions[instructionOffset] != null || + deleted[instructionOffset]; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { +// DEBUG = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + // TODO: Remove this when the code has stabilized. + // Catch any unexpected exceptions from the actual visiting method. + try + { + // Process the code. + visitCodeAttribute0(clazz, method, codeAttribute); + } + catch (RuntimeException ex) + { + System.err.println("Unexpected error while editing code:"); + System.err.println(" Class = ["+clazz.getName()+"]"); + System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + + throw ex; + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]"); + } + + // Avoid doing any work if nothing is changing anyway. + if (!modified) + { + return; + } + + // Check if we can perform a faster simple replacement of instructions. + if (canPerformSimpleReplacements(codeAttribute)) + { + // Simply overwrite the instructions. + performSimpleReplacements(codeAttribute); + + // Update the maximum stack size and local variable frame size. + updateFrameSizes(clazz, method, codeAttribute); + } + else + { + // Move and remap the instructions. + codeAttribute.u4codeLength = + updateInstructions(clazz, method, codeAttribute); + + // Remap the exception table. + codeAttribute.exceptionsAccept(clazz, method, this); + + // Remove exceptions with empty code blocks. + codeAttribute.u2exceptionTableLength = + removeEmptyExceptions(codeAttribute.exceptionTable, + codeAttribute.u2exceptionTableLength); + + // Update the maximum stack size and local variable frame size. + updateFrameSizes(clazz, method, codeAttribute); + + // Remap the line number table and the local variable table. + codeAttribute.attributesAccept(clazz, method, this); + + // Make sure instructions are widened if necessary. + instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + private void updateFrameSizes(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (updateFrameSizes) + { + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // Remap all stack map entries. + expectedStackMapFrameOffset = -1; + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // Remap all stack map table entries. + expectedStackMapFrameOffset = 0; + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Remap all line number table entries. + lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); + + // Remove line numbers with empty code blocks. + lineNumberTableAttribute.u2lineNumberTableLength = + removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, + lineNumberTableAttribute.u2lineNumberTableLength, + codeAttribute.u4codeLength); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Remap all local variable table entries. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables with empty code blocks. + localVariableTableAttribute.u2localVariableTableLength = + removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength, + codeAttribute.u2maxLocals); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Remap all local variable table entries. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables with empty code blocks. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength, + codeAttribute.u2maxLocals); + } + + + /** + * Checks if it is possible to modifies the given code without having to + * update any offsets. + * @param codeAttribute the code to be changed. + * @return the new code length. + */ + private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute) + { + if (!simple) + { + return false; + } + + byte[] code = codeAttribute.code; + int codeLength = codeAttribute.u4codeLength; + + // Go over all replacement instructions. + for (int offset = 0; offset < codeLength; offset++) + { + // Check if the replacement instruction, if any, has a different + // length than the original instruction. + Instruction replacementInstruction = replacements[offset]; + if (replacementInstruction != null && + replacementInstruction.length(offset) != + InstructionFactory.create(code, offset).length(offset)) + { + return false; + } + } + + return true; + } + + + /** + * Modifies the given code without updating any offsets. + * @param codeAttribute the code to be changed. + */ + private void performSimpleReplacements(CodeAttribute codeAttribute) + { + int codeLength = codeAttribute.u4codeLength; + + // Go over all replacement instructions. + for (int offset = 0; offset < codeLength; offset++) + { + // Overwrite the original instruction with the replacement + // instruction if any. + Instruction replacementInstruction = replacements[offset]; + if (replacementInstruction != null) + { + replacementInstruction.write(codeAttribute, offset); + + if (DEBUG) + { + System.out.println(" Replaced "+replacementInstruction.toString(newOffset)); + } + } + } + } + + + /** + * Modifies the given code based on the previously specified changes. + * @param clazz the class file of the code to be changed. + * @param method the method of the code to be changed. + * @param codeAttribute the code to be changed. + * @return the new code length. + */ + private int updateInstructions(Clazz clazz, + Method method, + CodeAttribute codeAttribute) + { + byte[] oldCode = codeAttribute.code; + int oldLength = codeAttribute.u4codeLength; + + // Make sure there is a sufficiently large instruction offset map. + if (instructionOffsetMap.length < oldLength + 1) + { + instructionOffsetMap = new int[oldLength + 1]; + } + + // Fill out the instruction offset map. + int newLength = mapInstructions(oldCode, + oldLength); + + // Create a new code array if necessary. + if (lengthIncreased) + { + codeAttribute.code = new byte[newLength]; + } + + // Prepare for possible widening of instructions. + instructionWriter.reset(newLength); + + // Move the instructions into the new code array. + moveInstructions(clazz, + method, + codeAttribute, + oldCode, + oldLength); + + // We can return the new length. + return newLength; + } + + + /** + * Fills out the instruction offset map for the given code block. + * @param oldCode the instructions to be moved. + * @param oldLength the code length. + * @return the new code length. + */ + private int mapInstructions(byte[] oldCode, int oldLength) + { + // Start mapping instructions at the beginning. + newOffset = 0; + lengthIncreased = false; + + int oldOffset = 0; + do + { + // Get the next instruction. + Instruction instruction = InstructionFactory.create(oldCode, oldOffset); + + // Compute the mapping of the instruction. + mapInstruction(oldOffset, instruction); + + oldOffset += instruction.length(oldOffset); + + if (newOffset > oldOffset) + { + lengthIncreased = true; + } + } + while (oldOffset < oldLength); + + // Also add an entry for the first offset after the code. + instructionOffsetMap[oldOffset] = newOffset; + + return newOffset; + } + + + /** + * Fills out the instruction offset map for the given instruction. + * @param oldOffset the instruction's old offset. + * @param instruction the instruction to be moved. + */ + private void mapInstruction(int oldOffset, + Instruction instruction) + { + instructionOffsetMap[oldOffset] = newOffset; + + // Account for the pre-inserted instruction, if any. + Instruction preInstruction = preInsertions[oldOffset]; + if (preInstruction != null) + { + newOffset += preInstruction.length(newOffset); + } + + // Account for the replacement instruction, or for the current + // instruction, if it shouldn't be deleted. + Instruction replacementInstruction = replacements[oldOffset]; + if (replacementInstruction != null) + { + newOffset += replacementInstruction.length(newOffset); + } + else if (!deleted[oldOffset]) + { + // Note that the instruction's length may change at its new offset, + // e.g. if it is a switch instruction. + newOffset += instruction.length(newOffset); + } + + // Account for the post-inserted instruction, if any. + Instruction postInstruction = postInsertions[oldOffset]; + if (postInstruction != null) + { + newOffset += postInstruction.length(newOffset); + } + } + + + /** + * Moves the given code block to the new offsets. + * @param clazz the class file of the code to be changed. + * @param method the method of the code to be changed. + * @param codeAttribute the code to be changed. + * @param oldCode the original code to be moved. + * @param oldLength the original code length. + */ + private void moveInstructions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + byte[] oldCode, + int oldLength) + { + // Start writing instructions at the beginning. + newOffset = 0; + + int oldOffset = 0; + do + { + // Get the next instruction. + Instruction instruction = InstructionFactory.create(oldCode, oldOffset); + + // Move the instruction to its new offset. + moveInstruction(clazz, + method, + codeAttribute, + oldOffset, + instruction); + + oldOffset += instruction.length(oldOffset); + } + while (oldOffset < oldLength); + } + + + /** + * Moves the given instruction to its new offset. + * @param clazz the class file of the code to be changed. + * @param method the method of the code to be changed. + * @param codeAttribute the code to be changed. + * @param oldOffset the original instruction offset. + * @param instruction the original instruction. + */ + private void moveInstruction(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int oldOffset, + Instruction instruction) + { + // Remap and insert the pre-inserted instruction, if any. + Instruction preInstruction = preInsertions[oldOffset]; + if (preInstruction != null) + { + if (DEBUG) + { + System.out.println(" Pre-inserted "+preInstruction.toString(newOffset)); + } + + // Remap the instruction. + preInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + + // Remap and insert the replacement instruction, or the current + // instruction, if it shouldn't be deleted. + Instruction replacementInstruction = replacements[oldOffset]; + if (replacementInstruction != null) + { + if (DEBUG) + { + System.out.println(" Replaced "+replacementInstruction.toString(newOffset)); + } + // Remap the instruction. + replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + else if (!deleted[oldOffset]) + { + if (DEBUG) + { + System.out.println(" Copied "+instruction.toString(newOffset)); + } + + // Remap the instruction. + instruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + + // Remap and insert the post-inserted instruction, if any. + Instruction postInstruction = postInsertions[oldOffset]; + if (postInstruction != null) + { + if (DEBUG) + { + System.out.println(" Post-inserted "+postInstruction.toString(newOffset)); + } + + // Remap the instruction. + postInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + // Write out the instruction. + instructionWriter.visitSimpleInstruction(clazz, + method, + codeAttribute, + newOffset, + simpleInstruction); + + newOffset += simpleInstruction.length(newOffset); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Write out the instruction. + instructionWriter.visitConstantInstruction(clazz, + method, + codeAttribute, + newOffset, + constantInstruction); + + newOffset += constantInstruction.length(newOffset); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Write out the instruction. + instructionWriter.visitVariableInstruction(clazz, + method, + codeAttribute, + newOffset, + variableInstruction); + + newOffset += variableInstruction.length(newOffset); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + // Adjust the branch offset. + branchInstruction.branchOffset = remapBranchOffset(offset, + branchInstruction.branchOffset); + + // Write out the instruction. + instructionWriter.visitBranchInstruction(clazz, + method, + codeAttribute, + newOffset, + branchInstruction); + + newOffset += branchInstruction.length(newOffset); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + // Adjust the default jump offset. + tableSwitchInstruction.defaultOffset = remapBranchOffset(offset, + tableSwitchInstruction.defaultOffset); + + // Adjust the jump offsets. + remapJumpOffsets(offset, + tableSwitchInstruction.jumpOffsets); + + // Write out the instruction. + instructionWriter.visitTableSwitchInstruction(clazz, + method, + codeAttribute, + newOffset, + tableSwitchInstruction); + + newOffset += tableSwitchInstruction.length(newOffset); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + // Adjust the default jump offset. + lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset, + lookUpSwitchInstruction.defaultOffset); + + // Adjust the jump offsets. + remapJumpOffsets(offset, + lookUpSwitchInstruction.jumpOffsets); + + // Write out the instruction. + instructionWriter.visitLookUpSwitchInstruction(clazz, + method, + codeAttribute, + newOffset, + lookUpSwitchInstruction); + + newOffset += lookUpSwitchInstruction.length(newOffset); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Remap the code offsets. Note that the instruction offset map also has + // an entry for the first offset after the code, for u2endPC. + exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC); + exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC); + exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC); + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + // Remap the stack map frame offset. + int stackMapFrameOffset = remapInstructionOffset(offset); + + int offsetDelta = stackMapFrameOffset; + + // Compute the offset delta if the frame is part of a stack map frame + // table (for JDK 6.0) instead of a stack map (for Java Micro Edition). + if (expectedStackMapFrameOffset >= 0) + { + offsetDelta -= expectedStackMapFrameOffset; + + expectedStackMapFrameOffset = stackMapFrameOffset + 1; + } + + stackMapFrame.u2offsetDelta = offsetDelta; + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); + + // Remap the verification type offset. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); + + // Remap the verification type offsets. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Remap the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); + + // Remap the verification type offsets. + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + // Remap the offset of the 'new' instruction. + uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Remap the code offset. + lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Remap the code offset and length. + // TODO: The local variable frame might not be strictly preserved. + localVariableInfo.u2length = remapBranchOffset(localVariableInfo.u2startPC, + localVariableInfo.u2length); + localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Remap the code offset and length. + // TODO: The local variable frame might not be strictly preserved. + localVariableTypeInfo.u2length = remapBranchOffset(localVariableTypeInfo.u2startPC, + localVariableTypeInfo.u2length); + localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC); + } + + + // Small utility methods. + + /** + * Adjusts the given jump offsets for the instruction at the given offset. + */ + private void remapJumpOffsets(int offset, int[] jumpOffsets) + { + for (int index = 0; index < jumpOffsets.length; index++) + { + jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]); + } + } + + + /** + * Computes the new branch offset for the instruction at the given offset + * with the given branch offset. + */ + private int remapBranchOffset(int offset, int branchOffset) + { + return remapInstructionOffset(offset + branchOffset) - newOffset; + } + + + /** + * Computes the new instruction offset for the instruction at the given offset. + */ + private int remapInstructionOffset(int offset) + { + if (offset < 0 || + offset > codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]"); + } + + return instructionOffsetMap[offset]; + } + + + /** + * Returns the given list of exceptions, without the ones that have empty + * code blocks. + */ + private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, + int exceptionInfoCount) + { + // Overwrite all empty exceptions. + int newIndex = 0; + for (int index = 0; index < exceptionInfoCount; index++) + { + ExceptionInfo exceptionInfo = exceptionInfos[index]; + if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) + { + exceptionInfos[newIndex++] = exceptionInfo; + } + } + + return newIndex; + } + + + /** + * Returns the given list of line numbers, without the ones that have empty + * code blocks or that exceed the code size. + */ + private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, + int lineNumberInfoCount, + int codeLength) + { + // Overwrite all empty line number entries. + int newIndex = 0; + for (int index = 0; index < lineNumberInfoCount; index++) + { + LineNumberInfo lineNumberInfo = lineNumberInfos[index]; + int startPC = lineNumberInfo.u2startPC; + if (startPC < codeLength && + (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) + { + lineNumberInfos[newIndex++] = lineNumberInfo; + } + } + + return newIndex; + } + + + /** + * Returns the given list of local variables, without the ones that have empty + * code blocks or that exceed the actual number of local variables. + */ + private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount, + int maxLocals) + { + // Overwrite all empty local variable entries. + int newIndex = 0; + for (int index = 0; index < localVariableInfoCount; index++) + { + LocalVariableInfo localVariableInfo = localVariableInfos[index]; + if (localVariableInfo.u2length > 0 && + localVariableInfo.u2index < maxLocals) + { + localVariableInfos[newIndex++] = localVariableInfo; + } + } + + return newIndex; + } + + + /** + * Returns the given list of local variable types, without the ones that + * have empty code blocks or that exceed the actual number of local variables. + */ + private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount, + int maxLocals) + { + // Overwrite all empty local variable type entries. + int newIndex = 0; + for (int index = 0; index < localVariableTypeInfoCount; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; + if (localVariableTypeInfo.u2length > 0 && + localVariableTypeInfo.u2index < maxLocals) + { + localVariableTypeInfos[newIndex++] = localVariableTypeInfo; + } + } + + return newIndex; + } + + + private class CompositeInstruction + extends Instruction + { + private Instruction[] instructions; + + + private CompositeInstruction(Instruction[] instructions) + { + this.instructions = instructions; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + for (int index = 0; index < instructions.length; index++) + { + instructions[index] = instructions[index].shrink(); + } + + return this; + } + + + public void write(byte[] code, int offset) + { + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + + instruction.write(code, offset); + + offset += instruction.length(offset); + } + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read composite instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write composite instruction"); + } + + + public int length(int offset) + { + int newOffset = offset; + + for (int index = 0; index < instructions.length; index++) + { + newOffset += instructions[index].length(newOffset); + } + + return newOffset - offset; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + if (instructionVisitor != CodeAttributeEditor.this) + { + throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); + } + + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + + instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this); + + offset += instruction.length(offset); + } + } + + + // Implementations for Object. + + public String toString() + { + StringBuffer stringBuffer = new StringBuffer(); + + for (int index = 0; index < instructions.length; index++) + { + stringBuffer.append(instructions[index].toString()).append("; "); + } + + return stringBuffer.toString(); + } + } +} diff --git a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java new file mode 100644 index 0000000..9962ea5 --- /dev/null +++ b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java @@ -0,0 +1,60 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor resets it CodeAttributeEditor whenever it visits a + * code attribute. + * + * @author Eric Lafortune + */ +public class CodeAttributeEditorResetter +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final CodeAttributeEditor codeAttributeEditor; + + + /** + * Creates a new CodeAttributeEditorResetter. + * @param codeAttributeEditor the code attribute editor that will be reset. + */ + public CodeAttributeEditorResetter(CodeAttributeEditor codeAttributeEditor) + { + this.codeAttributeEditor = codeAttributeEditor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttributeEditor.reset(codeAttribute.u4codeLength); + } +} diff --git a/src/proguard/classfile/editor/ComparableConstant.java b/src/proguard/classfile/editor/ComparableConstant.java new file mode 100644 index 0000000..bb81221 --- /dev/null +++ b/src/proguard/classfile/editor/ComparableConstant.java @@ -0,0 +1,200 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + + +/** + * This class is a <code>Comparable</code> wrapper of <code>Constant</code> + * objects. It can store an index, in order to identify the constant pool + * entry after it has been sorted. The comparison is primarily based on the + * types of the constant pool entries, and secondarily on the contents of + * the constant pool entries. + * + * @author Eric Lafortune + */ +class ComparableConstant +extends SimplifiedVisitor +implements Comparable, ConstantVisitor +{ + private static final int[] PRIORITIES = new int[13]; + static + { + PRIORITIES[ClassConstants.CONSTANT_Integer] = 0; // Possibly byte index (ldc). + PRIORITIES[ClassConstants.CONSTANT_Float] = 1; + PRIORITIES[ClassConstants.CONSTANT_String] = 2; + PRIORITIES[ClassConstants.CONSTANT_Class] = 3; + PRIORITIES[ClassConstants.CONSTANT_Long] = 4; // Always wide index (ldc2_w). + PRIORITIES[ClassConstants.CONSTANT_Double] = 5; + PRIORITIES[ClassConstants.CONSTANT_Fieldref] = 6; // Always wide index. + PRIORITIES[ClassConstants.CONSTANT_Methodref] = 7; + PRIORITIES[ClassConstants.CONSTANT_InterfaceMethodref] = 8; + PRIORITIES[ClassConstants.CONSTANT_NameAndType] = 9; + PRIORITIES[ClassConstants.CONSTANT_Utf8] = 10; + } + + private final Clazz clazz; + private final int thisIndex; + private final Constant thisConstant; + + private Constant otherConstant; + private int result; + + + public ComparableConstant(Clazz clazz, int index, Constant constant) + { + this.clazz = clazz; + this.thisIndex = index; + this.thisConstant = constant; + } + + + public int getIndex() + { + return thisIndex; + } + + + public Constant getConstant() + { + return thisConstant; + } + + + // Implementations for Comparable. + + public int compareTo(Object other) + { + ComparableConstant otherComparableConstant = (ComparableConstant)other; + + otherConstant = otherComparableConstant.thisConstant; + + // Compare based on the original indices, if the actual constant pool + // entries are the same. + if (thisConstant == otherConstant) + { + int otherIndex = otherComparableConstant.thisIndex; + + return thisIndex < otherIndex ? -1 : + thisIndex == otherIndex ? 0 : + 1; + } + + // Compare based on the tags, if they are different. + int thisTag = thisConstant.getTag(); + int otherTag = otherConstant.getTag(); + + if (thisTag != otherTag) + { + return PRIORITIES[thisTag] < PRIORITIES[otherTag] ? -1 : 1; + } + + // Otherwise compare based on the contents of the Constant objects. + thisConstant.accept(clazz, this); + + return result; + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + // In JDK 1.4, we can use Integer.compare(a,b). + result = new Integer(integerConstant.getValue()).compareTo(new Integer(((IntegerConstant)otherConstant).getValue())); + } + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + // In JDK 1.4, we can use Long.compare(a,b). + result = new Long(longConstant.getValue()).compareTo(new Long(((LongConstant)otherConstant).getValue())); + } + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + // In JDK 1.4, we can use Float.compare(a,b). + result = new Float(floatConstant.getValue()).compareTo(new Float(((FloatConstant)otherConstant).getValue())); + } + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + // In JDK 1.4, we can use Double.compare(a,b). + result = new Double(doubleConstant.getValue()).compareTo(new Double(((DoubleConstant)otherConstant).getValue())); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + result = stringConstant.getString(clazz).compareTo(((StringConstant)otherConstant).getString(clazz)); + } + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + result = utf8Constant.getString().compareTo(((Utf8Constant)otherConstant).getString()); + } + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + RefConstant otherRefConstant = (RefConstant)otherConstant; + result = (refConstant.getClassName(clazz) + ' ' + + refConstant.getName(clazz) + ' ' + + refConstant.getType(clazz)) + .compareTo + (otherRefConstant.getClassName(clazz) + ' ' + + otherRefConstant.getName(clazz) + ' ' + + otherRefConstant.getType(clazz)); + } + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + result = classConstant.getName(clazz).compareTo(((ClassConstant)otherConstant).getName(clazz)); + } + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + NameAndTypeConstant otherNameAndTypeConstant = (NameAndTypeConstant)otherConstant; + result = (nameAndTypeConstant.getName(clazz) + ' ' + + nameAndTypeConstant.getType(clazz)) + .compareTo + (otherNameAndTypeConstant.getName(clazz) + ' ' + + otherNameAndTypeConstant.getType(clazz)); + } + + + // Implementations for Object. + + public boolean equals(Object other) + { + return other != null && + this.getClass().equals(other.getClass()) && + this.getConstant().getClass().equals(((ComparableConstant)other).getConstant().getClass()) && + this.compareTo(other) == 0; + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } +} diff --git a/src/proguard/classfile/editor/ConstantAdder.java b/src/proguard/classfile/editor/ConstantAdder.java new file mode 100644 index 0000000..2b74f5f --- /dev/null +++ b/src/proguard/classfile/editor/ConstantAdder.java @@ -0,0 +1,194 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This ConstantVisitor adds all constants that it visits to the constant pool + * of a given target class. + * + * @author Eric Lafortune + */ +public class ConstantAdder +implements ConstantVisitor +{ + private final ConstantPoolEditor constantPoolEditor; + + private int constantIndex; + + + /** + * Creates a new ConstantAdder that will copy constants into the given + * target class. + */ + public ConstantAdder(ProgramClass targetClass) + { + constantPoolEditor = new ConstantPoolEditor(targetClass); + } + + + /** + * Adds a copy of the specified constant in the given class and returns + * its index. If the specified index is 0, the returned value is 0 too. + */ + public int addConstant(Clazz clazz, int constantIndex) + { + clazz.constantPoolEntryAccept(constantIndex, this); + + return this.constantIndex; + } + + + /** + * Adds a copy of the given constant in the given class and returns + * its index. + */ + public int addConstant(Clazz clazz, Constant constant) + { + constant.accept(clazz, this); + + return this.constantIndex; + } + + + /** + * Returns the index of the most recently created constant in the constant + * pool of the target class. + */ + public int getConstantIndex() + { + return constantIndex; + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + constantIndex = + constantPoolEditor.addIntegerConstant(integerConstant.getValue()); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + constantIndex = + constantPoolEditor.addLongConstant(longConstant.getValue()); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + constantIndex = + constantPoolEditor.addFloatConstant(floatConstant.getValue()); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + constantIndex = + constantPoolEditor.addDoubleConstant(doubleConstant.getValue()); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + constantIndex = + constantPoolEditor.addStringConstant(stringConstant.getString(clazz), + stringConstant.referencedClass, + stringConstant.referencedMember); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + constantIndex = + constantPoolEditor.addUtf8Constant(utf8Constant.getString()); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // First add the referenced class constant, with its own referenced class. + clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this); + + // Then add the actual field reference constant, with its referenced + // class and class member. + constantIndex = + constantPoolEditor.addFieldrefConstant(constantIndex, + fieldrefConstant.getName(clazz), + fieldrefConstant.getType(clazz), + fieldrefConstant.referencedClass, + fieldrefConstant.referencedMember); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + // First add the referenced class constant, with its own referenced class. + clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this); + + // Then add the actual interface method reference constant, with its + // referenced class and class member. + constantIndex = + constantPoolEditor.addInterfaceMethodrefConstant(constantIndex, + interfaceMethodrefConstant.getName(clazz), + interfaceMethodrefConstant.getType(clazz), + interfaceMethodrefConstant.referencedClass, + interfaceMethodrefConstant.referencedMember); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // First add the referenced class constant, with its own referenced class. + clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this); + + // Then add the actual method reference constant, with its referenced + // class and class member. + constantIndex = + constantPoolEditor.addMethodrefConstant(constantIndex, + methodrefConstant.getName(clazz), + methodrefConstant.getType(clazz), + methodrefConstant.referencedClass, + methodrefConstant.referencedMember); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Add the class constant, with its referenced class.. + constantIndex = + constantPoolEditor.addClassConstant(classConstant.getName(clazz), + classConstant.referencedClass); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + constantIndex = + constantPoolEditor.addNameAndTypeConstant(nameAndTypeConstant.getName(clazz), + nameAndTypeConstant.getType(clazz)); + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/src/proguard/classfile/editor/ConstantPoolEditor.java new file mode 100644 index 0000000..8663dee --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolEditor.java @@ -0,0 +1,665 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.constant.*; + +/** + * This class can add constant pool entries to a given class. + * + * @author Eric Lafortune + */ +public class ConstantPoolEditor +{ + private static final boolean DEBUG = false; + + private ProgramClass targetClass; + + + /** + * Creates a new ConstantPoolEditor that will edit constants in the given + * target class. + */ + public ConstantPoolEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Finds or creates a IntegerConstant constant pool entry with the given + * value. + * @return the constant pool index of the Utf8Constant. + */ + public int addIntegerConstant(int value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Integer) + { + IntegerConstant integerConstant = (IntegerConstant)constant; + if (integerConstant.getValue() == value) + { + return index; + } + } + } + + return addConstant(new IntegerConstant(value)); + } + + + /** + * Finds or creates a LongConstant constant pool entry with the given value. + * @return the constant pool index of the LongConstant. + */ + public int addLongConstant(long value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Long) + { + LongConstant longConstant = (LongConstant)constant; + if (longConstant.getValue() == value) + { + return index; + } + } + } + + return addConstant(new LongConstant(value)); + } + + + /** + * Finds or creates a FloatConstant constant pool entry with the given + * value. + * @return the constant pool index of the FloatConstant. + */ + public int addFloatConstant(float value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Float) + { + FloatConstant floatConstant = (FloatConstant)constant; + if (floatConstant.getValue() == value) + { + return index; + } + } + } + + return addConstant(new FloatConstant(value)); + } + + + /** + * Finds or creates a DoubleConstant constant pool entry with the given + * value. + * @return the constant pool index of the DoubleConstant. + */ + public int addDoubleConstant(double value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Double) + { + DoubleConstant doubleConstant = (DoubleConstant)constant; + if (doubleConstant.getValue() == value) + { + return index; + } + } + } + + return addConstant(new DoubleConstant(value)); + } + + + /** + * Finds or creates a StringConstant constant pool entry with the given + * value. + * @return the constant pool index of the StringConstant. + */ + public int addStringConstant(String string, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_String) + { + StringConstant stringConstant = (StringConstant)constant; + if (stringConstant.getString(targetClass).equals(string)) + { + return index; + } + } + } + + return addConstant(new StringConstant(addUtf8Constant(string), + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry for the given + * class and field. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class name, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class name, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class constant pool entry index, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class constant pool entry index and name and type constant pool entry + * index. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Fieldref) + { + FieldrefConstant fieldrefConstant = (FieldrefConstant)constant; + if (fieldrefConstant.u2classIndex == classIndex && + fieldrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new FieldrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class name, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class name, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry for the + * given class and method. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class constant pool entry index, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class constant pool entry index and name and type constant pool + * entry index. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref) + { + InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant; + if (methodrefConstant.u2classIndex == classIndex && + methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new InterfaceMethodrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry for the given + * class and method. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class name, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class name, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class constant pool entry index, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class constant pool entry index and name and type constant pool entry + * index. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Methodref) + { + MethodrefConstant methodrefConstant = (MethodrefConstant)constant; + if (methodrefConstant.u2classIndex == classIndex && + methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new MethodrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a ClassConstant constant pool entry for the given class. + * @return the constant pool index of the ClassConstant. + */ + public int addClassConstant(Clazz referencedClass) + { + return addClassConstant(referencedClass.getName(), + referencedClass); + } + + + /** + * Finds or creates a ClassConstant constant pool entry with the given name. + * @return the constant pool index of the ClassConstant. + */ + public int addClassConstant(String name, + Clazz referencedClass) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Class) + { + ClassConstant classConstant = (ClassConstant)constant; + if (classConstant.getName(targetClass).equals(name)) + { + return index; + } + } + } + + int nameIndex = addUtf8Constant(name); + + return addConstant(new ClassConstant(nameIndex, referencedClass)); + } + + + /** + * Finds or creates a NameAndTypeConstant constant pool entry with the given + * name and type. + * @return the constant pool index of the NameAndTypeConstant. + */ + public int addNameAndTypeConstant(String name, + String type) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_NameAndType) + { + NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant; + if (nameAndTypeConstant.getName(targetClass).equals(name) && + nameAndTypeConstant.getType(targetClass).equals(type)) + { + return index; + } + } + } + + return addConstant(new NameAndTypeConstant(addUtf8Constant(name), + addUtf8Constant(type))); + } + + + /** + * Finds or creates a Utf8Constant constant pool entry for the given string. + * @return the constant pool index of the Utf8Constant. + */ + public int addUtf8Constant(String string) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Check if the entry already exists. + for (int index = 1; index < constantPoolCount; index++) + { + Constant constant = constantPool[index]; + + if (constant != null && + constant.getTag() == ClassConstants.CONSTANT_Utf8) + { + Utf8Constant utf8Constant = (Utf8Constant)constant; + if (utf8Constant.getString().equals(string)) + { + return index; + } + } + } + + return addConstant(new Utf8Constant(string)); + } + + + /** + * Adds a given constant pool entry to the end of the constant pool/ + * @return the constant pool index for the added entry. + */ + public int addConstant(Constant constant) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Make sure there is enough space for another constant pool entry. + if (constantPool.length < constantPoolCount+2) + { + targetClass.constantPool = new Constant[constantPoolCount+2]; + System.arraycopy(constantPool, 0, + targetClass.constantPool, 0, + constantPoolCount); + constantPool = targetClass.constantPool; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount); + } + + // Create a new Utf8Constant for the given string. + constantPool[targetClass.u2constantPoolCount++] = constant; + + // Long constants and double constants take up two entries in the + // constant pool. + int tag = constant.getTag(); + if (tag == ClassConstants.CONSTANT_Long || + tag == ClassConstants.CONSTANT_Double) + { + constantPool[targetClass.u2constantPoolCount++] = null; + } + + return constantPoolCount; + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/src/proguard/classfile/editor/ConstantPoolRemapper.java new file mode 100644 index 0000000..7430d3d --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolRemapper.java @@ -0,0 +1,617 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor remaps all possible references to constant pool entries + * of the classes that it visits, based on a given index map. It is assumed that + * the constant pool entries themselves have already been remapped. + * + * @author Eric Lafortune + */ +public class ConstantPoolRemapper +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + InstructionVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + private int[] constantIndexMap; + + + /** + * Sets the given mapping of old constant pool entry indexes to their new + * indexes. + */ + public void setConstantIndexMap(int[] constantIndexMap) + { + this.constantIndexMap = constantIndexMap; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Remap the local constant pool references. + programClass.u2thisClass = remapConstantIndex(programClass.u2thisClass); + programClass.u2superClass = remapConstantIndex(programClass.u2superClass); + + remapConstantIndexArray(programClass.u2interfaces, + programClass.u2interfacesCount); + + // Remap the references of the contant pool entries themselves. + programClass.constantPoolEntriesAccept(this); + + // Remap the references in all fields, methods, and attributes. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.u2nameIndex = + remapConstantIndex(classConstant.u2nameIndex); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + // Nothing to do. + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + fieldrefConstant.u2classIndex = + remapConstantIndex(fieldrefConstant.u2classIndex); + fieldrefConstant.u2nameAndTypeIndex = + remapConstantIndex(fieldrefConstant.u2nameAndTypeIndex); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + // Nothing to do. + } + + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + // Nothing to do. + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + interfaceMethodrefConstant.u2classIndex = + remapConstantIndex(interfaceMethodrefConstant.u2classIndex); + interfaceMethodrefConstant.u2nameAndTypeIndex = + remapConstantIndex(interfaceMethodrefConstant.u2nameAndTypeIndex); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + // Nothing to do. + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + methodrefConstant.u2classIndex = + remapConstantIndex(methodrefConstant.u2classIndex); + methodrefConstant.u2nameAndTypeIndex = + remapConstantIndex(methodrefConstant.u2nameAndTypeIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + nameAndTypeConstant.u2nameIndex = + remapConstantIndex(nameAndTypeConstant.u2nameIndex); + nameAndTypeConstant.u2descriptorIndex = + remapConstantIndex(nameAndTypeConstant.u2descriptorIndex); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + stringConstant.u2stringIndex = + remapConstantIndex(stringConstant.u2stringIndex); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + // Nothing to do. + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + visitMember(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + visitMember(programClass, programMethod); + } + + + private void visitMember(ProgramClass programClass, ProgramMember programMember) + { + // Remap the local constant pool references. + programMember.u2nameIndex = + remapConstantIndex(programMember.u2nameIndex); + programMember.u2descriptorIndex = + remapConstantIndex(programMember.u2descriptorIndex); + + // Remap the constant pool references of the remaining attributes. + programMember.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Library classes are left unchanged. + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Library classes are left unchanged. + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + unknownAttribute.u2attributeNameIndex = + remapConstantIndex(unknownAttribute.u2attributeNameIndex); + + // There's not much else we can do with unknown attributes. + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + sourceFileAttribute.u2attributeNameIndex = + remapConstantIndex(sourceFileAttribute.u2attributeNameIndex); + sourceFileAttribute.u2sourceFileIndex = + remapConstantIndex(sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + sourceDirAttribute.u2attributeNameIndex = + remapConstantIndex(sourceDirAttribute.u2attributeNameIndex); + sourceDirAttribute.u2sourceDirIndex = + remapConstantIndex(sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + innerClassesAttribute.u2attributeNameIndex = + remapConstantIndex(innerClassesAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the inner classes. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + enclosingMethodAttribute.u2attributeNameIndex = + remapConstantIndex(enclosingMethodAttribute.u2attributeNameIndex); + enclosingMethodAttribute.u2classIndex = + remapConstantIndex(enclosingMethodAttribute.u2classIndex); + enclosingMethodAttribute.u2nameAndTypeIndex = + remapConstantIndex(enclosingMethodAttribute.u2nameAndTypeIndex); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + deprecatedAttribute.u2attributeNameIndex = + remapConstantIndex(deprecatedAttribute.u2attributeNameIndex); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + syntheticAttribute.u2attributeNameIndex = + remapConstantIndex(syntheticAttribute.u2attributeNameIndex); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + signatureAttribute.u2attributeNameIndex = + remapConstantIndex(signatureAttribute.u2attributeNameIndex); + signatureAttribute.u2signatureIndex = + remapConstantIndex(signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + constantValueAttribute.u2attributeNameIndex = + remapConstantIndex(constantValueAttribute.u2attributeNameIndex); + constantValueAttribute.u2constantValueIndex = + remapConstantIndex(constantValueAttribute.u2constantValueIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + exceptionsAttribute.u2attributeNameIndex = + remapConstantIndex(exceptionsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the exceptions. + remapConstantIndexArray(exceptionsAttribute.u2exceptionIndexTable, + exceptionsAttribute.u2exceptionIndexTableLength); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.u2attributeNameIndex = + remapConstantIndex(codeAttribute.u2attributeNameIndex); + + // Initially, the code attribute editor doesn't contain any changes. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Remap the constant pool references of the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Apply the code atribute editor. It will only contain any changes if + // the code length is changing at any point. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + // Remap the constant pool references of the exceptions and attributes. + codeAttribute.exceptionsAccept(clazz, method, this); + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + stackMapAttribute.u2attributeNameIndex = + remapConstantIndex(stackMapAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the stack map frames. + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + stackMapTableAttribute.u2attributeNameIndex = + remapConstantIndex(stackMapTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the stack map frames. + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + lineNumberTableAttribute.u2attributeNameIndex = + remapConstantIndex(lineNumberTableAttribute.u2attributeNameIndex); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + localVariableTableAttribute.u2attributeNameIndex = + remapConstantIndex(localVariableTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + localVariableTypeTableAttribute.u2attributeNameIndex = + remapConstantIndex(localVariableTypeTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + annotationsAttribute.u2attributeNameIndex = + remapConstantIndex(annotationsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + parameterAnnotationsAttribute.u2attributeNameIndex = + remapConstantIndex(parameterAnnotationsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + annotationDefaultAttribute.u2attributeNameIndex = + remapConstantIndex(annotationDefaultAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + if (innerClassesInfo.u2innerClassIndex != 0) + { + innerClassesInfo.u2innerClassIndex = + remapConstantIndex(innerClassesInfo.u2innerClassIndex); + } + + if (innerClassesInfo.u2outerClassIndex != 0) + { + innerClassesInfo.u2outerClassIndex = + remapConstantIndex(innerClassesInfo.u2outerClassIndex); + } + + if (innerClassesInfo.u2innerNameIndex != 0) + { + innerClassesInfo.u2innerNameIndex = + remapConstantIndex(innerClassesInfo.u2innerNameIndex); + } + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (exceptionInfo.u2catchType != 0) + { + exceptionInfo.u2catchType = + remapConstantIndex(exceptionInfo.u2catchType); + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Is the new constant pool index different from the original one? + int newConstantIndex = remapConstantIndex(constantInstruction.constantIndex); + if (newConstantIndex != constantInstruction.constantIndex) + { + // Replace the instruction. + Instruction replacementInstruction = + new ConstantInstruction(constantInstruction.opcode, + newConstantIndex, + constantInstruction.constant).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) {} + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + // Remap the constant pool references of the verification types. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Remap the constant pool references of the verification types. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Remap the constant pool references of the verification types. + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + objectType.u2classIndex = + remapConstantIndex(objectType.u2classIndex); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.u2nameIndex = + remapConstantIndex(localVariableInfo.u2nameIndex); + localVariableInfo.u2descriptorIndex = + remapConstantIndex(localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.u2nameIndex = + remapConstantIndex(localVariableTypeInfo.u2nameIndex); + localVariableTypeInfo.u2signatureIndex = + remapConstantIndex(localVariableTypeInfo.u2signatureIndex); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.u2typeIndex = + remapConstantIndex(annotation.u2typeIndex); + + // Remap the constant pool references of the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + constantElementValue.u2elementNameIndex = + remapConstantIndex(constantElementValue.u2elementNameIndex); + constantElementValue.u2constantValueIndex = + remapConstantIndex(constantElementValue.u2constantValueIndex); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + enumConstantElementValue.u2elementNameIndex = + remapConstantIndex(enumConstantElementValue.u2elementNameIndex); + enumConstantElementValue.u2typeNameIndex = + remapConstantIndex(enumConstantElementValue.u2typeNameIndex); + enumConstantElementValue.u2constantNameIndex = + remapConstantIndex(enumConstantElementValue.u2constantNameIndex); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + classElementValue.u2elementNameIndex = + remapConstantIndex(classElementValue.u2elementNameIndex); + classElementValue.u2classInfoIndex = + remapConstantIndex(classElementValue.u2classInfoIndex); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + annotationElementValue.u2elementNameIndex = + remapConstantIndex(annotationElementValue.u2elementNameIndex); + + // Remap the constant pool references of the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + arrayElementValue.u2elementNameIndex = + remapConstantIndex(arrayElementValue.u2elementNameIndex); + + // Remap the constant pool references of the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Remaps all constant pool indices in the given array. + */ + private void remapConstantIndexArray(int[] array, int length) + { + for (int index = 0; index < length; index++) + { + array[index] = remapConstantIndex(array[index]); + } + } + + + /** + * Returns the new constant pool index of the entry at the + * given index. + */ + private int remapConstantIndex(int constantIndex) + { + return constantIndexMap[constantIndex]; + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/src/proguard/classfile/editor/ConstantPoolSorter.java new file mode 100644 index 0000000..faae318 --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolSorter.java @@ -0,0 +1,126 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Arrays; + +/** + * This ClassVisitor sorts the constant pool entries of the program classes + * that it visits. The sorting order is based on the types of the constant pool + * entries in the first place, and on their contents in the second place. + * + * @author Eric Lafortune + */ +public class ConstantPoolSorter +extends SimplifiedVisitor +implements ClassVisitor +{ + private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private ComparableConstant[] comparableConstantPool = new ComparableConstant[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private Constant[] newConstantPool = new Constant[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + int constantPoolCount = programClass.u2constantPoolCount; + + // Sort the constant pool and set up an index map. + if (constantIndexMap.length < constantPoolCount) + { + constantIndexMap = new int[constantPoolCount]; + comparableConstantPool = new ComparableConstant[constantPoolCount]; + newConstantPool = new Constant[constantPoolCount]; + } + + // Initialize an array whose elements can be compared. + int sortLength = 0; + for (int oldIndex = 1; oldIndex < constantPoolCount; oldIndex++) + { + Constant constant = programClass.constantPool[oldIndex]; + if (constant != null) + { + comparableConstantPool[sortLength++] = + new ComparableConstant(programClass, oldIndex, constant); + } + } + + // Sort the array. + Arrays.sort(comparableConstantPool, 0, sortLength); + + // Save the sorted elements. + int newLength = 1; + int newIndex = 1; + ComparableConstant previousComparableConstant = null; + for (int sortIndex = 0; sortIndex < sortLength; sortIndex++) + { + ComparableConstant comparableConstant = comparableConstantPool[sortIndex]; + + // Isn't this a duplicate of the previous constant? + if (!comparableConstant.equals(previousComparableConstant)) + { + // Remember the index of the new entry. + newIndex = newLength; + + // Copy the sorted constant pool entry over to the constant pool. + Constant constant = comparableConstant.getConstant(); + + newConstantPool[newLength++] = constant; + + // Long entries take up two slots, the second of which is null. + int tag = constant.getTag(); + if (tag == ClassConstants.CONSTANT_Long || + tag == ClassConstants.CONSTANT_Double) + { + newConstantPool[newLength++] = null; + } + + previousComparableConstant = comparableConstant; + } + + // Fill out the map array. + constantIndexMap[comparableConstant.getIndex()] = newIndex; + } + + // Copy the new constant pool over. + System.arraycopy(newConstantPool, 0, programClass.constantPool, 0, newLength); + + // Clear any remaining entries. + for (int index = newLength; index < constantPoolCount; index++) + { + programClass.constantPool[index] = null; + } + + programClass.u2constantPoolCount = newLength; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } +} diff --git a/src/proguard/classfile/editor/ElementValueAdder.java b/src/proguard/classfile/editor/ElementValueAdder.java new file mode 100644 index 0000000..8cbd11d --- /dev/null +++ b/src/proguard/classfile/editor/ElementValueAdder.java @@ -0,0 +1,217 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; + +/** + * This AnnotationVisitor adds all element values that it visits to the given + * target annotation default attribute, annotation, or element value. + * + * @author Eric Lafortune + */ +public class ElementValueAdder +implements ElementValueVisitor +{ + private static final ElementValue[] EMPTY_ELEMENT_VALUES = new ElementValue[0]; + + + private final ProgramClass targetClass; + private final AnnotationDefaultAttribute targetAnnotationDefaultAttribute; + + private final ConstantAdder constantAdder; + private final ElementValuesEditor elementValuesEditor; + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target annotation default attribute value. + */ + public ElementValueAdder(ProgramClass targetClass, + AnnotationDefaultAttribute targetAnnotationDefaultAttribute, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = targetAnnotationDefaultAttribute; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = null; + } + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target annotation. + */ + public ElementValueAdder(ProgramClass targetClass, + Annotation targetAnnotation, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = null; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = new ElementValuesEditor(targetClass, + targetAnnotation, + replaceElementValues); + } + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target element value. + */ + public ElementValueAdder(ProgramClass targetClass, + ArrayElementValue targetArrayElementValue, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = null; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = new ElementValuesEditor(targetClass, + targetArrayElementValue, + replaceElementValues); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + // Create a copy of the element value. + ConstantElementValue newConstantElementValue = + new ConstantElementValue(constantElementValue.u1tag, + constantElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, constantElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, constantElementValue.u2constantValueIndex)); + + newConstantElementValue.referencedClass = constantElementValue.referencedClass; + newConstantElementValue.referencedMethod = constantElementValue.referencedMethod; + + // Add it to the target. + addElementValue(newConstantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Create a copy of the element value. + EnumConstantElementValue newEnumConstantElementValue = + new EnumConstantElementValue(enumConstantElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, enumConstantElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, enumConstantElementValue.u2typeNameIndex), + constantAdder.addConstant(clazz, enumConstantElementValue.u2constantNameIndex)); + + newEnumConstantElementValue.referencedClass = enumConstantElementValue.referencedClass; + newEnumConstantElementValue.referencedMethod = enumConstantElementValue.referencedMethod; + + // TODO: Clone array. + newEnumConstantElementValue.referencedClasses = enumConstantElementValue.referencedClasses; + + // Add it to the target. + addElementValue(newEnumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Create a copy of the element value. + ClassElementValue newClassElementValue = + new ClassElementValue(classElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, classElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, classElementValue.u2classInfoIndex)); + + newClassElementValue.referencedClass = classElementValue.referencedClass; + newClassElementValue.referencedMethod = classElementValue.referencedMethod; + + // TODO: Clone array. + newClassElementValue.referencedClasses = classElementValue.referencedClasses; + + // Add it to the target. + addElementValue(newClassElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Create a copy of the element value. + AnnotationElementValue newAnnotationElementValue = + new AnnotationElementValue(annotationElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, annotationElementValue.u2elementNameIndex), + new Annotation()); + + newAnnotationElementValue.referencedClass = annotationElementValue.referencedClass; + newAnnotationElementValue.referencedMethod = annotationElementValue.referencedMethod; + + annotationElementValue.annotationAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationElementValue)); + + // Add it to the target. + addElementValue(newAnnotationElementValue); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Create a copy of the element value. + ArrayElementValue newArrayElementValue = + new ArrayElementValue(arrayElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, arrayElementValue.u2elementNameIndex), + 0, + arrayElementValue.u2elementValuesCount > 0 ? + new ElementValue[arrayElementValue.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + newArrayElementValue.referencedClass = arrayElementValue.referencedClass; + newArrayElementValue.referencedMethod = arrayElementValue.referencedMethod; + + arrayElementValue.elementValuesAccept(clazz, + annotation, + new ElementValueAdder(targetClass, + newArrayElementValue, + false)); + + // Add it to the target. + addElementValue(newArrayElementValue); + } + + + // Small utility methods. + + private void addElementValue(ElementValue newElementValue) + { + // What's the target? + if (targetAnnotationDefaultAttribute != null) + { + // Simply set the completed element value. + targetAnnotationDefaultAttribute.defaultValue = newElementValue; + } + else + { + // Add it to the target. + elementValuesEditor.addElementValue(newElementValue); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/ElementValuesEditor.java b/src/proguard/classfile/editor/ElementValuesEditor.java new file mode 100644 index 0000000..bfc4e9f --- /dev/null +++ b/src/proguard/classfile/editor/ElementValuesEditor.java @@ -0,0 +1,238 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.annotation.*; + +/** + * This class can add and delete element values to and from a given target + * annotation default attribute, annotation, or array element value. Element + * values to be added must be filled out beforehand, including their references + * to the constant pool. + * + * @author Eric Lafortune + */ +public class ElementValuesEditor +{ + private final ProgramClass targetClass; + private final Annotation targetAnnotation; + private final ArrayElementValue targetArrayElementValue; + private final boolean replaceElementValues; + + + /** + * Creates a new ElementValuesEditor that will edit element values in the + * given target annotation. + */ + public ElementValuesEditor(ProgramClass targetClass, + Annotation targetAnnotation, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotation = targetAnnotation; + this.targetArrayElementValue = null; + this.replaceElementValues = replaceElementValues; + } + + + /** + * Creates a new ElementValuesEditor that will edit element values in the + * given target array element value. + */ + public ElementValuesEditor(ProgramClass targetClass, + ArrayElementValue targetArrayElementValue, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotation = null; + this.targetArrayElementValue = targetArrayElementValue; + this.replaceElementValues = replaceElementValues; + } + + + /** + * Adds the given elementValue to the target. + */ + public void addElementValue(ElementValue elementValue) + { + // What's the target? + if (targetAnnotation != null) + { + // Try to replace an existing element value. + if (!replaceElementValues || + !replaceElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValue)) + { + // Otherwise append the element value. + targetAnnotation.elementValues = + addElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValue); + + targetAnnotation.u2elementValuesCount++; + } + } + else + { + // Try to replace an existing element value. + if (!replaceElementValues || + !replaceElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValue)) + { + // Otherwise append the element value. + targetArrayElementValue.elementValues = + addElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValue); + + targetArrayElementValue.u2elementValuesCount++; + } + } + } + + + /** + * Deletes the given elementValue to the target. + */ + public void deleteElementValue(String elementValueMethodName) + { + // What's the target? + if (targetAnnotation != null) + { + // Delete the element value to the target annotation. + targetAnnotation.u2elementValuesCount = + deleteElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValueMethodName); + } + else + { + // Delete the element value to the target array element value. + targetArrayElementValue.u2elementValuesCount = + deleteElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValueMethodName); + } + } + + + // Small utility methods. + + /** + * Tries put the given element value in place of an existing element value + * of the same name, returning whether it was present. + */ + private boolean replaceElementValue(int elementValuesCount, + ElementValue[] elementValues, + ElementValue elementValue) + { + // Find the element value with the same name. + int index = findElementValue(elementValuesCount, + elementValues, + elementValue.getMethodName(targetClass)); + if (index < 0) + { + return false; + } + + elementValues[index] = elementValue; + + return true; + } + + + /** + * Appends the given element value to the given array of element values, + * creating a new array if necessary. + */ + private ElementValue[] addElementValue(int elementValuesCount, + ElementValue[] elementValues, + ElementValue elementValue) + { + // Is the array too small to contain the additional elementValue? + if (elementValues.length <= elementValuesCount) + { + // Create a new array and copy the elementValues into it. + ElementValue[] newElementValues = new ElementValue[elementValuesCount + 1]; + System.arraycopy(elementValues, 0, + newElementValues, 0, + elementValuesCount); + elementValues = newElementValues; + } + + // Append the elementValue. + elementValues[elementValuesCount] = elementValue; + + return elementValues; + } + + + /** + * Deletes the element values with the given name from the given array of + * element values, returning the new number of element values. + */ + private int deleteElementValue(int elementValuesCount, + ElementValue[] elementValues, + String elementValueMethodName) + { + // Find the element value. + int index = findElementValue(elementValuesCount, + elementValues, + elementValueMethodName); + if (index < 0) + { + return elementValuesCount; + } + + // Shift the other element values in the array. + System.arraycopy(elementValues, index + 1, + elementValues, index, + elementValuesCount - index - 1); + + // Clear the last entry in the array. + elementValues[--elementValuesCount] = null; + + return elementValuesCount; + } + + + /** + * Finds the index of the element value with the given name in the given + * array of element values. + */ + private int findElementValue(int elementValuesCount, + ElementValue[] elementValues, + String elementValueName) + { + for (int index = 0; index < elementValuesCount; index++) + { + if (elementValues[index].getMethodName(targetClass).equals(elementValueName)) + { + return index; + } + } + + return -1; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/ExceptionAdder.java b/src/proguard/classfile/editor/ExceptionAdder.java new file mode 100644 index 0000000..1ccb1a6 --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionAdder.java @@ -0,0 +1,65 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.ExceptionsAttribute; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor adds all class constants that it visits to the given + * target exceptions attribute. + * + * @author Eric Lafortune + */ +public class ExceptionAdder +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ConstantAdder constantAdder; + private final ExceptionsAttributeEditor exceptionsAttributeEditor; + + + /** + * Creates a new ExceptionAdder that will copy classes into the given + * target exceptions attribute. + */ + public ExceptionAdder(ProgramClass targetClass, + ExceptionsAttribute targetExceptionsAttribute) + { + constantAdder = new ConstantAdder(targetClass); + exceptionsAttributeEditor = new ExceptionsAttributeEditor(targetExceptionsAttribute); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Add a class constant to the constant pool. + constantAdder.visitClassConstant(clazz, classConstant); + + // Add the index of the class constant to the list of exceptions. + exceptionsAttributeEditor.addException(constantAdder.getConstantIndex()); + } +} diff --git a/src/proguard/classfile/editor/ExceptionInfoAdder.java b/src/proguard/classfile/editor/ExceptionInfoAdder.java new file mode 100644 index 0000000..e0cc9c5 --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionInfoAdder.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor adds all exception information that it visits to + * the given target code attribute. + * + * @author Eric Lafortune + */ +public class ExceptionInfoAdder +implements ExceptionInfoVisitor +{ + private final ConstantAdder constantAdder; + private final CodeAttributeComposer codeAttributeComposer; + + + /** + * Creates a new ExceptionAdder that will copy exceptions into the given + * target code attribute. + */ + public ExceptionInfoAdder(ProgramClass targetClass, + CodeAttributeComposer targetComposer) + { + constantAdder = new ConstantAdder(targetClass); + codeAttributeComposer = targetComposer; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Create a copy of the exception info. + ExceptionInfo newExceptionInfo = + new ExceptionInfo(exceptionInfo.u2startPC, + exceptionInfo.u2endPC, + exceptionInfo.u2handlerPC, + exceptionInfo.u2catchType == 0 ? 0 : + constantAdder.addConstant(clazz, exceptionInfo.u2catchType)); + + // Add the completed exception info. + codeAttributeComposer.appendException(newExceptionInfo); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java new file mode 100644 index 0000000..4509a9a --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java @@ -0,0 +1,68 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.ExceptionsAttribute; + +/** + * This class can add exceptions to a given exceptions attribute. + * Exceptions to be added must have been added to the constant pool and filled + * out beforehand. + * + * @author Eric Lafortune + */ +public class ExceptionsAttributeEditor +{ + private ExceptionsAttribute targetExceptionsAttribute; + + + /** + * Creates a new ExceptionsAttributeEditor that will edit exceptions in the + * given exceptions attribute. + */ + public ExceptionsAttributeEditor(ExceptionsAttribute targetExceptionsAttribute) + { + this.targetExceptionsAttribute = targetExceptionsAttribute; + } + + + /** + * Adds a given exception to the exceptions attribute. + */ + public void addException(int exceptionIndex) + { + int exceptionIndexTableLength = targetExceptionsAttribute.u2exceptionIndexTableLength; + int[] exceptionIndexTable = targetExceptionsAttribute.u2exceptionIndexTable; + + // Make sure there is enough space for the new exception. + if (exceptionIndexTable.length <= exceptionIndexTableLength) + { + targetExceptionsAttribute.u2exceptionIndexTable = new int[exceptionIndexTableLength+1]; + System.arraycopy(exceptionIndexTable, 0, + targetExceptionsAttribute.u2exceptionIndexTable, 0, + exceptionIndexTableLength); + exceptionIndexTable = targetExceptionsAttribute.u2exceptionIndexTable; + } + + // Add the exception. + exceptionIndexTable[targetExceptionsAttribute.u2exceptionIndexTableLength++] = exceptionIndex; + } +} diff --git a/src/proguard/classfile/editor/InstructionAdder.java b/src/proguard/classfile/editor/InstructionAdder.java new file mode 100644 index 0000000..60fde6d --- /dev/null +++ b/src/proguard/classfile/editor/InstructionAdder.java @@ -0,0 +1,76 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor adds all instructions that it visits to the given + * target code attribute. + * + * @author Eric Lafortune + */ +public class InstructionAdder +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final ConstantAdder constantAdder; + private final CodeAttributeComposer codeAttributeComposer; + + + /** + * Creates a new InstructionAdder that will copy classes into the given + * target code attribute. + */ + public InstructionAdder(ProgramClass targetClass, + CodeAttributeComposer targetComposer) + { + constantAdder = new ConstantAdder(targetClass); + codeAttributeComposer = targetComposer; + } + + + // Implementations for InstructionVisitor. + + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Add the instruction. + codeAttributeComposer.appendInstruction(offset, instruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Create a copy of the instruction. + Instruction newConstantInstruction = + new ConstantInstruction(constantInstruction.opcode, + constantAdder.addConstant(clazz, constantInstruction.constantIndex), + constantInstruction.constant).shrink(); + + // Add the instruction. + codeAttributeComposer.appendInstruction(offset, newConstantInstruction); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/InstructionWriter.java b/src/proguard/classfile/editor/InstructionWriter.java new file mode 100644 index 0000000..d842358 --- /dev/null +++ b/src/proguard/classfile/editor/InstructionWriter.java @@ -0,0 +1,278 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor writes out the instructions that it visits, + * collecting instructions that have to be widened. As an AttributeVisitor, + * it then applies the collected changes. The process will be repeated + * recursively, if necessary. + * + * @author Eric Lafortune + */ +public class InstructionWriter +extends SimplifiedVisitor +implements InstructionVisitor, + AttributeVisitor +{ + private int codeLength; + + private CodeAttributeEditor codeAttributeEditor; + + + /** + * Resets the accumulated code changes. + * @param codeLength the length of the code that will be edited next. + */ + public void reset(int codeLength) + { + this.codeLength = codeLength; + + // The code attribute editor has to be created lazily. + if (codeAttributeEditor != null) + { + codeAttributeEditor.reset(codeLength); + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + // Try to write out the instruction. + // Simple instructions should always fit. + simpleInstruction.write(codeAttribute, offset); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + try + { + // Try to write out the instruction. + constantInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new constant instruction that will fit. + Instruction replacementInstruction = + new ConstantInstruction(constantInstruction.opcode, + constantInstruction.constantIndex, + constantInstruction.constant).shrink(); + + replaceInstruction(offset, replacementInstruction); + + // Write out a dummy constant instruction for now. + constantInstruction.constantIndex = 0; + constantInstruction.constant = 0; + constantInstruction.write(codeAttribute, offset); + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + try + { + // Try to write out the instruction. + variableInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new variable instruction that will fit. + Instruction replacementInstruction = + new VariableInstruction(variableInstruction.opcode, + variableInstruction.variableIndex, + variableInstruction.constant).shrink(); + + replaceInstruction(offset, replacementInstruction); + + // Write out a dummy variable instruction for now. + variableInstruction.variableIndex = 0; + variableInstruction.constant = 0; + variableInstruction.write(codeAttribute, offset); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + try + { + // Try to write out the instruction. + branchInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new unconditional branch that will fit. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO_W, + branchInstruction.branchOffset); + + // Create a new instruction that will fit. + switch (branchInstruction.opcode) + { + default: + { + // Create a new branch instruction that will fit. + replacementInstruction = + new BranchInstruction(branchInstruction.opcode, + branchInstruction.branchOffset).shrink(); + + break; + } + + // Some special cases, for which a wide branch doesn't exist. + case InstructionConstants.OP_IFEQ: + case InstructionConstants.OP_IFNE: + case InstructionConstants.OP_IFLT: + case InstructionConstants.OP_IFGE: + case InstructionConstants.OP_IFGT: + case InstructionConstants.OP_IFLE: + case InstructionConstants.OP_IFICMPEQ: + case InstructionConstants.OP_IFICMPNE: + case InstructionConstants.OP_IFICMPLT: + case InstructionConstants.OP_IFICMPGE: + case InstructionConstants.OP_IFICMPGT: + case InstructionConstants.OP_IFICMPLE: + case InstructionConstants.OP_IFACMPEQ: + case InstructionConstants.OP_IFACMPNE: + { + // Insert the complementary conditional branch. + Instruction complementaryConditionalBranch = + new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1), + (1+2) + (1+4)); + + insertBeforeInstruction(offset, complementaryConditionalBranch); + + // Create a new unconditional branch that will fit. + break; + } + + case InstructionConstants.OP_IFNULL: + case InstructionConstants.OP_IFNONNULL: + { + // Insert the complementary conditional branch. + Instruction complementaryConditionalBranch = + new BranchInstruction((byte)(branchInstruction.opcode ^ 1), + (1+2) + (1+4)); + + insertBeforeInstruction(offset, complementaryConditionalBranch); + + // Create a new unconditional branch that will fit. + break; + } + } + + replaceInstruction(offset, replacementInstruction); + + // Write out a dummy branch instruction for now. + branchInstruction.branchOffset = 0; + branchInstruction.write(codeAttribute, offset); + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Try to write out the instruction. + // Switch instructions should always fit. + switchInstruction.write(codeAttribute, offset); + } + + + // Implementations for AttributeVisitor. + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Avoid doing any work if nothing is changing anyway. + if (codeAttributeEditor != null) + { + // Apply the collected expansions. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + // Clear the modifications for the next run. + codeAttributeEditor = null; + } + } + + + // Small utility methods. + + /** + * Remembers to place the given instruction right before the instruction + * at the given offset. + */ + private void insertBeforeInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction); + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instruction. + */ + private void replaceInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.replaceInstruction(instructionOffset, instruction); + } + + + /** + * Remembers to place the given instruction right after the instruction + * at the given offset. + */ + private void insertAfterInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction); + } + + + /** + * Makes sure there is a code attribute editor for the given code attribute. + */ + private void ensureCodeAttributeEditor() + { + if (codeAttributeEditor == null) + { + codeAttributeEditor = new CodeAttributeEditor(); + codeAttributeEditor.reset(codeLength); + } + } +} diff --git a/src/proguard/classfile/editor/InterfaceAdder.java b/src/proguard/classfile/editor/InterfaceAdder.java new file mode 100644 index 0000000..e095af6 --- /dev/null +++ b/src/proguard/classfile/editor/InterfaceAdder.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor adds all interfaces that it visits to the given + * target class. + * + * @author Eric Lafortune + */ +public class InterfaceAdder +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ConstantAdder constantAdder; + private final InterfacesEditor interfacesEditor; + + + /** + * Creates a new InterfaceAdder that will add interfaces to the given + * target class. + */ + public InterfaceAdder(ProgramClass targetClass) + { + constantAdder = new ConstantAdder(targetClass); + interfacesEditor = new InterfacesEditor(targetClass); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + interfacesEditor.addInterface(constantAdder.addConstant(clazz, classConstant)); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/InterfaceSorter.java b/src/proguard/classfile/editor/InterfaceSorter.java new file mode 100644 index 0000000..6521369 --- /dev/null +++ b/src/proguard/classfile/editor/InterfaceSorter.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Arrays; + +/** + * This ClassVisitor sorts the interfaces of the program classes that it visits. + * + * @author Eric Lafortune + */ +public class InterfaceSorter +extends SimplifiedVisitor +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + int[] interfaces = programClass.u2interfaces; + int interfacesCount = programClass.u2interfacesCount; + + // Sort the interfaces. + Arrays.sort(interfaces, 0, interfacesCount); + + // Remove any duplicate entries. + int newInterfacesCount = 0; + int previousInterfaceIndex = 0; + for (int index = 0; index < interfacesCount; index++) + { + int interfaceIndex = interfaces[index]; + + // Isn't this a duplicate of the previous interface? + if (interfaceIndex != previousInterfaceIndex) + { + interfaces[newInterfacesCount++] = interfaceIndex; + + // Remember the interface. + previousInterfaceIndex = interfaceIndex; + } + } + + programClass.u2interfacesCount = newInterfacesCount; + } +} diff --git a/src/proguard/classfile/editor/InterfacesEditor.java b/src/proguard/classfile/editor/InterfacesEditor.java new file mode 100644 index 0000000..d3170e1 --- /dev/null +++ b/src/proguard/classfile/editor/InterfacesEditor.java @@ -0,0 +1,122 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; + +import java.util.Arrays; + +/** + * This class can add and delete interfaces to and from classes. References to + * the constant pool must be filled out beforehand. + * + * @author Eric Lafortune + */ +public class InterfacesEditor +{ + private final ProgramClass targetClass; + + + /** + * Creates a new InterfacesEditor that will edit interfaces in the given + * target class. + */ + public InterfacesEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Adds the specified interface to the target class, if it isn't present yet. + */ + public void addInterface(int interfaceConstantIndex) + { + // Is the interface not yet present? + if (findInterfaceIndex(interfaceConstantIndex) < 0) + { + int interfacesCount = targetClass.u2interfacesCount++; + int[] interfaces = targetClass.u2interfaces; + + // Is the array too small to contain the additional interface? + if (interfaces.length <= interfacesCount) + { + // Create a new array and copy the interfaces into it. + int[] newinterfaces = new int[interfacesCount + 1]; + System.arraycopy(interfaces, 0, newinterfaces, 0, interfacesCount); + interfaces = newinterfaces; + + targetClass.u2interfaces = interfaces; + } + + // Append the interface. + interfaces[interfacesCount] = interfaceConstantIndex; + } + } + + + /** + * Deletes the given interface from the target class, if it is present. + */ + public void deleteInterface(int interfaceConstantIndex) + { + // Is the interface already present? + int interfaceIndex = findInterfaceIndex(interfaceConstantIndex); + if (interfaceIndex >= 0) + { + int interfacesCount = --targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + // Shift the other interfaces in the array. + for (int index = interfaceIndex; index < interfacesCount; index++) + { + interfaces[index] = interfaces[index + 1]; + } + + // Clear the remaining entry in the array. + interfaces[interfacesCount] = 0; + } + } + + + // Small utility methods. + + /** + * Finds the index of the specified interface in the list of interfaces of + * the target class. + */ + private int findInterfaceIndex(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + for (int index = 0; index < interfacesCount; index++) + { + if (interfaces[index] == interfaceConstantIndex) + { + return index; + } + } + + return -1; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/LineNumberInfoAdder.java b/src/proguard/classfile/editor/LineNumberInfoAdder.java new file mode 100644 index 0000000..aa8c0c4 --- /dev/null +++ b/src/proguard/classfile/editor/LineNumberInfoAdder.java @@ -0,0 +1,59 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.visitor.LineNumberInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LineNumberInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LineNumberInfoAdder +implements LineNumberInfoVisitor +{ + private final LineNumberTableAttributeEditor lineNumberTableAttributeEditor; + + + /** + * Creates a new LineNumberInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LineNumberInfoAdder(LineNumberTableAttribute targetLineNumberTableAttribute) + { + this.lineNumberTableAttributeEditor = new LineNumberTableAttributeEditor(targetLineNumberTableAttribute); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Create a new line number. + LineNumberInfo newLineNumberInfo = + new LineNumberInfo(lineNumberInfo.u2startPC, + lineNumberInfo.u2lineNumber); + + // Add it to the target. + lineNumberTableAttributeEditor.addLineNumberInfo(newLineNumberInfo); + } +} diff --git a/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java new file mode 100644 index 0000000..ab96b38 --- /dev/null +++ b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add line numbers to a given line number table attribute. + * Line numbers to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LineNumberTableAttributeEditor +{ + private LineNumberTableAttribute targetLineNumberTableAttribute; + + + /** + * Creates a new LineNumberTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LineNumberTableAttributeEditor(LineNumberTableAttribute targetLineNumberTableAttribute) + { + this.targetLineNumberTableAttribute = targetLineNumberTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLineNumberInfo(LineNumberInfo lineNumberInfo) + { + int lineNumberTableLength = targetLineNumberTableAttribute.u2lineNumberTableLength; + LineNumberInfo[] lineNumberTable = targetLineNumberTableAttribute.lineNumberTable; + + // Make sure there is enough space for the new lineNumberInfo. + if (lineNumberTable.length <= lineNumberTableLength) + { + targetLineNumberTableAttribute.lineNumberTable = new LineNumberInfo[lineNumberTableLength+1]; + System.arraycopy(lineNumberTable, 0, + targetLineNumberTableAttribute.lineNumberTable, 0, + lineNumberTableLength); + lineNumberTable = targetLineNumberTableAttribute.lineNumberTable; + } + + // Add the lineNumberInfo. + lineNumberTable[targetLineNumberTableAttribute.u2lineNumberTableLength++] = lineNumberInfo; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableInfoAdder.java b/src/proguard/classfile/editor/LocalVariableInfoAdder.java new file mode 100644 index 0000000..f285e4f --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableInfoAdder.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.visitor.LocalVariableInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LocalVariableInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LocalVariableInfoAdder +implements LocalVariableInfoVisitor +{ + private final ConstantAdder constantAdder; + private final LocalVariableTableAttributeEditor localVariableTableAttributeEditor; + + + /** + * Creates a new LocalVariableInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LocalVariableInfoAdder(ProgramClass targetClass, + LocalVariableTableAttribute targetLocalVariableTableAttribute) + { + this.constantAdder = new ConstantAdder(targetClass); + this.localVariableTableAttributeEditor = new LocalVariableTableAttributeEditor(targetLocalVariableTableAttribute); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Create a new line number. + LocalVariableInfo newLocalVariableInfo = + new LocalVariableInfo(localVariableInfo.u2startPC, + localVariableInfo.u2length, + constantAdder.addConstant(clazz, localVariableInfo.u2nameIndex), + constantAdder.addConstant(clazz, localVariableInfo.u2descriptorIndex), + localVariableInfo.u2index); + + newLocalVariableInfo.referencedClass = localVariableInfo.referencedClass; + + // Add it to the target. + localVariableTableAttributeEditor.addLocalVariableInfo(newLocalVariableInfo); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java new file mode 100644 index 0000000..053b628 --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add local variables to a given local variable table attribute. + * Local variables to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LocalVariableTableAttributeEditor +{ + private LocalVariableTableAttribute targetLocalVariableTableAttribute; + + + /** + * Creates a new LocalVariableTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LocalVariableTableAttributeEditor(LocalVariableTableAttribute targetLocalVariableTableAttribute) + { + this.targetLocalVariableTableAttribute = targetLocalVariableTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLocalVariableInfo(LocalVariableInfo localVariableInfo) + { + int localVariableTableLength = targetLocalVariableTableAttribute.u2localVariableTableLength; + LocalVariableInfo[] localVariableTable = targetLocalVariableTableAttribute.localVariableTable; + + // Make sure there is enough space for the new localVariableInfo. + if (localVariableTable.length <= localVariableTableLength) + { + targetLocalVariableTableAttribute.localVariableTable = new LocalVariableInfo[localVariableTableLength+1]; + System.arraycopy(localVariableTable, 0, + targetLocalVariableTableAttribute.localVariableTable, 0, + localVariableTableLength); + localVariableTable = targetLocalVariableTableAttribute.localVariableTable; + } + + // Add the localVariableInfo. + localVariableTable[targetLocalVariableTableAttribute.u2localVariableTableLength++] = localVariableInfo; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java new file mode 100644 index 0000000..ca50f3f --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java @@ -0,0 +1,68 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.visitor.LocalVariableTypeInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LocalVariableTypeInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LocalVariableTypeInfoAdder +implements LocalVariableTypeInfoVisitor +{ + private final ConstantAdder constantAdder; + private final LocalVariableTypeTableAttributeEditor localVariableTypeTableAttributeEditor; + + + /** + * Creates a new LocalVariableTypeInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LocalVariableTypeInfoAdder(ProgramClass targetClass, + LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute) + { + this.constantAdder = new ConstantAdder(targetClass); + this.localVariableTypeTableAttributeEditor = new LocalVariableTypeTableAttributeEditor(targetLocalVariableTypeTableAttribute); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Create a new line number. + LocalVariableTypeInfo newLocalVariableTypeInfo = + new LocalVariableTypeInfo(localVariableTypeInfo.u2startPC, + localVariableTypeInfo.u2length, + constantAdder.addConstant(clazz, localVariableTypeInfo.u2nameIndex), + constantAdder.addConstant(clazz, localVariableTypeInfo.u2signatureIndex), + localVariableTypeInfo.u2index); + + // TODO: Clone array. + newLocalVariableTypeInfo.referencedClasses = localVariableTypeInfo.referencedClasses; + + // Add it to the target. + localVariableTypeTableAttributeEditor.addLocalVariableTypeInfo(newLocalVariableTypeInfo); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java new file mode 100644 index 0000000..fe5a64d --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java @@ -0,0 +1,68 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add local variables to a given local variable type table + * attribute. + * Local variable types to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LocalVariableTypeTableAttributeEditor +{ + private LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute; + + + /** + * Creates a new LocalVariableTypeTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LocalVariableTypeTableAttributeEditor(LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute) + { + this.targetLocalVariableTypeTableAttribute = targetLocalVariableTypeTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLocalVariableTypeInfo(LocalVariableTypeInfo localVariableTypeInfo) + { + int localVariableTypeTableLength = targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength; + LocalVariableTypeInfo[] localVariableTypeTable = targetLocalVariableTypeTableAttribute.localVariableTypeTable; + + // Make sure there is enough space for the new localVariableTypeInfo. + if (localVariableTypeTable.length <= localVariableTypeTableLength) + { + targetLocalVariableTypeTableAttribute.localVariableTypeTable = new LocalVariableTypeInfo[localVariableTypeTableLength+1]; + System.arraycopy(localVariableTypeTable, 0, + targetLocalVariableTypeTableAttribute.localVariableTypeTable, 0, + localVariableTypeTableLength); + localVariableTypeTable = targetLocalVariableTypeTableAttribute.localVariableTypeTable; + } + + // Add the localVariableTypeInfo. + localVariableTypeTable[targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength++] = localVariableTypeInfo; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/MemberAdder.java b/src/proguard/classfile/editor/MemberAdder.java new file mode 100644 index 0000000..5f939bb --- /dev/null +++ b/src/proguard/classfile/editor/MemberAdder.java @@ -0,0 +1,257 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This ConstantVisitor adds all class members that it visits to the given + * target class. + * + * @author Eric Lafortune + */ +public class MemberAdder +extends SimplifiedVisitor +implements MemberVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + + private final ProgramClass targetClass; +// private final boolean addFields; + + private final ConstantAdder constantAdder; + private final ClassEditor classEditor; + private final ConstantPoolEditor constantPoolEditor; + + + /** + * Creates a new MemberAdder that will copy methods into the given target + * class. + * @param targetClass the class to which all visited class members will be + * added. + */ +// * @param addFields specifies whether fields should be added, or fused +// * with the present fields. + public MemberAdder(ProgramClass targetClass)//), +// boolean addFields) + { + this.targetClass = targetClass; +// this.addFields = addFields; + + constantAdder = new ConstantAdder(targetClass); + classEditor = new ClassEditor(targetClass); + constantPoolEditor = new ConstantPoolEditor(targetClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + String name = programField.getName(programClass); + String descriptor = programField.getDescriptor(programClass); + int accessFlags = programField.getAccessFlags(); + + // Does the target class already have such a field? + ProgramField targetField = (ProgramField)targetClass.findField(name, descriptor); + if (targetField != null) + { + // Is the field private or static? + int targetAccessFlags = targetField.getAccessFlags(); + if ((targetAccessFlags & + (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC)) != 0) + { + if (DEBUG) + { + System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]"); + } + + // Rename the private or static field. + targetField.u2nameIndex = + constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName())); + } +// else +// { +// // Keep the non-private and non-static field, but update its +// // contents, in order to keep any references to it valid. +// if (DEBUG) +// { +// System.out.println("MemberAdder: updating field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); +// } +// +// // Combine the access flags. +// targetField.u2accessFlags = accessFlags | targetAccessFlags; +// +// // Add and replace any attributes. +// programField.attributesAccept(programClass, +// new AttributeAdder(targetClass, +// targetField, +// true)); +// +// // Don't add a new field. +// return; +// } + } + + if (DEBUG) + { + System.out.println("MemberAdder: copying field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Create a copy of the field. + ProgramField newProgramField = + new ProgramField(accessFlags, + constantAdder.addConstant(programClass, programField.u2nameIndex), + constantAdder.addConstant(programClass, programField.u2descriptorIndex), + 0, + programField.u2attributesCount > 0 ? + new Attribute[programField.u2attributesCount] : + EMPTY_ATTRIBUTES, + programField.referencedClass); + + // Link to its visitor info. + newProgramField.setVisitorInfo(programField); + + // Copy its attributes. + programField.attributesAccept(programClass, + new AttributeAdder(targetClass, + newProgramField, + false)); + + // Add the completed field. + classEditor.addField(newProgramField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + String name = programMethod.getName(programClass); + String descriptor = programMethod.getDescriptor(programClass); + int accessFlags = programMethod.getAccessFlags(); + + // Does the target class already have such a method? + ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor); + if (targetMethod != null) + { + // is this source method abstract? + if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + // Keep the target method. + if (DEBUG) + { + System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Don't add a new method. + return; + } + + // Is the target method abstract? + int targetAccessFlags = targetMethod.getAccessFlags(); + if ((targetAccessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + // Keep the abstract method, but update its contents, in order + // to keep any references to it valid. + if (DEBUG) + { + System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Replace the access flags. + targetMethod.u2accessFlags = + accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL; + + // Add and replace the attributes. + programMethod.attributesAccept(programClass, + new AttributeAdder(targetClass, + targetMethod, + true)); + + // Don't add a new method. + return; + } + + if (DEBUG) + { + System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + } + + // Rename the private (non-abstract) or static method. + targetMethod.u2nameIndex = + constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, descriptor)); + } + + if (DEBUG) + { + System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Create a copy of the method. + ProgramMethod newProgramMethod = + new ProgramMethod(accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL, + constantAdder.addConstant(programClass, programMethod.u2nameIndex), + constantAdder.addConstant(programClass, programMethod.u2descriptorIndex), + 0, + programMethod.u2attributesCount > 0 ? + new Attribute[programMethod.u2attributesCount] : + EMPTY_ATTRIBUTES, + programMethod.referencedClasses != null ? + (Clazz[])programMethod.referencedClasses.clone() : + null); + + // Link to its visitor info. + newProgramMethod.setVisitorInfo(programMethod); + + // Copy its attributes. + programMethod.attributesAccept(programClass, + new AttributeAdder(targetClass, + newProgramMethod, + false)); + + // Add the completed method. + classEditor.addMethod(newProgramMethod); + } + + + // Small utility methods. + + /** + * Returns a unique class member name, based on the given name and descriptor. + */ + private String newUniqueMemberName(String name, String descriptor) + { + return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + ClassConstants.INTERNAL_METHOD_NAME_INIT : + name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + } +} diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/src/proguard/classfile/editor/MemberReferenceFixer.java new file mode 100644 index 0000000..4bd8af5 --- /dev/null +++ b/src/proguard/classfile/editor/MemberReferenceFixer.java @@ -0,0 +1,456 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor fixes constant pool field and method references to fields + * and methods whose names or descriptors have changed. + * + * @author Eric Lafortune + */ +public class MemberReferenceFixer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private static final boolean DEBUG = false; + + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + + // Parameter for the visitor methods. + private int constantIndex; + + // Return values for the visitor methods. + private boolean isInterfaceMethod; + private boolean stackSizesMayHaveChanged; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + stackSizesMayHaveChanged = false; + + // Fix the constant pool entries. + for (int index = 1; index < programClass.u2constantPoolCount; index++) + { + Constant constant = programClass.constantPool[index]; + if (constant != null) + { + // Fix the entry, replacing it entirely if needed. + this.constantIndex = index; + + constant.accept(programClass, this); + } + } + + // Fix the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Fix the attributes. + programClass.attributesAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Does the string refer to a class member, due to a + // Class.get[Declared]{Field,Method} construct? + Member referencedMember = stringConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = stringConstant.referencedClass; + + // Does it have a new name? + String newName = referencedMember.getName(referencedClass); + + if (!stringConstant.getString(clazz).equals(newName)) + { + if (DEBUG) + { + debug(clazz, stringConstant, referencedClass, referencedMember); + } + + // Update the name. + stringConstant.u2stringIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); + } + } + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Do we know the referenced field? + Member referencedMember = fieldrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = fieldrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!fieldrefConstant.getName(clazz).equals(newName) || + !fieldrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, fieldrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + fieldrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + } + } + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + // Do we know the referenced interface method? + Member referencedMember = interfaceMethodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = interfaceMethodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!interfaceMethodrefConstant.getName(clazz).equals(newName) || + !interfaceMethodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + interfaceMethodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = true; + clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this); + + // Has the method become a non-interface method? + if (!isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref method = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz)); + System.out.println(" -> ordinary method"); + } + + // Replace the interface method reference by a method reference. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new MethodrefConstant(interfaceMethodrefConstant.u2classIndex, + interfaceMethodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // Do we know the referenced method? + Member referencedMember = methodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = methodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!methodrefConstant.getName(clazz).equals(newName) || + !methodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, methodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + methodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = false; + clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this); + + // Has the method become an interface method? + if (isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref method = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz)); + System.out.println(" -> interface method"); + } + + // Replace the method reference by an interface method reference. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new InterfaceMethodrefConstant(methodrefConstant.u2classIndex, + methodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check if this class entry is an array type. + if (ClassUtil.isInternalArrayType(classConstant.getName(clazz))) + { + isInterfaceMethod = false; + } + else + { + // Check if this class entry refers to an interface class. + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0; + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Fix the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + Member referencedMember = enclosingMethodAttribute.referencedMethod; + if (referencedMember != null) + { + Clazz referencedClass = enclosingMethodAttribute.referencedClass; + + // Does it have a new class? + if (!enclosingMethodAttribute.getClassName(clazz).equals(referencedClass.getName())) + { + // Update the class index. + enclosingMethodAttribute.u2classIndex = + new ConstantPoolEditor((ProgramClass)clazz).addClassConstant(referencedClass); + } + + // Does it have a new name or type? + if (!enclosingMethodAttribute.getName(clazz).equals(referencedMember.getName(referencedClass)) || + !enclosingMethodAttribute.getType(clazz).equals(referencedMember.getDescriptor(referencedClass))) + { + // Update the name and type index. + enclosingMethodAttribute.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass)); + } + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Recompute the maximum stack size if necessary. + if (stackSizesMayHaveChanged) + { + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Fix the nested attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Fix the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Fix the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Fix the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Fix the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + fixElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + fixElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + fixElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + fixElementValue(clazz, annotation, annotationElementValue); + + // Fix the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + fixElementValue(clazz, annotation, arrayElementValue); + + // Fix the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Fixes the method reference of the element value, if any. + */ + private void fixElementValue(Clazz clazz, + Annotation annotation, + ElementValue elementValue) + { + // Do we know the referenced method? + Member referencedMember = elementValue.referencedMethod; + if (referencedMember != null) + { + // Does it have a new name or type? + String methodName = elementValue.getMethodName(clazz); + String newMethodName = referencedMember.getName(elementValue.referencedClass); + + if (!methodName.equals(newMethodName)) + { + // Update the element name index. + elementValue.u2elementNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName); + } + } + } + + + private void debug(Clazz clazz, + StringConstant stringConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref member name = "+stringConstant.getString(clazz)); + System.out.println(" -> "+referencedMember.getName(referencedClass)); + } + + + private void debug(Clazz clazz, + RefConstant refConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref member name = "+refConstant.getName(clazz)); + System.out.println(" -> "+referencedMember.getName(referencedClass)); + System.out.println(" Ref descriptor = "+refConstant.getType(clazz)); + System.out.println(" -> "+referencedMember.getDescriptor(referencedClass)); + } +} diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java new file mode 100644 index 0000000..ef76012 --- /dev/null +++ b/src/proguard/classfile/editor/MethodInvocationFixer.java @@ -0,0 +1,254 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This AttributeVisitor fixes all inappropriate special/virtual/static/interface + * invocations of the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class MethodInvocationFixer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ClassVisitor, + MemberVisitor +{ + private static final boolean DEBUG = false; + + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + // Return values for the visitor methods. + private Clazz referencedClass; + private Clazz referencedMethodClass; + private Member referencedMethod; + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Reset the code attribute editor. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Remap the variables of the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Apply the code atribute editor. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + int constantIndex = constantInstruction.constantIndex; + + // Get information on the called class and method, if present. + referencedMethod = null; + + clazz.constantPoolEntryAccept(constantIndex, this); + + // Did we find the called class and method? + if (referencedMethod != null) + { + // Do we need to update the opcode? + byte opcode = constantInstruction.opcode; + + // Is the method static? + if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + // But is it not a static invocation? + if (opcode != InstructionConstants.OP_INVOKESTATIC) + { + // Replace the invocation by an invokestatic instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method private, or an instance initializer? + else if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 || + referencedMethod.getName(referencedMethodClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + // But is it not a special invocation? + if (opcode != InstructionConstants.OP_INVOKESPECIAL) + { + // Replace the invocation by an invokespecial instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method an interface method? + else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + int invokeinterfaceConstant = + (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)) << 8; + + // But is it not an interface invocation, or is the parameter + // size incorrect? + if (opcode != InstructionConstants.OP_INVOKEINTERFACE || + constantInstruction.constant != invokeinterfaceConstant) + { + // Fix the parameter size of the interface invocation. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, + constantIndex, + invokeinterfaceConstant).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // The method is not static, private, an instance initializer, or + // an interface method. + else + { + // But is it not a virtual invocation (or a special invocation, + // but not a super call)? + if (opcode != InstructionConstants.OP_INVOKEVIRTUAL && + (opcode != InstructionConstants.OP_INVOKESPECIAL || + !clazz.extends_(referencedClass))) + { + // Replace the invocation by an invokevirtual instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, + constantIndex).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + // Check if this is an interface method. Note that we're interested in + // the class of the method reference, not in the class in which the + // method was actually found. + //refConstant.referencedClassAccept(this); + clazz.constantPoolEntryAccept(refConstant.u2classIndex, this); + + // Get the referenced access flags and names. + refConstant.referencedMemberAccept(this); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check if this is an interface class. + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Remember the referenced class. + referencedClass = clazz; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Remember the referenced method. + referencedMethodClass = clazz; + referencedMethod = member; + } + + + // Small utility methods. + + private void debug(Clazz clazz, + Method method, + int offset, + ConstantInstruction constantInstruction, + Instruction replacementInstruction) + { + System.out.println("MethodInvocationFixer:"); + System.out.println(" Class = "+clazz.getName()); + System.out.println(" Method = "+method.getName(clazz)+method.getDescriptor(clazz)); + System.out.println(" Instruction = "+constantInstruction.toString(offset)); + System.out.println(" -> Class = "+referencedClass); + System.out.println(" Method = "+referencedMethod); + if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + System.out.println(" Parameter size = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false))); + } + System.out.println(" Replacement instruction = "+replacementInstruction.toString(offset)); + } +} diff --git a/src/proguard/classfile/editor/NamedAttributeDeleter.java b/src/proguard/classfile/editor/NamedAttributeDeleter.java new file mode 100644 index 0000000..0c4d339 --- /dev/null +++ b/src/proguard/classfile/editor/NamedAttributeDeleter.java @@ -0,0 +1,54 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; +import proguard.util.StringMatcher; + + +/** + * This ClassVisitor deletes attributes with a given name in the program + * classes that it visits. + * + * @author Eric Lafortune + */ +public class NamedAttributeDeleter implements ClassVisitor +{ + private final String attributeName; + + + public NamedAttributeDeleter(String attributeName) + { + this.attributeName = attributeName; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + new AttributesEditor(programClass, false).deleteAttribute(attributeName); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java new file mode 100644 index 0000000..4cad6b8 --- /dev/null +++ b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.attribute.annotation.*; + +/** + * This class can add annotations to a given parameter annotations attribute. + * Annotations to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class ParameterAnnotationsAttributeEditor +{ + private ParameterAnnotationsAttribute targetParameterAnnotationsAttribute; + + + /** + * Creates a new ParameterAnnotationsAttributeEditor that will edit + * annotations in the given parameter annotations attribute. + */ + public ParameterAnnotationsAttributeEditor(ParameterAnnotationsAttribute targetParameterAnnotationsAttribute) + { + this.targetParameterAnnotationsAttribute = targetParameterAnnotationsAttribute; + } + + + /** + * Adds a given annotation to the annotations attribute. + */ + public void addAnnotation(int parameterIndex, Annotation annotation) + { + int annotationsCount = targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]; + Annotation[] annotations = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex]; + + // Make sure there is enough space for the new annotation. + if (annotations == null || + annotations.length <= annotationsCount) + { + targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex] = new Annotation[annotationsCount+1]; + if (annotations != null) + { + System.arraycopy(annotations, 0, + targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex], 0, + annotationsCount); + } + annotations = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex]; + } + + // Add the annotation. + annotations[targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]++] = annotation; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/src/proguard/classfile/editor/StackSizeUpdater.java new file mode 100644 index 0000000..94e0519 --- /dev/null +++ b/src/proguard/classfile/editor/StackSizeUpdater.java @@ -0,0 +1,54 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor computes and updates the maximum stack size of the + * code attributes that it visits. + * + * @author Eric Lafortune + */ +public class StackSizeUpdater +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final StackSizeComputer stackSizeComputer = new StackSizeComputer(); + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Compute the stack sizes. + stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); + + // Update the maximum stack size. + codeAttribute.u2maxStack = stackSizeComputer.getMaxStackSize(); + } +} diff --git a/src/proguard/classfile/editor/SubclassAdder.java b/src/proguard/classfile/editor/SubclassAdder.java new file mode 100644 index 0000000..6b9fd64 --- /dev/null +++ b/src/proguard/classfile/editor/SubclassAdder.java @@ -0,0 +1,59 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor adds the given class to the list of subclasses of the + * classes that it visits. + * + * @author Eric Lafortune + */ +public class SubclassAdder +implements ClassVisitor +{ + private final Clazz subclass; + + + /** + * Creates a new SubclassAdder that will add the given subclass. + */ + public SubclassAdder(Clazz subclass) + { + this.subclass = subclass; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.addSubClass(subclass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.addSubClass(subclass); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/SubclassToAdder.java b/src/proguard/classfile/editor/SubclassToAdder.java new file mode 100644 index 0000000..deb242f --- /dev/null +++ b/src/proguard/classfile/editor/SubclassToAdder.java @@ -0,0 +1,60 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor adds all classes that it visits to the list of subclasses + * of the given target class. + * + * @author Eric Lafortune + */ +public class SubclassToAdder +implements ClassVisitor +{ + private final Clazz targetClass; + + + /** + * Creates a new SubclassAdder that will add subclasses to the given + * target class. + */ + public SubclassToAdder(Clazz targetClass) + { + this.targetClass = targetClass; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + targetClass.addSubClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + targetClass.addSubClass(libraryClass); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/VariableCleaner.java b/src/proguard/classfile/editor/VariableCleaner.java new file mode 100644 index 0000000..1e93c15 --- /dev/null +++ b/src/proguard/classfile/editor/VariableCleaner.java @@ -0,0 +1,135 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.optimize.info.VariableUsageMarker; + +/** + * This AttributeVisitor cleans up unused variables in all attributes that it + * visits. + * + * @author Eric Lafortune + */ +public class VariableCleaner +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final VariableUsageMarker variableUsageMarker = new VariableUsageMarker(); + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Figure out the local variables that are used by the code. + variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute); + + // Clean up the variables of the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Clean up local variables that aren't used. + localVariableTableAttribute.u2localVariableTableLength = + removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength, + codeAttribute.u2maxLocals); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Clean up local variables that aren't used. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength, + codeAttribute.u2maxLocals); + } + + + // Small utility methods. + + /** + * Returns the given list of local variables, without the ones that aren't + * used + */ + private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount, + int maxLocals) + { + // Overwrite all empty local variable entries. + int newIndex = 0; + for (int index = 0; index < localVariableInfoCount && index < maxLocals; index++) + { + if (variableUsageMarker.isVariableUsed(index)) + { + localVariableInfos[newIndex++] = localVariableInfos[index]; + } + } + + // Clean up any remaining array elements. + for (int index = newIndex; index < localVariableInfoCount; index++) + { + localVariableInfos[index] = null; + } + + return newIndex; + } + + + /** + * Returns the given list of local variable types, without the ones that + * aren't used + */ + private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount, + int maxLocals) + { + // Overwrite all empty local variable type entries. + int newIndex = 0; + for (int index = 0; index < localVariableTypeInfoCount && index < maxLocals; index++) + { + if (variableUsageMarker.isVariableUsed(index)) + { + localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index]; + } + } + + // Clean up any remaining array elements. + for (int index = newIndex; index < localVariableTypeInfoCount; index++) + { + localVariableTypeInfos[index] = null; + } + + return newIndex; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java new file mode 100644 index 0000000..a583b49 --- /dev/null +++ b/src/proguard/classfile/editor/VariableEditor.java @@ -0,0 +1,129 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor accumulates specified changes to local variables, and + * then applies these accumulated changes to the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class VariableEditor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private boolean modified; + + private boolean[] deleted = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE]; + private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE]; + + private final VariableRemapper variableRemapper = new VariableRemapper(); + + + /** + * Resets the accumulated code changes. + * @param maxLocals the length of the local variable frame that will be + * edited next. + */ + public void reset(int maxLocals) + { + // Try to reuse the previous array. + if (deleted.length < maxLocals) + { + deleted = new boolean[maxLocals]; + } + else + { + for (int index = 0; index < maxLocals; index++) + { + deleted[index] = false; + } + } + + modified = false; + } + + + /** + * Remembers to delete the given variable. + * @param variableIndex the index of the variable to be deleted. + */ + public void deleteVariable(int variableIndex) + { + deleted[variableIndex] = true; + + modified = true; + } + + + /** + * Returns whether the given variable at the given offset has deleted. + */ + public boolean isDeleted(int instructionOffset) + { + return deleted[instructionOffset]; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Avoid doing any work if nothing is changing anyway. + if (!modified) + { + return; + } + + int oldMaxLocals = codeAttribute.u2maxLocals; + + // Make sure there is a sufficiently large variable map. + if (variableMap.length < oldMaxLocals) + { + variableMap = new int[oldMaxLocals]; + } + + // Fill out the variable map. + int newVariableIndex = 0; + for (int oldVariableIndex = 0; oldVariableIndex < oldMaxLocals; oldVariableIndex++) + { + variableMap[oldVariableIndex] = deleted[oldVariableIndex] ? + -1 : newVariableIndex++; + } + + // Set the map. + variableRemapper.setVariableMap(variableMap); + + // Remap the variables. + variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); + + // Update the length of local variable frame. + codeAttribute.u2maxLocals = newVariableIndex; + } +} diff --git a/src/proguard/classfile/editor/VariableRemapper.java b/src/proguard/classfile/editor/VariableRemapper.java new file mode 100644 index 0000000..590cd4e --- /dev/null +++ b/src/proguard/classfile/editor/VariableRemapper.java @@ -0,0 +1,197 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor remaps variable indexes in all attributes that it + * visits, based on a given index map. + * + * @author Eric Lafortune + */ +public class VariableRemapper +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + private int[] variableMap; + + + /** + * Sets the given mapping of old variable indexes to their new indexes. + * Variables that should disappear can be mapped to -1. + */ + public void setVariableMap(int[] variableMap) + { + this.variableMap = variableMap; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Initially, the code attribute editor doesn't contain any changes. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Remap the variables of the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Apply the code atribute editor. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + // Remap the variables of the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Remap the variable references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables that haven't been mapped. + localVariableTableAttribute.u2localVariableTableLength = + removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Remap the variable references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Remove local variables that haven't been mapped. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.u2index = + remapVariable(localVariableInfo.u2index); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.u2index = + remapVariable(localVariableTypeInfo.u2index); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Is the new variable index different from the original one? + int oldVariableIndex = variableInstruction.variableIndex; + int newVariableIndex = remapVariable(oldVariableIndex); + if (newVariableIndex != oldVariableIndex) + { + // Replace the instruction. + Instruction replacementInstruction = + new VariableInstruction(variableInstruction.opcode, + newVariableIndex, + variableInstruction.constant).shrink(); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + } + + + // Small utility methods. + + /** + * Returns the new variable index of the given variable. + */ + private int remapVariable(int variableIndex) + { + return variableMap[variableIndex]; + } + + + /** + * Returns the given list of local variables, without the ones that have + * been removed. + */ + private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount) + { + // Overwrite all empty local variable entries. + int newIndex = 0; + for (int index = 0; index < localVariableInfoCount; index++) + { + LocalVariableInfo localVariableInfo = localVariableInfos[index]; + if (localVariableInfo.u2index >= 0) + { + localVariableInfos[newIndex++] = localVariableInfo; + } + } + + return newIndex; + } + + + /** + * Returns the given list of local variable types, without the ones that + * have been removed. + */ + private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount) + { + // Overwrite all empty local variable type entries. + int newIndex = 0; + for (int index = 0; index < localVariableTypeInfoCount; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; + if (localVariableTypeInfo.u2index >= 0) + { + localVariableTypeInfos[newIndex++] = localVariableTypeInfo; + } + } + + return newIndex; + } +} diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/src/proguard/classfile/editor/VariableSizeUpdater.java new file mode 100644 index 0000000..18958c5 --- /dev/null +++ b/src/proguard/classfile/editor/VariableSizeUpdater.java @@ -0,0 +1,98 @@ +/* + * 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.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; + +/** + * This AttributeVisitor computes and updates the maximum local variable frame + * size of the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class VariableSizeUpdater +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { +// DEBUG = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + // The minimum variable size is determined by the arguments. + codeAttribute.u2maxLocals = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + if (DEBUG) + { + System.out.println("VariableSizeUpdater: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + System.out.println(" Max locals: "+codeAttribute.u2maxLocals+" <- parameters"); + } + + // Go over all instructions. + codeAttribute.instructionsAccept(clazz, method, this); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + int variableSize = variableInstruction.variableIndex + 1; + if (variableInstruction.isCategory2()) + { + variableSize++; + } + + if (codeAttribute.u2maxLocals < variableSize) + { + codeAttribute.u2maxLocals = variableSize; + + if (DEBUG) + { + System.out.println("Max locals: "+codeAttribute.u2maxLocals+" <- "+variableInstruction.toString(offset)); + } + } + } +} diff --git a/src/proguard/classfile/editor/package.html b/src/proguard/classfile/editor/package.html new file mode 100644 index 0000000..d37f541 --- /dev/null +++ b/src/proguard/classfile/editor/package.html @@ -0,0 +1,3 @@ +<body> +This package contains visitors to edit byte code. +</body> diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/src/proguard/classfile/instruction/BranchInstruction.java new file mode 100644 index 0000000..2baa917 --- /dev/null +++ b/src/proguard/classfile/instruction/BranchInstruction.java @@ -0,0 +1,180 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This interface describes an instruction that branches to a given offset in + * the code. + * + * @author Eric Lafortune + */ +public class BranchInstruction extends Instruction +{ + public int branchOffset; + + + /** + * Creates an uninitialized BranchInstruction. + */ + public BranchInstruction() {} + + + public BranchInstruction(byte opcode, int branchOffset) + { + this.opcode = opcode; + this.branchOffset = branchOffset; + } + + + /** + * Copies the given instruction into this instruction. + * @param branchInstruction the instruction to be copied. + * @return this instruction. + */ + public BranchInstruction copy(BranchInstruction branchInstruction) + { + this.opcode = branchInstruction.opcode; + this.branchOffset = branchInstruction.branchOffset; + + return this; + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // Remove the _w extension, if any. + switch (opcode) + { + case InstructionConstants.OP_GOTO_W: return InstructionConstants.OP_GOTO; + + case InstructionConstants.OP_JSR_W: return InstructionConstants.OP_JSR; + + default: return opcode; + } + } + + public Instruction shrink() + { + // Do we need an ordinary branch or a wide branch? + if (requiredBranchOffsetSize() == 2) + { + // Can we replace the wide branch by an ordinary branch? + if (opcode == InstructionConstants.OP_GOTO_W) + { + opcode = InstructionConstants.OP_GOTO; + } + else if (opcode == InstructionConstants.OP_JSR_W) + { + opcode = InstructionConstants.OP_JSR; + } + } + else + { + // Should we replace the ordinary branch by a wide branch? + if (opcode == InstructionConstants.OP_GOTO) + { + opcode = InstructionConstants.OP_GOTO_W; + } + else if (opcode == InstructionConstants.OP_JSR) + { + opcode = InstructionConstants.OP_JSR_W; + } + else + { + throw new IllegalArgumentException("Branch instruction can't be widened ("+this.toString()+")"); + } + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + branchOffset = readSignedValue(code, offset, branchOffsetSize()); + } + + + protected void writeInfo(byte[] code, int offset) + { + if (requiredBranchOffsetSize() > branchOffsetSize()) + { + throw new IllegalArgumentException("Instruction has invalid branch offset size ("+this.toString(offset)+")"); + } + + writeSignedValue(code, offset, branchOffset, branchOffsetSize()); + } + + + public int length(int offset) + { + return 1 + branchOffsetSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, this); + } + + + public String toString(int offset) + { + return "["+offset+"] "+toString()+" (target="+(offset+branchOffset)+")"; + } + + + // Implementations for Object. + + public String toString() + { + return getName()+" "+(branchOffset >= 0 ? "+" : "")+branchOffset; + } + + + // Small utility methods. + + /** + * Returns the branch offset size for this instruction. + */ + private int branchOffsetSize() + { + return opcode == InstructionConstants.OP_GOTO_W || + opcode == InstructionConstants.OP_JSR_W ? 4 : + 2; + } + + + /** + * Computes the required branch offset size for this instruction's branch + * offset. + */ + private int requiredBranchOffsetSize() + { + return branchOffset << 16 >> 16 == branchOffset ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/ConstantInstruction.java b/src/proguard/classfile/instruction/ConstantInstruction.java new file mode 100644 index 0000000..6c2d1a3 --- /dev/null +++ b/src/proguard/classfile/instruction/ConstantInstruction.java @@ -0,0 +1,303 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.ClassUtil; + +/** + * This Instruction represents an instruction that refers to an entry in the + * constant pool. + * + * @author Eric Lafortune + */ +public class ConstantInstruction extends Instruction +implements ConstantVisitor +{ + public int constantIndex; + public int constant; + + + // Fields acting as return parameters for the ConstantVisitor methods. + private int parameterStackDelta; + private int typeStackDelta; + + + /** + * Creates an uninitialized ConstantInstruction. + */ + public ConstantInstruction() {} + + + /** + * Creates a new ConstantInstruction with the given opcode and constant pool + * index. + */ + public ConstantInstruction(byte opcode, int constantIndex) + { + this(opcode, constantIndex, 0); + } + + + /** + * Creates a new ConstantInstruction with the given opcode, constant pool + * index, and constant. + */ + public ConstantInstruction(byte opcode, int constantIndex, int constant) + { + this.opcode = opcode; + this.constantIndex = constantIndex; + this.constant = constant; + } + + + /** + * Copies the given instruction into this instruction. + * @param constantInstruction the instruction to be copied. + * @return this instruction. + */ + public ConstantInstruction copy(ConstantInstruction constantInstruction) + { + this.opcode = constantInstruction.opcode; + this.constantIndex = constantInstruction.constantIndex; + this.constant = constantInstruction.constant; + + return this; + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // Remove the _w extension, if any. + switch (opcode) + { + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_LDC2_W: return InstructionConstants.OP_LDC; + + default: return opcode; + } + } + + public Instruction shrink() + { + // Do we need a short index or a long index? + if (requiredConstantIndexSize() == 1) + { + // Can we replace the long instruction by a short instruction? + if (opcode == InstructionConstants.OP_LDC_W) + { + opcode = InstructionConstants.OP_LDC; + } + } + else + { + // Should we replace the short instruction by a long instruction? + if (opcode == InstructionConstants.OP_LDC) + { + opcode = InstructionConstants.OP_LDC_W; + } + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + int constantIndexSize = constantIndexSize(); + int constantSize = constantSize(); + + constantIndex = readValue(code, offset, constantIndexSize); offset += constantIndexSize; + constant = readValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int constantIndexSize = constantIndexSize(); + int constantSize = constantSize(); + + if (requiredConstantIndexSize() > constantIndexSize) + { + throw new IllegalArgumentException("Instruction has invalid constant index size ("+this.toString(offset)+")"); + } + + writeValue(code, offset, constantIndex, constantIndexSize); offset += constantIndexSize; + writeValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return 1 + constantIndexSize() + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, this); + } + + + public int stackPopCount(Clazz clazz) + { + int stackPopCount = super.stackPopCount(clazz); + + // Some special cases. + switch (opcode) + { + case InstructionConstants.OP_MULTIANEWARRAY: + // For each dimension, an integer size is popped from the stack. + stackPopCount += constant; + break; + + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_PUTFIELD: + // The field value is be popped from the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPopCount += typeStackDelta; + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + // The some parameters may be popped from the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPopCount += parameterStackDelta; + break; + } + + return stackPopCount; + } + + + public int stackPushCount(Clazz clazz) + { + int stackPushCount = super.stackPushCount(clazz); + + // Some special cases. + switch (opcode) + { + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + // The field value or a return value may be pushed onto the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPushCount += typeStackDelta; + break; + } + + return stackPushCount; + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) {} + public void visitLongConstant(Clazz clazz, LongConstant longConstant) {} + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) {} + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) {} + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) {} + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) {} + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) {} + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) {} + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + String type = fieldrefConstant.getType(clazz); + + typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type)); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + visitRefConstant(clazz, interfaceMethodrefConstant); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + visitRefConstant(clazz, methodrefConstant); + } + + + private void visitRefConstant(Clazz clazz, RefConstant methodrefConstant) + { + String type = methodrefConstant.getType(clazz); + + parameterStackDelta = ClassUtil.internalMethodParameterSize(type); + typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type)); + } + + + // Implementations for Object. + + public String toString() + { + return getName()+" #"+constantIndex; + } + + + // Small utility methods. + + /** + * Returns the constant pool index size for this instruction. + */ + private int constantIndexSize() + { + return opcode == InstructionConstants.OP_LDC ? 1 : + 2; + } + + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode == InstructionConstants.OP_MULTIANEWARRAY ? 1 : + opcode == InstructionConstants.OP_INVOKEINTERFACE ? 2 : + 0; + } + + + /** + * Computes the required constant pool index size for this instruction's + * constant pool index. + */ + private int requiredConstantIndexSize() + { + return (constantIndex & 0xff) == constantIndex ? 1 : + (constantIndex & 0xffff) == constantIndex ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/Instruction.java b/src/proguard/classfile/instruction/Instruction.java new file mode 100644 index 0000000..8437495 --- /dev/null +++ b/src/proguard/classfile/instruction/Instruction.java @@ -0,0 +1,920 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * Base class for representing instructions. + * + * @author Eric Lafortune + */ +public abstract class Instruction +{ + // An array for marking Category 2 instructions. + private static final boolean[] IS_CATEGORY2 = new boolean[] + { + false, // nop + false, // aconst_null + false, // iconst_m1 + false, // iconst_0 + false, // iconst_1 + false, // iconst_2 + false, // iconst_3 + false, // iconst_4 + false, // iconst_5 + true, // lconst_0 + true, // lconst_1 + false, // fconst_0 + false, // fconst_1 + false, // fconst_2 + true, // dconst_0 + true, // dconst_1 + false, // bipush + false, // sipush + false, // ldc + false, // ldc_w + true, // ldc2_w + false, // iload + true, // lload + false, // fload + true, // dload + false, // aload + false, // iload_0 + false, // iload_1 + false, // iload_2 + false, // iload_3 + true, // lload_0 + true, // lload_1 + true, // lload_2 + true, // lload_3 + false, // fload_0 + false, // fload_1 + false, // fload_2 + false, // fload_3 + true, // dload_0 + true, // dload_1 + true, // dload_2 + true, // dload_3 + false, // aload_0 + false, // aload_1 + false, // aload_2 + false, // aload_3 + false, // iaload + true, // laload + false, // faload + true, // daload + false, // aaload + false, // baload + false, // caload + false, // saload + false, // istore + true, // lstore + false, // fstore + true, // dstore + false, // astore + false, // istore_0 + false, // istore_1 + false, // istore_2 + false, // istore_3 + true, // lstore_0 + true, // lstore_1 + true, // lstore_2 + true, // lstore_3 + false, // fstore_0 + false, // fstore_1 + false, // fstore_2 + false, // fstore_3 + true, // dstore_0 + true, // dstore_1 + true, // dstore_2 + true, // dstore_3 + false, // astore_0 + false, // astore_1 + false, // astore_2 + false, // astore_3 + false, // iastore + true, // lastore + false, // fastore + true, // dastore + false, // aastore + false, // bastore + false, // castore + false, // sastore + false, // pop + true, // pop2 + false, // dup + false, // dup_x1 + false, // dup_x2 + true, // dup2 + true, // dup2_x1 + true, // dup2_x2 + false, // swap + false, // iadd + true, // ladd + false, // fadd + true, // dadd + false, // isub + true, // lsub + false, // fsub + true, // dsub + false, // imul + true, // lmul + false, // fmul + true, // dmul + false, // idiv + true, // ldiv + false, // fdiv + true, // ddiv + false, // irem + true, // lrem + false, // frem + true, // drem + false, // ineg + true, // lneg + false, // fneg + true, // dneg + false, // ishl + true, // lshl + false, // ishr + true, // lshr + false, // iushr + true, // lushr + false, // iand + true, // land + false, // ior + true, // lor + false, // ixor + true, // lxor + false, // iinc + false, // i2l + false, // i2f + false, // i2d + true, // l2i + true, // l2f + true, // l2d + false, // f2i + false, // f2l + false, // f2d + true, // d2i + true, // d2l + true, // d2f + false, // i2b + false, // i2c + false, // i2s + true, // lcmp + false, // fcmpl + false, // fcmpg + true, // dcmpl + true, // dcmpg + false, // ifeq + false, // ifne + false, // iflt + false, // ifge + false, // ifgt + false, // ifle + false, // ificmpeq + false, // ificmpne + false, // ificmplt + false, // ificmpge + false, // ificmpgt + false, // ificmple + false, // ifacmpeq + false, // ifacmpne + false, // goto + false, // jsr + false, // ret + false, // tableswitch + false, // lookupswitch + false, // ireturn + true, // lreturn + false, // freturn + true, // dreturn + false, // areturn + false, // return + false, // getstatic + false, // putstatic + false, // getfield + false, // putfield + false, // invokevirtual + false, // invokespecial + false, // invokestatic + false, // invokeinterface + false, // unused + false, // new + false, // newarray + false, // anewarray + false, // arraylength + false, // athrow + false, // checkcast + false, // instanceof + false, // monitorenter + false, // monitorexit + false, // wide + false, // multianewarray + false, // ifnull + false, // ifnonnull + false, // goto_w + false, // jsr_w + }; + + + // An array containing the fixed number of entries popped from the stack, + // for all instructions. + private static final int[] STACK_POP_COUNTS = new int[] + { + 0, // nop + 0, // aconst_null + 0, // iconst_m1 + 0, // iconst_0 + 0, // iconst_1 + 0, // iconst_2 + 0, // iconst_3 + 0, // iconst_4 + 0, // iconst_5 + 0, // lconst_0 + 0, // lconst_1 + 0, // fconst_0 + 0, // fconst_1 + 0, // fconst_2 + 0, // dconst_0 + 0, // dconst_1 + 0, // bipush + 0, // sipush + 0, // ldc + 0, // ldc_w + 0, // ldc2_w + 0, // iload + 0, // lload + 0, // fload + 0, // dload + 0, // aload + 0, // iload_0 + 0, // iload_1 + 0, // iload_2 + 0, // iload_3 + 0, // lload_0 + 0, // lload_1 + 0, // lload_2 + 0, // lload_3 + 0, // fload_0 + 0, // fload_1 + 0, // fload_2 + 0, // fload_3 + 0, // dload_0 + 0, // dload_1 + 0, // dload_2 + 0, // dload_3 + 0, // aload_0 + 0, // aload_1 + 0, // aload_2 + 0, // aload_3 + 2, // iaload + 2, // laload + 2, // faload + 2, // daload + 2, // aaload + 2, // baload + 2, // caload + 2, // saload + 1, // istore + 2, // lstore + 1, // fstore + 2, // dstore + 1, // astore + 1, // istore_0 + 1, // istore_1 + 1, // istore_2 + 1, // istore_3 + 2, // lstore_0 + 2, // lstore_1 + 2, // lstore_2 + 2, // lstore_3 + 1, // fstore_0 + 1, // fstore_1 + 1, // fstore_2 + 1, // fstore_3 + 2, // dstore_0 + 2, // dstore_1 + 2, // dstore_2 + 2, // dstore_3 + 1, // astore_0 + 1, // astore_1 + 1, // astore_2 + 1, // astore_3 + 3, // iastore + 4, // lastore + 3, // fastore + 4, // dastore + 3, // aastore + 3, // bastore + 3, // castore + 3, // sastore + 1, // pop + 2, // pop2 + 1, // dup + 2, // dup_x1 + 3, // dup_x2 + 2, // dup2 + 3, // dup2_x1 + 4, // dup2_x2 + 2, // swap + 2, // iadd + 4, // ladd + 2, // fadd + 4, // dadd + 2, // isub + 4, // lsub + 2, // fsub + 4, // dsub + 2, // imul + 4, // lmul + 2, // fmul + 4, // dmul + 2, // idiv + 4, // ldiv + 2, // fdiv + 4, // ddiv + 2, // irem + 4, // lrem + 2, // frem + 4, // drem + 1, // ineg + 2, // lneg + 1, // fneg + 2, // dneg + 2, // ishl + 3, // lshl + 2, // ishr + 3, // lshr + 2, // iushr + 3, // lushr + 2, // iand + 4, // land + 2, // ior + 4, // lor + 2, // ixor + 4, // lxor + 0, // iinc + 1, // i2l + 1, // i2f + 1, // i2d + 2, // l2i + 2, // l2f + 2, // l2d + 1, // f2i + 1, // f2l + 1, // f2d + 2, // d2i + 2, // d2l + 2, // d2f + 1, // i2b + 1, // i2c + 1, // i2s + 4, // lcmp + 2, // fcmpl + 2, // fcmpg + 4, // dcmpl + 4, // dcmpg + 1, // ifeq + 1, // ifne + 1, // iflt + 1, // ifge + 1, // ifgt + 1, // ifle + 2, // ificmpeq + 2, // ificmpne + 2, // ificmplt + 2, // ificmpge + 2, // ificmpgt + 2, // ificmple + 2, // ifacmpeq + 2, // ifacmpne + 0, // goto + 0, // jsr + 0, // ret + 1, // tableswitch + 1, // lookupswitch + 1, // ireturn + 2, // lreturn + 1, // freturn + 2, // dreturn + 1, // areturn + 0, // return + 0, // getstatic + 0, // putstatic + 1, // getfield + 1, // putfield + 1, // invokevirtual + 1, // invokespecial + 0, // invokestatic + 1, // invokeinterface + 0, // unused + 0, // new + 1, // newarray + 1, // anewarray + 1, // arraylength + 1, // athrow + 1, // checkcast + 1, // instanceof + 1, // monitorenter + 1, // monitorexit + 0, // wide + 0, // multianewarray + 1, // ifnull + 1, // ifnonnull + 0, // goto_w + 0, // jsr_w + }; + + + // An array containing the fixed number of entries pushed onto the stack, + // for all instructions. + private static final int[] STACK_PUSH_COUNTS = new int[] + { + 0, // nop + 1, // aconst_null + 1, // iconst_m1 + 1, // iconst_0 + 1, // iconst_1 + 1, // iconst_2 + 1, // iconst_3 + 1, // iconst_4 + 1, // iconst_5 + 2, // lconst_0 + 2, // lconst_1 + 1, // fconst_0 + 1, // fconst_1 + 1, // fconst_2 + 2, // dconst_0 + 2, // dconst_1 + 1, // bipush + 1, // sipush + 1, // ldc + 1, // ldc_w + 2, // ldc2_w + 1, // iload + 2, // lload + 1, // fload + 2, // dload + 1, // aload + 1, // iload_0 + 1, // iload_1 + 1, // iload_2 + 1, // iload_3 + 2, // lload_0 + 2, // lload_1 + 2, // lload_2 + 2, // lload_3 + 1, // fload_0 + 1, // fload_1 + 1, // fload_2 + 1, // fload_3 + 2, // dload_0 + 2, // dload_1 + 2, // dload_2 + 2, // dload_3 + 1, // aload_0 + 1, // aload_1 + 1, // aload_2 + 1, // aload_3 + 1, // iaload + 2, // laload + 1, // faload + 2, // daload + 1, // aaload + 1, // baload + 1, // caload + 1, // saload + 0, // istore + 0, // lstore + 0, // fstore + 0, // dstore + 0, // astore + 0, // istore_0 + 0, // istore_1 + 0, // istore_2 + 0, // istore_3 + 0, // lstore_0 + 0, // lstore_1 + 0, // lstore_2 + 0, // lstore_3 + 0, // fstore_0 + 0, // fstore_1 + 0, // fstore_2 + 0, // fstore_3 + 0, // dstore_0 + 0, // dstore_1 + 0, // dstore_2 + 0, // dstore_3 + 0, // astore_0 + 0, // astore_1 + 0, // astore_2 + 0, // astore_3 + 0, // iastore + 0, // lastore + 0, // fastore + 0, // dastore + 0, // aastore + 0, // bastore + 0, // castore + 0, // sastore + 0, // pop + 0, // pop2 + 2, // dup + 3, // dup_x1 + 4, // dup_x2 + 4, // dup2 + 5, // dup2_x1 + 6, // dup2_x2 + 2, // swap + 1, // iadd + 2, // ladd + 1, // fadd + 2, // dadd + 1, // isub + 2, // lsub + 1, // fsub + 2, // dsub + 1, // imul + 2, // lmul + 1, // fmul + 2, // dmul + 1, // idiv + 2, // ldiv + 1, // fdiv + 2, // ddiv + 1, // irem + 2, // lrem + 1, // frem + 2, // drem + 1, // ineg + 2, // lneg + 1, // fneg + 2, // dneg + 1, // ishl + 2, // lshl + 1, // ishr + 2, // lshr + 1, // iushr + 2, // lushr + 1, // iand + 2, // land + 1, // ior + 2, // lor + 1, // ixor + 2, // lxor + 0, // iinc + 2, // i2l + 1, // i2f + 2, // i2d + 1, // l2i + 1, // l2f + 2, // l2d + 1, // f2i + 2, // f2l + 2, // f2d + 1, // d2i + 2, // d2l + 1, // d2f + 1, // i2b + 1, // i2c + 1, // i2s + 1, // lcmp + 1, // fcmpl + 1, // fcmpg + 1, // dcmpl + 1, // dcmpg + 0, // ifeq + 0, // ifne + 0, // iflt + 0, // ifge + 0, // ifgt + 0, // ifle + 0, // ificmpeq + 0, // ificmpne + 0, // ificmplt + 0, // ificmpge + 0, // ificmpgt + 0, // ificmple + 0, // ifacmpeq + 0, // ifacmpne + 0, // goto + 1, // jsr + 0, // ret + 0, // tableswitch + 0, // lookupswitch + 0, // ireturn + 0, // lreturn + 0, // freturn + 0, // dreturn + 0, // areturn + 0, // return + 0, // getstatic + 0, // putstatic + 0, // getfield + 0, // putfield + 0, // invokevirtual + 0, // invokespecial + 0, // invokestatic + 0, // invokeinterface + 0, // unused + 1, // new + 1, // newarray + 1, // anewarray + 1, // arraylength + 0, // athrow + 1, // checkcast + 1, // instanceof + 0, // monitorenter + 0, // monitorexit + 0, // wide + 1, // multianewarray + 0, // ifnull + 0, // ifnonnull + 0, // goto_w + 1, // jsr_w + }; + + + public byte opcode; + + + /** + * Returns the canonical opcode of this instruction, i.e. typically the + * opcode whose extension has been removed. + */ + public byte canonicalOpcode() + { + return opcode; + } + + + /** + * Shrinks this instruction to its shortest possible form. + * @return this instruction. + */ + public abstract Instruction shrink(); + + + + /** + * Writes the Instruction at the given offset in the given code attribute. + */ + public final void write(CodeAttribute codeAttribute, int offset) + { + write(codeAttribute.code, offset); + } + + + /** + * Writes the Instruction at the given offset in the given code array. + */ + public void write(byte[] code, int offset) + { + // Write the wide opcode, if necessary. + if (isWide()) + { + code[offset++] = InstructionConstants.OP_WIDE; + } + + // Write the opcode. + code[offset++] = opcode; + + // Write any additional arguments. + writeInfo(code, offset); + } + + + /** + * Returns whether the instruction is wide, i.e. preceded by a wide opcode. + * With the current specifications, only variable instructions can be wide. + */ + protected boolean isWide() + { + return false; + } + + + /** + * Reads the data following the instruction opcode. + */ + protected abstract void readInfo(byte[] code, int offset); + + + /** + * Writes data following the instruction opcode. + */ + protected abstract void writeInfo(byte[] code, int offset); + + + /** + * Returns the length in bytes of the instruction. + */ + public abstract int length(int offset); + + + /** + * Accepts the given visitor. + */ + public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor); + + + /** + * Returns a description of the instruction, at the given offset. + */ + public String toString(int offset) + { + return "["+offset+"] "+ this.toString(); + } + + + /** + * Returns the name of the instruction. + */ + public String getName() + { + return InstructionConstants.NAMES[opcode & 0xff]; + } + + + /** + * Returns whether the instruction is a Category 2 instruction. This means + * that it operates on long or double arguments. + */ + public boolean isCategory2() + { + return IS_CATEGORY2[opcode & 0xff]; + } + + + /** + * Returns the number of entries popped from the stack during the execution + * of the instruction. + */ + public int stackPopCount(Clazz clazz) + { + return STACK_POP_COUNTS[opcode & 0xff]; + } + + + /** + * Returns the number of entries pushed onto the stack during the execution + * of the instruction. + */ + public int stackPushCount(Clazz clazz) + { + return STACK_PUSH_COUNTS[opcode & 0xff]; + } + + + // Small utility methods. + + protected static int readByte(byte[] code, int offset) + { + return code[offset] & 0xff; + } + + protected static int readShort(byte[] code, int offset) + { + return ((code[offset++] & 0xff) << 8) | + ( code[offset ] & 0xff ); + } + + protected static int readInt(byte[] code, int offset) + { + return ( code[offset++] << 24) | + ((code[offset++] & 0xff) << 16) | + ((code[offset++] & 0xff) << 8) | + ( code[offset ] & 0xff ); + } + + protected static int readValue(byte[] code, int offset, int valueSize) + { + switch (valueSize) + { + case 0: return 0; + case 1: return readByte( code, offset); + case 2: return readShort(code, offset); + case 4: return readInt( code, offset); + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static int readSignedByte(byte[] code, int offset) + { + return code[offset]; + } + + protected static int readSignedShort(byte[] code, int offset) + { + return (code[offset++] << 8) | + (code[offset ] & 0xff); + } + + protected static int readSignedValue(byte[] code, int offset, int valueSize) + { + switch (valueSize) + { + case 0: return 0; + case 1: return readSignedByte( code, offset); + case 2: return readSignedShort(code, offset); + case 4: return readInt( code, offset); + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static void writeByte(byte[] code, int offset, int value) + { + if (value > 0xff) + { + throw new IllegalArgumentException("Unsigned byte value larger than 0xff ["+value+"]"); + } + + code[offset] = (byte)value; + } + + protected static void writeShort(byte[] code, int offset, int value) + { + if (value > 0xffff) + { + throw new IllegalArgumentException("Unsigned short value larger than 0xffff ["+value+"]"); + } + + code[offset++] = (byte)(value >> 8); + code[offset ] = (byte)(value ); + } + + protected static void writeInt(byte[] code, int offset, int value) + { + code[offset++] = (byte)(value >> 24); + code[offset++] = (byte)(value >> 16); + code[offset++] = (byte)(value >> 8); + code[offset ] = (byte)(value ); + } + + protected static void writeValue(byte[] code, int offset, int value, int valueSize) + { + switch (valueSize) + { + case 0: break; + case 1: writeByte( code, offset, value); break; + case 2: writeShort(code, offset, value); break; + case 4: writeInt( code, offset, value); break; + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static void writeSignedByte(byte[] code, int offset, int value) + { + if (value << 24 >> 24 != value) + { + throw new IllegalArgumentException("Signed byte value out of range ["+value+"]"); + } + + code[offset] = (byte)value; + } + + protected static void writeSignedShort(byte[] code, int offset, int value) + { + if (value << 16 >> 16 != value) + { + throw new IllegalArgumentException("Signed short value out of range ["+value+"]"); + } + + code[offset++] = (byte)(value >> 8); + code[offset ] = (byte)(value ); + } + + protected static void writeSignedValue(byte[] code, int offset, int value, int valueSize) + { + switch (valueSize) + { + case 0: break; + case 1: writeSignedByte( code, offset, value); break; + case 2: writeSignedShort(code, offset, value); break; + case 4: writeInt( code, offset, value); break; + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } +} diff --git a/src/proguard/classfile/instruction/InstructionConstants.java b/src/proguard/classfile/instruction/InstructionConstants.java new file mode 100644 index 0000000..78730b3 --- /dev/null +++ b/src/proguard/classfile/instruction/InstructionConstants.java @@ -0,0 +1,449 @@ +/* + * 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.classfile.instruction; + +/** + * Representation of an instruction. + * + * @author Eric Lafortune + */ +public interface InstructionConstants +{ + public static final byte OP_NOP = 0; + public static final byte OP_ACONST_NULL = 1; + public static final byte OP_ICONST_M1 = 2; + public static final byte OP_ICONST_0 = 3; + public static final byte OP_ICONST_1 = 4; + public static final byte OP_ICONST_2 = 5; + public static final byte OP_ICONST_3 = 6; + public static final byte OP_ICONST_4 = 7; + public static final byte OP_ICONST_5 = 8; + public static final byte OP_LCONST_0 = 9; + public static final byte OP_LCONST_1 = 10; + public static final byte OP_FCONST_0 = 11; + public static final byte OP_FCONST_1 = 12; + public static final byte OP_FCONST_2 = 13; + public static final byte OP_DCONST_0 = 14; + public static final byte OP_DCONST_1 = 15; + public static final byte OP_BIPUSH = 16; + public static final byte OP_SIPUSH = 17; + public static final byte OP_LDC = 18; + public static final byte OP_LDC_W = 19; + public static final byte OP_LDC2_W = 20; + public static final byte OP_ILOAD = 21; + public static final byte OP_LLOAD = 22; + public static final byte OP_FLOAD = 23; + public static final byte OP_DLOAD = 24; + public static final byte OP_ALOAD = 25; + public static final byte OP_ILOAD_0 = 26; + public static final byte OP_ILOAD_1 = 27; + public static final byte OP_ILOAD_2 = 28; + public static final byte OP_ILOAD_3 = 29; + public static final byte OP_LLOAD_0 = 30; + public static final byte OP_LLOAD_1 = 31; + public static final byte OP_LLOAD_2 = 32; + public static final byte OP_LLOAD_3 = 33; + public static final byte OP_FLOAD_0 = 34; + public static final byte OP_FLOAD_1 = 35; + public static final byte OP_FLOAD_2 = 36; + public static final byte OP_FLOAD_3 = 37; + public static final byte OP_DLOAD_0 = 38; + public static final byte OP_DLOAD_1 = 39; + public static final byte OP_DLOAD_2 = 40; + public static final byte OP_DLOAD_3 = 41; + public static final byte OP_ALOAD_0 = 42; + public static final byte OP_ALOAD_1 = 43; + public static final byte OP_ALOAD_2 = 44; + public static final byte OP_ALOAD_3 = 45; + public static final byte OP_IALOAD = 46; + public static final byte OP_LALOAD = 47; + public static final byte OP_FALOAD = 48; + public static final byte OP_DALOAD = 49; + public static final byte OP_AALOAD = 50; + public static final byte OP_BALOAD = 51; + public static final byte OP_CALOAD = 52; + public static final byte OP_SALOAD = 53; + public static final byte OP_ISTORE = 54; + public static final byte OP_LSTORE = 55; + public static final byte OP_FSTORE = 56; + public static final byte OP_DSTORE = 57; + public static final byte OP_ASTORE = 58; + public static final byte OP_ISTORE_0 = 59; + public static final byte OP_ISTORE_1 = 60; + public static final byte OP_ISTORE_2 = 61; + public static final byte OP_ISTORE_3 = 62; + public static final byte OP_LSTORE_0 = 63; + public static final byte OP_LSTORE_1 = 64; + public static final byte OP_LSTORE_2 = 65; + public static final byte OP_LSTORE_3 = 66; + public static final byte OP_FSTORE_0 = 67; + public static final byte OP_FSTORE_1 = 68; + public static final byte OP_FSTORE_2 = 69; + public static final byte OP_FSTORE_3 = 70; + public static final byte OP_DSTORE_0 = 71; + public static final byte OP_DSTORE_1 = 72; + public static final byte OP_DSTORE_2 = 73; + public static final byte OP_DSTORE_3 = 74; + public static final byte OP_ASTORE_0 = 75; + public static final byte OP_ASTORE_1 = 76; + public static final byte OP_ASTORE_2 = 77; + public static final byte OP_ASTORE_3 = 78; + public static final byte OP_IASTORE = 79; + public static final byte OP_LASTORE = 80; + public static final byte OP_FASTORE = 81; + public static final byte OP_DASTORE = 82; + public static final byte OP_AASTORE = 83; + public static final byte OP_BASTORE = 84; + public static final byte OP_CASTORE = 85; + public static final byte OP_SASTORE = 86; + public static final byte OP_POP = 87; + public static final byte OP_POP2 = 88; + public static final byte OP_DUP = 89; + public static final byte OP_DUP_X1 = 90; + public static final byte OP_DUP_X2 = 91; + public static final byte OP_DUP2 = 92; + public static final byte OP_DUP2_X1 = 93; + public static final byte OP_DUP2_X2 = 94; + public static final byte OP_SWAP = 95; + public static final byte OP_IADD = 96; + public static final byte OP_LADD = 97; + public static final byte OP_FADD = 98; + public static final byte OP_DADD = 99; + public static final byte OP_ISUB = 100; + public static final byte OP_LSUB = 101; + public static final byte OP_FSUB = 102; + public static final byte OP_DSUB = 103; + public static final byte OP_IMUL = 104; + public static final byte OP_LMUL = 105; + public static final byte OP_FMUL = 106; + public static final byte OP_DMUL = 107; + public static final byte OP_IDIV = 108; + public static final byte OP_LDIV = 109; + public static final byte OP_FDIV = 110; + public static final byte OP_DDIV = 111; + public static final byte OP_IREM = 112; + public static final byte OP_LREM = 113; + public static final byte OP_FREM = 114; + public static final byte OP_DREM = 115; + public static final byte OP_INEG = 116; + public static final byte OP_LNEG = 117; + public static final byte OP_FNEG = 118; + public static final byte OP_DNEG = 119; + public static final byte OP_ISHL = 120; + public static final byte OP_LSHL = 121; + public static final byte OP_ISHR = 122; + public static final byte OP_LSHR = 123; + public static final byte OP_IUSHR = 124; + public static final byte OP_LUSHR = 125; + public static final byte OP_IAND = 126; + public static final byte OP_LAND = 127; + public static final byte OP_IOR = -128; + public static final byte OP_LOR = -127; + public static final byte OP_IXOR = -126; + public static final byte OP_LXOR = -125; + public static final byte OP_IINC = -124; + public static final byte OP_I2L = -123; + public static final byte OP_I2F = -122; + public static final byte OP_I2D = -121; + public static final byte OP_L2I = -120; + public static final byte OP_L2F = -119; + public static final byte OP_L2D = -118; + public static final byte OP_F2I = -117; + public static final byte OP_F2L = -116; + public static final byte OP_F2D = -115; + public static final byte OP_D2I = -114; + public static final byte OP_D2L = -113; + public static final byte OP_D2F = -112; + public static final byte OP_I2B = -111; + public static final byte OP_I2C = -110; + public static final byte OP_I2S = -109; + public static final byte OP_LCMP = -108; + public static final byte OP_FCMPL = -107; + public static final byte OP_FCMPG = -106; + public static final byte OP_DCMPL = -105; + public static final byte OP_DCMPG = -104; + public static final byte OP_IFEQ = -103; + public static final byte OP_IFNE = -102; + public static final byte OP_IFLT = -101; + public static final byte OP_IFGE = -100; + public static final byte OP_IFGT = -99; + public static final byte OP_IFLE = -98; + public static final byte OP_IFICMPEQ = -97; + public static final byte OP_IFICMPNE = -96; + public static final byte OP_IFICMPLT = -95; + public static final byte OP_IFICMPGE = -94; + public static final byte OP_IFICMPGT = -93; + public static final byte OP_IFICMPLE = -92; + public static final byte OP_IFACMPEQ = -91; + public static final byte OP_IFACMPNE = -90; + public static final byte OP_GOTO = -89; + public static final byte OP_JSR = -88; + public static final byte OP_RET = -87; + public static final byte OP_TABLESWITCH = -86; + public static final byte OP_LOOKUPSWITCH = -85; + public static final byte OP_IRETURN = -84; + public static final byte OP_LRETURN = -83; + public static final byte OP_FRETURN = -82; + public static final byte OP_DRETURN = -81; + public static final byte OP_ARETURN = -80; + public static final byte OP_RETURN = -79; + public static final byte OP_GETSTATIC = -78; + public static final byte OP_PUTSTATIC = -77; + public static final byte OP_GETFIELD = -76; + public static final byte OP_PUTFIELD = -75; + public static final byte OP_INVOKEVIRTUAL = -74; + public static final byte OP_INVOKESPECIAL = -73; + public static final byte OP_INVOKESTATIC = -72; + public static final byte OP_INVOKEINTERFACE = -71; +// public static final byte OP_UNUSED = -70; + public static final byte OP_NEW = -69; + public static final byte OP_NEWARRAY = -68; + public static final byte OP_ANEWARRAY = -67; + public static final byte OP_ARRAYLENGTH = -66; + public static final byte OP_ATHROW = -65; + public static final byte OP_CHECKCAST = -64; + public static final byte OP_INSTANCEOF = -63; + public static final byte OP_MONITORENTER = -62; + public static final byte OP_MONITOREXIT = -61; + public static final byte OP_WIDE = -60; + public static final byte OP_MULTIANEWARRAY = -59; + public static final byte OP_IFNULL = -58; + public static final byte OP_IFNONNULL = -57; + public static final byte OP_GOTO_W = -56; + public static final byte OP_JSR_W = -55; + + + public static final String[] NAMES = + { + "nop", + "aconst_null", + "iconst_m1", + "iconst_0", + "iconst_1", + "iconst_2", + "iconst_3", + "iconst_4", + "iconst_5", + "lconst_0", + "lconst_1", + "fconst_0", + "fconst_1", + "fconst_2", + "dconst_0", + "dconst_1", + "bipush", + "sipush", + "ldc", + "ldc_w", + "ldc2_w", + "iload", + "lload", + "fload", + "dload", + "aload", + "iload_0", + "iload_1", + "iload_2", + "iload_3", + "lload_0", + "lload_1", + "lload_2", + "lload_3", + "fload_0", + "fload_1", + "fload_2", + "fload_3", + "dload_0", + "dload_1", + "dload_2", + "dload_3", + "aload_0", + "aload_1", + "aload_2", + "aload_3", + "iaload", + "laload", + "faload", + "daload", + "aaload", + "baload", + "caload", + "saload", + "istore", + "lstore", + "fstore", + "dstore", + "astore", + "istore_0", + "istore_1", + "istore_2", + "istore_3", + "lstore_0", + "lstore_1", + "lstore_2", + "lstore_3", + "fstore_0", + "fstore_1", + "fstore_2", + "fstore_3", + "dstore_0", + "dstore_1", + "dstore_2", + "dstore_3", + "astore_0", + "astore_1", + "astore_2", + "astore_3", + "iastore", + "lastore", + "fastore", + "dastore", + "aastore", + "bastore", + "castore", + "sastore", + "pop", + "pop2", + "dup", + "dup_x1", + "dup_x2", + "dup2", + "dup2_x1", + "dup2_x2", + "swap", + "iadd", + "ladd", + "fadd", + "dadd", + "isub", + "lsub", + "fsub", + "dsub", + "imul", + "lmul", + "fmul", + "dmul", + "idiv", + "ldiv", + "fdiv", + "ddiv", + "irem", + "lrem", + "frem", + "drem", + "ineg", + "lneg", + "fneg", + "dneg", + "ishl", + "lshl", + "ishr", + "lshr", + "iushr", + "lushr", + "iand", + "land", + "ior", + "lor", + "ixor", + "lxor", + "iinc", + "i2l", + "i2f", + "i2d", + "l2i", + "l2f", + "l2d", + "f2i", + "f2l", + "f2d", + "d2i", + "d2l", + "d2f", + "i2b", + "i2c", + "i2s", + "lcmp", + "fcmpl", + "fcmpg", + "dcmpl", + "dcmpg", + "ifeq", + "ifne", + "iflt", + "ifge", + "ifgt", + "ifle", + "ificmpeq", + "ificmpne", + "ificmplt", + "ificmpge", + "ificmpgt", + "ificmple", + "ifacmpeq", + "ifacmpne", + "goto", + "jsr", + "ret", + "tableswitch", + "lookupswitch", + "ireturn", + "lreturn", + "freturn", + "dreturn", + "areturn", + "return", + "getstatic", + "putstatic", + "getfield", + "putfield", + "invokevirtual", + "invokespecial", + "invokestatic", + "invokeinterface", + "unused", + "new", + "newarray", + "anewarray", + "arraylength", + "athrow", + "checkcast", + "instanceof", + "monitorenter", + "monitorexit", + "wide", + "multianewarray", + "ifnull", + "ifnonnull", + "goto_w", + "jsr_w", + }; + + + public static final byte ARRAY_T_BOOLEAN = 4; + public static final byte ARRAY_T_CHAR = 5; + public static final byte ARRAY_T_FLOAT = 6; + public static final byte ARRAY_T_DOUBLE = 7; + public static final byte ARRAY_T_BYTE = 8; + public static final byte ARRAY_T_SHORT = 9; + public static final byte ARRAY_T_INT = 10; + public static final byte ARRAY_T_LONG = 11; +} diff --git a/src/proguard/classfile/instruction/InstructionFactory.java b/src/proguard/classfile/instruction/InstructionFactory.java new file mode 100644 index 0000000..f898471 --- /dev/null +++ b/src/proguard/classfile/instruction/InstructionFactory.java @@ -0,0 +1,299 @@ +/* + * 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.classfile.instruction; + +/** + * This class provides methods to create and reuse Instruction objects. + * + * @author Eric Lafortune + */ +public class InstructionFactory +{ + /** + * Creates a new Instruction from the data in the byte array, starting + * at the given index. + */ + public static Instruction create(byte[] code, int offset) + { + Instruction instruction; + + int index = offset; + byte opcode = code[index++]; + + boolean wide = false; + if (opcode == InstructionConstants.OP_WIDE) + { + opcode = code[index++]; + wide = true; + } + + switch (opcode) + { + // Simple instructions. + case InstructionConstants.OP_NOP: + case InstructionConstants.OP_ACONST_NULL: + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_LCONST_0: + case InstructionConstants.OP_LCONST_1: + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: + + case InstructionConstants.OP_BIPUSH: + case InstructionConstants.OP_SIPUSH: + + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_POP: + case InstructionConstants.OP_POP2: + case InstructionConstants.OP_DUP: + case InstructionConstants.OP_DUP_X1: + case InstructionConstants.OP_DUP_X2: + case InstructionConstants.OP_DUP2: + case InstructionConstants.OP_DUP2_X1: + case InstructionConstants.OP_DUP2_X2: + case InstructionConstants.OP_SWAP: + case InstructionConstants.OP_IADD: + case InstructionConstants.OP_LADD: + case InstructionConstants.OP_FADD: + case InstructionConstants.OP_DADD: + case InstructionConstants.OP_ISUB: + case InstructionConstants.OP_LSUB: + case InstructionConstants.OP_FSUB: + case InstructionConstants.OP_DSUB: + case InstructionConstants.OP_IMUL: + case InstructionConstants.OP_LMUL: + case InstructionConstants.OP_FMUL: + case InstructionConstants.OP_DMUL: + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_FDIV: + case InstructionConstants.OP_DDIV: + case InstructionConstants.OP_IREM: + case InstructionConstants.OP_LREM: + case InstructionConstants.OP_FREM: + case InstructionConstants.OP_DREM: + case InstructionConstants.OP_INEG: + case InstructionConstants.OP_LNEG: + case InstructionConstants.OP_FNEG: + case InstructionConstants.OP_DNEG: + case InstructionConstants.OP_ISHL: + case InstructionConstants.OP_LSHL: + case InstructionConstants.OP_ISHR: + case InstructionConstants.OP_LSHR: + case InstructionConstants.OP_IUSHR: + case InstructionConstants.OP_LUSHR: + case InstructionConstants.OP_IAND: + case InstructionConstants.OP_LAND: + case InstructionConstants.OP_IOR: + case InstructionConstants.OP_LOR: + case InstructionConstants.OP_IXOR: + case InstructionConstants.OP_LXOR: + + case InstructionConstants.OP_I2L: + case InstructionConstants.OP_I2F: + case InstructionConstants.OP_I2D: + case InstructionConstants.OP_L2I: + case InstructionConstants.OP_L2F: + case InstructionConstants.OP_L2D: + case InstructionConstants.OP_F2I: + case InstructionConstants.OP_F2L: + case InstructionConstants.OP_F2D: + case InstructionConstants.OP_D2I: + case InstructionConstants.OP_D2L: + case InstructionConstants.OP_D2F: + case InstructionConstants.OP_I2B: + case InstructionConstants.OP_I2C: + case InstructionConstants.OP_I2S: + case InstructionConstants.OP_LCMP: + case InstructionConstants.OP_FCMPL: + case InstructionConstants.OP_FCMPG: + case InstructionConstants.OP_DCMPL: + case InstructionConstants.OP_DCMPG: + + case InstructionConstants.OP_IRETURN: + case InstructionConstants.OP_LRETURN: + case InstructionConstants.OP_FRETURN: + case InstructionConstants.OP_DRETURN: + case InstructionConstants.OP_ARETURN: + case InstructionConstants.OP_RETURN: + + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ARRAYLENGTH: + case InstructionConstants.OP_ATHROW: + + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + instruction = new SimpleInstruction(); + break; + + // Instructions with a contant pool index. + case InstructionConstants.OP_LDC: + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_LDC2_W: + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + + case InstructionConstants.OP_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_INSTANCEOF: + case InstructionConstants.OP_MULTIANEWARRAY: + instruction = new ConstantInstruction(); + break; + + // Instructions with a local variable index. + case InstructionConstants.OP_ILOAD: + case InstructionConstants.OP_LLOAD: + case InstructionConstants.OP_FLOAD: + case InstructionConstants.OP_DLOAD: + case InstructionConstants.OP_ALOAD: + case InstructionConstants.OP_ILOAD_0: + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_ILOAD_3: + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_LLOAD_3: + case InstructionConstants.OP_FLOAD_0: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_FLOAD_3: + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_DLOAD_3: + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: + + case InstructionConstants.OP_ISTORE: + case InstructionConstants.OP_LSTORE: + case InstructionConstants.OP_FSTORE: + case InstructionConstants.OP_DSTORE: + case InstructionConstants.OP_ASTORE: + case InstructionConstants.OP_ISTORE_0: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_ISTORE_2: + case InstructionConstants.OP_ISTORE_3: + case InstructionConstants.OP_LSTORE_0: + case InstructionConstants.OP_LSTORE_1: + case InstructionConstants.OP_LSTORE_2: + case InstructionConstants.OP_LSTORE_3: + case InstructionConstants.OP_FSTORE_0: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_FSTORE_2: + case InstructionConstants.OP_FSTORE_3: + case InstructionConstants.OP_DSTORE_0: + case InstructionConstants.OP_DSTORE_1: + case InstructionConstants.OP_DSTORE_2: + case InstructionConstants.OP_DSTORE_3: + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: + + case InstructionConstants.OP_IINC: + + case InstructionConstants.OP_RET: + instruction = new VariableInstruction(wide); + break; + + // Instructions with a branch offset operand. + case InstructionConstants.OP_IFEQ: + case InstructionConstants.OP_IFNE: + case InstructionConstants.OP_IFLT: + case InstructionConstants.OP_IFGE: + case InstructionConstants.OP_IFGT: + case InstructionConstants.OP_IFLE: + case InstructionConstants.OP_IFICMPEQ: + case InstructionConstants.OP_IFICMPNE: + case InstructionConstants.OP_IFICMPLT: + case InstructionConstants.OP_IFICMPGE: + case InstructionConstants.OP_IFICMPGT: + case InstructionConstants.OP_IFICMPLE: + case InstructionConstants.OP_IFACMPEQ: + case InstructionConstants.OP_IFACMPNE: + case InstructionConstants.OP_GOTO: + case InstructionConstants.OP_JSR: + + case InstructionConstants.OP_IFNULL: + case InstructionConstants.OP_IFNONNULL: + + case InstructionConstants.OP_GOTO_W: + case InstructionConstants.OP_JSR_W: + instruction = new BranchInstruction(); + break; + + // The tableswitch instruction. + case InstructionConstants.OP_TABLESWITCH: + instruction = new TableSwitchInstruction(); + break; + + // The lookupswitch instruction. + case InstructionConstants.OP_LOOKUPSWITCH: + instruction = new LookUpSwitchInstruction(); + break; + + default: + throw new IllegalArgumentException("Unknown instruction opcode ["+opcode+"] at offset "+offset); + } + + instruction.opcode = opcode; + + instruction.readInfo(code, index); + + return instruction; + } +} diff --git a/src/proguard/classfile/instruction/InstructionUtil.java b/src/proguard/classfile/instruction/InstructionUtil.java new file mode 100644 index 0000000..a3a328a --- /dev/null +++ b/src/proguard/classfile/instruction/InstructionUtil.java @@ -0,0 +1,67 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.ClassConstants; + +/** + * Utility methods for converting between representations of names and + * descriptions. + * + * @author Eric Lafortune + */ +public class InstructionUtil +{ + /** + * Returns the internal type corresponding to the given 'newarray' type. + * @param arrayType <code>InstructionConstants.ARRAY_T_BOOLEAN</code>, + * <code>InstructionConstants.ARRAY_T_BYTE</code>, + * <code>InstructionConstants.ARRAY_T_CHAR</code>, + * <code>InstructionConstants.ARRAY_T_SHORT</code>, + * <code>InstructionConstants.ARRAY_T_INT</code>, + * <code>InstructionConstants.ARRAY_T_LONG</code>, + * <code>InstructionConstants.ARRAY_T_FLOAT</code>, or + * <code>InstructionConstants.ARRAY_T_DOUBLE</code>. + * @return <code>ClassConstants.INTERNAL_TYPE_BOOLEAN</code>, + * <code>ClassConstants.INTERNAL_TYPE_BYTE</code>, + * <code>ClassConstants.INTERNAL_TYPE_CHAR</code>, + * <code>ClassConstants.INTERNAL_TYPE_SHORT</code>, + * <code>ClassConstants.INTERNAL_TYPE_INT</code>, + * <code>ClassConstants.INTERNAL_TYPE_LONG</code>, + * <code>ClassConstants.INTERNAL_TYPE_FLOAT</code>, or + * <code>ClassConstants.INTERNAL_TYPE_DOUBLE</code>. + */ + public static char internalTypeFromArrayType(byte arrayType) + { + switch (arrayType) + { + case InstructionConstants.ARRAY_T_BOOLEAN: return ClassConstants.INTERNAL_TYPE_BOOLEAN; + case InstructionConstants.ARRAY_T_CHAR: return ClassConstants.INTERNAL_TYPE_CHAR; + case InstructionConstants.ARRAY_T_FLOAT: return ClassConstants.INTERNAL_TYPE_FLOAT; + case InstructionConstants.ARRAY_T_DOUBLE: return ClassConstants.INTERNAL_TYPE_DOUBLE; + case InstructionConstants.ARRAY_T_BYTE: return ClassConstants.INTERNAL_TYPE_BYTE; + case InstructionConstants.ARRAY_T_SHORT: return ClassConstants.INTERNAL_TYPE_SHORT; + case InstructionConstants.ARRAY_T_INT: return ClassConstants.INTERNAL_TYPE_INT; + case InstructionConstants.ARRAY_T_LONG: return ClassConstants.INTERNAL_TYPE_LONG; + default: throw new IllegalArgumentException("Unknown array type ["+arrayType+"]"); + } + } +} diff --git a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java new file mode 100644 index 0000000..178cce5 --- /dev/null +++ b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java @@ -0,0 +1,135 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class LookUpSwitchInstruction extends SwitchInstruction +{ + public int[] cases; + + + /** + * Creates an uninitialized LookUpSwitchInstruction. + */ + public LookUpSwitchInstruction() {} + + + /** + * Creates a new LookUpSwitchInstruction with the given arguments. + */ + public LookUpSwitchInstruction(byte opcode, + int defaultOffset, + int[] cases, + int[] jumpOffsets) + { + this.opcode = opcode; + this.defaultOffset = defaultOffset; + this.cases = cases; + this.jumpOffsets = jumpOffsets; + } + + + /** + * Copies the given instruction into this instruction. + * @param lookUpSwitchInstruction the instruction to be copied. + * @return this instruction. + */ + public LookUpSwitchInstruction copy(LookUpSwitchInstruction lookUpSwitchInstruction) + { + this.opcode = lookUpSwitchInstruction.opcode; + this.defaultOffset = lookUpSwitchInstruction.defaultOffset; + this.cases = lookUpSwitchInstruction.cases; + this.jumpOffsets = lookUpSwitchInstruction.jumpOffsets; + + return this; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + // There aren't any ways to shrink this instruction. + return this; + } + + protected void readInfo(byte[] code, int offset) + { + // Skip up to three padding bytes. + offset += -offset & 3; + + // Read the two 32-bit arguments. + defaultOffset = readInt(code, offset); offset += 4; + int jumpOffsetCount = readInt(code, offset); offset += 4; + + // Read the matches-offset pairs. + cases = new int[jumpOffsetCount]; + jumpOffsets = new int[jumpOffsetCount]; + + for (int index = 0; index < jumpOffsetCount; index++) + { + cases[index] = readInt(code, offset); offset += 4; + jumpOffsets[index] = readInt(code, offset); offset += 4; + } + } + + + protected void writeInfo(byte[] code, int offset) + { + // Write up to three padding bytes. + while ((offset & 3) != 0) + { + writeByte(code, offset++, 0); + } + + // Write the two 32-bit arguments. + writeInt(code, offset, defaultOffset); offset += 4; + writeInt(code, offset, cases.length); offset += 4; + + // Write the matches-offset pairs. + for (int index = 0; index < cases.length; index++) + { + writeInt(code, offset, cases[index]); offset += 4; + writeInt(code, offset, jumpOffsets[index]); offset += 4; + } + } + + + public int length(int offset) + { + return 1 + (-(offset+1) & 3) + 8 + cases.length * 8; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, this); + } +} diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/src/proguard/classfile/instruction/SimpleInstruction.java new file mode 100644 index 0000000..84e6344 --- /dev/null +++ b/src/proguard/classfile/instruction/SimpleInstruction.java @@ -0,0 +1,255 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class SimpleInstruction extends Instruction +{ + public int constant; + + + /** + * Creates an uninitialized SimpleInstruction. + */ + public SimpleInstruction() {} + + + /** + * Creates a new SimpleInstruction with the given opcode. + */ + public SimpleInstruction(byte opcode) + { + this(opcode, embeddedConstant(opcode)); + } + + + /** + * Creates a new SimpleInstruction with the given opcode and constant. + */ + public SimpleInstruction(byte opcode, int constant) + { + this.opcode = opcode; + this.constant = constant; + } + + + /** + * Copies the given instruction into this instruction. + * @param simpleInstruction the instruction to be copied. + * @return this instruction. + */ + public SimpleInstruction copy(SimpleInstruction simpleInstruction) + { + this.opcode = simpleInstruction.opcode; + this.constant = simpleInstruction.constant; + + return this; + } + + + /** + * Return the embedded constant of the given opcode, or 0 if the opcode + * doesn't have one. + */ + private static int embeddedConstant(byte opcode) + { + switch (opcode) + { + case InstructionConstants.OP_ICONST_M1: return -1; + + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_LCONST_1: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_DCONST_1: return 1; + + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_FCONST_2: return 2; + + case InstructionConstants.OP_ICONST_3: return 3; + + case InstructionConstants.OP_ICONST_4: return 4; + + case InstructionConstants.OP_ICONST_5: return 5; + + default: return 0; + } + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // Replace any _1, _2, _3,... extension by _0. + switch (opcode) + { + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_BIPUSH: + case InstructionConstants.OP_SIPUSH: return InstructionConstants.OP_ICONST_0; + + case InstructionConstants.OP_LCONST_0: + case InstructionConstants.OP_LCONST_1: return InstructionConstants.OP_LCONST_0; + + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: return InstructionConstants.OP_FCONST_0; + + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: return InstructionConstants.OP_DCONST_0; + + default: return opcode; + } + } + + public Instruction shrink() + { + // Reconstruct the opcode of the shortest instruction, if there are + // any alternatives. + switch (opcode) + { + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_BIPUSH: + case InstructionConstants.OP_SIPUSH: + switch (requiredConstantSize()) + { + case 0: + opcode = (byte)(InstructionConstants.OP_ICONST_0 + constant); + break; + case 1: + opcode = InstructionConstants.OP_BIPUSH; + break; + case 2: + opcode = InstructionConstants.OP_SIPUSH; + break; + } + break; + + case InstructionConstants.OP_LCONST_0: + case InstructionConstants.OP_LCONST_1: + opcode = (byte)(InstructionConstants.OP_LCONST_0 + constant); + break; + + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: + opcode = (byte)(InstructionConstants.OP_FCONST_0 + constant); + break; + + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: + opcode = (byte)(InstructionConstants.OP_DCONST_0 + constant); + break; + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + int constantSize = constantSize(); + + // Also initialize embedded constants that are different from 0. + constant = constantSize == 0 ? + embeddedConstant(opcode) : + readSignedValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int constantSize = constantSize(); + + if (requiredConstantSize() > constantSize) + { + throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")"); + } + + writeSignedValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return 1 + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public String toString() + { + return getName() + + (constantSize() > 0 ? " "+constant : ""); + } + + + // Small utility methods. + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode == InstructionConstants.OP_BIPUSH || + opcode == InstructionConstants.OP_NEWARRAY ? 1 : + opcode == InstructionConstants.OP_SIPUSH ? 2 : + 0; + } + + + /** + * Computes the required constant size for this instruction. + */ + private int requiredConstantSize() + { + return constant >= -1 && constant <= 5 ? 0 : + constant << 24 >> 24 == constant ? 1 : + constant << 16 >> 16 == constant ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/SwitchInstruction.java b/src/proguard/classfile/instruction/SwitchInstruction.java new file mode 100644 index 0000000..b98c2fb --- /dev/null +++ b/src/proguard/classfile/instruction/SwitchInstruction.java @@ -0,0 +1,83 @@ +/* + * 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.classfile.instruction; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public abstract class SwitchInstruction extends Instruction +{ + public int defaultOffset; + public int[] jumpOffsets; + + + /** + * Creates an uninitialized SwitchInstruction. + */ + public SwitchInstruction() {} + + + /** + * Creates a new SwitchInstruction with the given arguments. + */ + public SwitchInstruction(byte opcode, + int defaultOffset, + int[] jumpOffsets) + { + this.opcode = opcode; + this.defaultOffset = defaultOffset; + this.jumpOffsets = jumpOffsets; + } + + + /** + * Copies the given instruction into this instruction. + * @param switchInstruction the instruction to be copied. + * @return this instruction. + */ + public SwitchInstruction copy(SwitchInstruction switchInstruction) + { + this.opcode = switchInstruction.opcode; + this.defaultOffset = switchInstruction.defaultOffset; + this.jumpOffsets = switchInstruction.jumpOffsets; + + return this; + } + + + // Implementations for Instruction. + + public String toString(int offset) + { + return "["+offset+"] "+toString()+" (target="+(offset+defaultOffset)+")"; + } + + + // Implementations for Object. + + public String toString() + { + return getName()+" ("+jumpOffsets.length+" offsets, default="+defaultOffset+")"; + } +} diff --git a/src/proguard/classfile/instruction/TableSwitchInstruction.java b/src/proguard/classfile/instruction/TableSwitchInstruction.java new file mode 100644 index 0000000..ee81af5 --- /dev/null +++ b/src/proguard/classfile/instruction/TableSwitchInstruction.java @@ -0,0 +1,139 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class TableSwitchInstruction extends SwitchInstruction +{ + public int lowCase; + public int highCase; + + + /** + * Creates an uninitialized TableSwitchInstruction. + */ + public TableSwitchInstruction() {} + + + /** + * Creates a new TableSwitchInstruction with the given arguments. + */ + public TableSwitchInstruction(byte opcode, + int defaultOffset, + int lowCase, + int highCase, + int[] jumpOffsets) + { + this.opcode = opcode; + this.defaultOffset = defaultOffset; + this.lowCase = lowCase; + this.highCase = highCase; + this.jumpOffsets = jumpOffsets; + } + + + /** + * Copies the given instruction into this instruction. + * @param tableSwitchInstruction the instruction to be copied. + * @return this instruction. + */ + public TableSwitchInstruction copy(TableSwitchInstruction tableSwitchInstruction) + { + this.opcode = tableSwitchInstruction.opcode; + this.defaultOffset = tableSwitchInstruction.defaultOffset; + this.lowCase = tableSwitchInstruction.lowCase; + this.highCase = tableSwitchInstruction.highCase; + this.jumpOffsets = tableSwitchInstruction.jumpOffsets; + + return this; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + // There aren't any ways to shrink this instruction. + return this; + } + + protected void readInfo(byte[] code, int offset) + { + // Skip up to three padding bytes. + offset += -offset & 3; + + // Read the three 32-bit arguments. + defaultOffset = readInt(code, offset); offset += 4; + lowCase = readInt(code, offset); offset += 4; + highCase = readInt(code, offset); offset += 4; + + // Read the jump offsets. + jumpOffsets = new int[highCase - lowCase + 1]; + + for (int index = 0; index < jumpOffsets.length; index++) + { + jumpOffsets[index] = readInt(code, offset); offset += 4; + } + } + + + protected void writeInfo(byte[] code, int offset) + { + // Write up to three padding bytes. + while ((offset & 3) != 0) + { + writeByte(code, offset++, 0); + } + + // Write the three 32-bit arguments. + writeInt(code, offset, defaultOffset); offset += 4; + writeInt(code, offset, lowCase); offset += 4; + writeInt(code, offset, highCase); offset += 4; + + // Write the jump offsets. + int length = highCase - lowCase + 1; + for (int index = 0; index < length; index++) + { + writeInt(code, offset, jumpOffsets[index]); offset += 4; + } + } + + + public int length(int offset) + { + return 1 + (-(offset+1) & 3) + 12 + (highCase - lowCase + 1) * 4; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, this); + } +} diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/src/proguard/classfile/instruction/VariableInstruction.java new file mode 100644 index 0000000..309f802 --- /dev/null +++ b/src/proguard/classfile/instruction/VariableInstruction.java @@ -0,0 +1,372 @@ +/* + * 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.classfile.instruction; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents an instruction that refers to a variable on the + * local variable stack. + * + * @author Eric Lafortune + */ +public class VariableInstruction extends Instruction +{ + public boolean wide; + public int variableIndex; + public int constant; + + + /** + * Creates an uninitialized VariableInstruction. + */ + public VariableInstruction() {} + + + public VariableInstruction(boolean wide) + { + this.wide = wide; + } + + + public VariableInstruction(byte opcode) + { + this(opcode, embeddedVariable(opcode), 0); + } + + + public VariableInstruction(byte opcode, + int variableIndex) + { + this(opcode, variableIndex, 0); + } + + + public VariableInstruction(byte opcode, + int variableIndex, + int constant) + { + this.opcode = opcode; + this.variableIndex = variableIndex; + this.constant = constant; + this.wide = requiredVariableIndexSize() > 1 || + requiredConstantSize() > 1; + } + + + /** + * Copies the given instruction into this instruction. + * @param variableInstruction the instruction to be copied. + * @return this instruction. + */ + public VariableInstruction copy(VariableInstruction variableInstruction) + { + this.opcode = variableInstruction.opcode; + this.variableIndex = variableInstruction.variableIndex; + this.constant = variableInstruction.constant; + this.wide = variableInstruction.wide; + + return this; + } + + + /** + * Return the embedded variable of the given opcode, or 0 if the opcode + * doesn't have one. + */ + private static int embeddedVariable(byte opcode) + { + switch (opcode) + { + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_LSTORE_1: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_DSTORE_1: + case InstructionConstants.OP_ASTORE_1: return 1; + + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ISTORE_2: + case InstructionConstants.OP_LSTORE_2: + case InstructionConstants.OP_FSTORE_2: + case InstructionConstants.OP_DSTORE_2: + case InstructionConstants.OP_ASTORE_2: return 2; + + case InstructionConstants.OP_ILOAD_3: + case InstructionConstants.OP_LLOAD_3: + case InstructionConstants.OP_FLOAD_3: + case InstructionConstants.OP_DLOAD_3: + case InstructionConstants.OP_ALOAD_3: + case InstructionConstants.OP_ISTORE_3: + case InstructionConstants.OP_LSTORE_3: + case InstructionConstants.OP_FSTORE_3: + case InstructionConstants.OP_DSTORE_3: + case InstructionConstants.OP_ASTORE_3: return 3; + + default: return 0; + } + } + + + /** + * Returns whether this instruction stores the value of a variable. + * The value is false for the ret instruction, but true for the iinc + * instruction. + */ + public boolean isStore() + { + // A store instruction can be recognized as follows. Note that this + // excludes the ret instruction, which has a negative opcode. + return opcode >= InstructionConstants.OP_ISTORE || + opcode == InstructionConstants.OP_IINC; + } + + + /** + * Returns whether this instruction loads the value of a variable. + * The value is true for the ret instruction and for the iinc + * instruction. + */ + public boolean isLoad() + { + // A load instruction can be recognized as follows. Note that this + // includes the ret instruction, which has a negative opcode. + return opcode < InstructionConstants.OP_ISTORE; + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // Remove the _0, _1, _2, _3 extension, if any. + switch (opcode) + { + case InstructionConstants.OP_ILOAD_0: + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_ILOAD_3: return InstructionConstants.OP_ILOAD; + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_LLOAD_3: return InstructionConstants.OP_LLOAD; + case InstructionConstants.OP_FLOAD_0: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_FLOAD_3: return InstructionConstants.OP_FLOAD; + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_DLOAD_3: return InstructionConstants.OP_DLOAD; + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: return InstructionConstants.OP_ALOAD; + + case InstructionConstants.OP_ISTORE_0: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_ISTORE_2: + case InstructionConstants.OP_ISTORE_3: return InstructionConstants.OP_ISTORE; + case InstructionConstants.OP_LSTORE_0: + case InstructionConstants.OP_LSTORE_1: + case InstructionConstants.OP_LSTORE_2: + case InstructionConstants.OP_LSTORE_3: return InstructionConstants.OP_LSTORE; + case InstructionConstants.OP_FSTORE_0: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_FSTORE_2: + case InstructionConstants.OP_FSTORE_3: return InstructionConstants.OP_FSTORE; + case InstructionConstants.OP_DSTORE_0: + case InstructionConstants.OP_DSTORE_1: + case InstructionConstants.OP_DSTORE_2: + case InstructionConstants.OP_DSTORE_3: return InstructionConstants.OP_DSTORE; + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: return InstructionConstants.OP_ASTORE; + + default: return opcode; + } + } + + public Instruction shrink() + { + opcode = canonicalOpcode(); + + // Is this instruction pointing to a variable with index from 0 to 3? + if (variableIndex <= 3) + { + switch (opcode) + { + case InstructionConstants.OP_ILOAD: opcode = (byte)(InstructionConstants.OP_ILOAD_0 + variableIndex); break; + case InstructionConstants.OP_LLOAD: opcode = (byte)(InstructionConstants.OP_LLOAD_0 + variableIndex); break; + case InstructionConstants.OP_FLOAD: opcode = (byte)(InstructionConstants.OP_FLOAD_0 + variableIndex); break; + case InstructionConstants.OP_DLOAD: opcode = (byte)(InstructionConstants.OP_DLOAD_0 + variableIndex); break; + case InstructionConstants.OP_ALOAD: opcode = (byte)(InstructionConstants.OP_ALOAD_0 + variableIndex); break; + + case InstructionConstants.OP_ISTORE: opcode = (byte)(InstructionConstants.OP_ISTORE_0 + variableIndex); break; + case InstructionConstants.OP_LSTORE: opcode = (byte)(InstructionConstants.OP_LSTORE_0 + variableIndex); break; + case InstructionConstants.OP_FSTORE: opcode = (byte)(InstructionConstants.OP_FSTORE_0 + variableIndex); break; + case InstructionConstants.OP_DSTORE: opcode = (byte)(InstructionConstants.OP_DSTORE_0 + variableIndex); break; + case InstructionConstants.OP_ASTORE: opcode = (byte)(InstructionConstants.OP_ASTORE_0 + variableIndex); break; + } + } + + // Only make the instruction wide if necessary. + wide = requiredVariableIndexSize() > 1 || + requiredConstantSize() > 1; + + return this; + } + + + protected boolean isWide() + { + return wide; + } + + + protected void readInfo(byte[] code, int offset) + { + int variableIndexSize = variableIndexSize(); + int constantSize = constantSize(); + + // Also initialize embedded variable indexes. + if (variableIndexSize == 0) + { + // An embedded variable index can be decoded as follows. + variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ? + (opcode - InstructionConstants.OP_ILOAD_0 ) & 3 : + (opcode - InstructionConstants.OP_ISTORE_0) & 3; + } + else + { + variableIndex = readValue(code, offset, variableIndexSize); offset += variableIndexSize; + } + + constant = readSignedValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int variableIndexSize = variableIndexSize(); + int constantSize = constantSize(); + + if (requiredVariableIndexSize() > variableIndexSize) + { + throw new IllegalArgumentException("Instruction has invalid variable index size ("+this.toString(offset)+")"); + } + + if (requiredConstantSize() > constantSize) + { + throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")"); + } + + writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize; + writeSignedValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return (wide ? 2 : 1) + variableIndexSize() + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public String toString() + { + return getName() + + (wide ? "_w" : "") + + " v"+variableIndex + + (constantSize() > 0 ? ", "+constant : ""); + } + + + // Small utility methods. + + /** + * Returns the variable index size for this instruction. + */ + private int variableIndexSize() + { + return (opcode >= InstructionConstants.OP_ILOAD_0 && + opcode <= InstructionConstants.OP_ALOAD_3) || + (opcode >= InstructionConstants.OP_ISTORE_0 && + opcode <= InstructionConstants.OP_ASTORE_3) ? 0 : + wide ? 2 : + 1; + } + + + /** + * Computes the required variable index size for this instruction's variable + * index. + */ + private int requiredVariableIndexSize() + { + return (variableIndex & 0x3) == variableIndex ? 0 : + (variableIndex & 0xff) == variableIndex ? 1 : + (variableIndex & 0xffff) == variableIndex ? 2 : + 4; + + } + + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode != InstructionConstants.OP_IINC ? 0 : + wide ? 2 : + 1; + } + + + /** + * Computes the required constant size for this instruction's constant. + */ + private int requiredConstantSize() + { + return opcode != InstructionConstants.OP_IINC ? 0 : + constant << 24 >> 24 == constant ? 1 : + constant << 16 >> 16 == constant ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/package.html b/src/proguard/classfile/instruction/package.html new file mode 100644 index 0000000..48c234e --- /dev/null +++ b/src/proguard/classfile/instruction/package.html @@ -0,0 +1,9 @@ +<body> +This package contains classes to represent Java bytecode instructions. +<p> +Not every instruction currently has its own class. Only groups of instructions +that refer to the constant pool get their own representations. +<p> +While the package is sufficient for the current needs of the ProGuard +application, it may very well be reorganised and extended in the future. +</body> diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java new file mode 100644 index 0000000..71b2cde --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java @@ -0,0 +1,56 @@ +/* + * 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.classfile.instruction.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given InstructionVisitor visit all Instruction + * objects of the CodeAttribute objects it visits. + * + * @author Eric Lafortune + */ +public class AllInstructionVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final InstructionVisitor instructionVisitor; + + + public AllInstructionVisitor(InstructionVisitor instructionVisitor) + { + this.instructionVisitor = instructionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.instructionsAccept(clazz, method, instructionVisitor); + } +} diff --git a/src/proguard/classfile/instruction/visitor/InstructionCounter.java b/src/proguard/classfile/instruction/visitor/InstructionCounter.java new file mode 100644 index 0000000..1d10980 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/InstructionCounter.java @@ -0,0 +1,59 @@ +/* + * 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.classfile.instruction.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor counts the number of instructions that has been visited. + * + * @author Eric Lafortune + */ +public class InstructionCounter +extends SimplifiedVisitor +implements InstructionVisitor +{ + private int count; + + + /** + * Returns the number of instructions that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) + { + count++; + } +} diff --git a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java new file mode 100644 index 0000000..11af131 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java @@ -0,0 +1,42 @@ +/* + * 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.classfile.instruction.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>Instruction</code> objects. + * + * @author Eric Lafortune + */ +public interface InstructionVisitor +{ + public void visitSimpleInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction); + public void visitVariableInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction); + public void visitConstantInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction); + public void visitBranchInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction); + public void visitTableSwitchInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction); + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction); +} diff --git a/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java new file mode 100644 index 0000000..aada455 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java @@ -0,0 +1,131 @@ +/* + * 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.classfile.instruction.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.instruction.*; + + +/** + * This InstructionVisitor delegates all visits to each InstructionVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiInstructionVisitor implements InstructionVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + + private InstructionVisitor[] instructionVisitors; + private int instructionVisitorCount; + + + public MultiInstructionVisitor() + { + } + + + public MultiInstructionVisitor(InstructionVisitor[] instructionVisitors) + { + this.instructionVisitors = instructionVisitors; + this.instructionVisitorCount = instructionVisitors.length; + } + + + public void addInstructionVisitor(InstructionVisitor instructionVisitor) + { + ensureArraySize(); + + instructionVisitors[instructionVisitorCount++] = instructionVisitor; + } + + + private void ensureArraySize() + { + if (instructionVisitors == null) + { + instructionVisitors = new InstructionVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (instructionVisitors.length == instructionVisitorCount) + { + InstructionVisitor[] newInstructionVisitors = + new InstructionVisitor[instructionVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(instructionVisitors, 0, + newInstructionVisitors, 0, + instructionVisitorCount); + instructionVisitors = newInstructionVisitors; + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + } + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + } + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + } + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction); + } + } +} diff --git a/src/proguard/classfile/instruction/visitor/package.html b/src/proguard/classfile/instruction/visitor/package.html new file mode 100644 index 0000000..a31a408 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/package.html @@ -0,0 +1,3 @@ +<body> +This package contains visitors for instructions. +</body> diff --git a/src/proguard/classfile/io/LibraryClassReader.java b/src/proguard/classfile/io/LibraryClassReader.java new file mode 100644 index 0000000..f14471c --- /dev/null +++ b/src/proguard/classfile/io/LibraryClassReader.java @@ -0,0 +1,362 @@ +/* + * 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.classfile.io; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.DataInput; + +/** + * This ClassVisitor fills out the LibraryClass objects that it visits with data + * from the given DataInput object. + * + * @author Eric Lafortune + */ +public class LibraryClassReader +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor +{ + private static final LibraryField[] EMPTY_LIBRARY_FIELDS = new LibraryField[0]; + private static final LibraryMethod[] EMPTY_LIBRARY_METHODS = new LibraryMethod[0]; + + + private final RuntimeDataInput dataInput; + private final boolean skipNonPublicClasses; + private final boolean skipNonPublicClassMembers; + + // A global array that acts as a parameter for the visitor methods. + private Constant[] constantPool; + + + /** + * Creates a new ProgramClassReader for reading from the given DataInput. + */ + public LibraryClassReader(DataInput dataInput, + boolean skipNonPublicClasses, + boolean skipNonPublicClassMembers) + { + this.dataInput = new RuntimeDataInput(dataInput); + this.skipNonPublicClasses = skipNonPublicClasses; + this.skipNonPublicClassMembers = skipNonPublicClassMembers; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass libraryClass) + { + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Read and check the magic number. + int u4magic = dataInput.readInt(); + + ClassUtil.checkMagicNumber(u4magic); + + // Read and check the version numbers. + int u2minorVersion = dataInput.readUnsignedShort(); + int u2majorVersion = dataInput.readUnsignedShort(); + + int u4version = ClassUtil.internalClassVersion(u2majorVersion, + u2minorVersion); + + ClassUtil.checkVersionNumbers(u4version); + + // Read the constant pool. Note that the first entry is not used. + int u2constantPoolCount = dataInput.readUnsignedShort(); + + // Create the constant pool array. + constantPool = new Constant[u2constantPoolCount]; + + for (int index = 1; index < u2constantPoolCount; index++) + { + Constant constant = createConstant(); + constant.accept(libraryClass, this); + + int tag = constant.getTag(); + if (tag == ClassConstants.CONSTANT_Class || + tag == ClassConstants.CONSTANT_Utf8) + { + constantPool[index] = constant; + } + + // Long constants and double constants take up two entries in the + // constant pool. + if (tag == ClassConstants.CONSTANT_Long || + tag == ClassConstants.CONSTANT_Double) + { + index++; + } + } + + // Read the general class information. + libraryClass.u2accessFlags = dataInput.readUnsignedShort(); + + // We may stop parsing this library class if it's not public anyway. + // E.g. only about 60% of all rt.jar classes need to be parsed. + if (skipNonPublicClasses && + AccessUtil.accessLevel(libraryClass.getAccessFlags()) < AccessUtil.PUBLIC) + { + return; + } + + // Read the class and super class indices. + int u2thisClass = dataInput.readUnsignedShort(); + int u2superClass = dataInput.readUnsignedShort(); + + // Store their actual names. + libraryClass.thisClassName = getClassName(u2thisClass); + libraryClass.superClassName = (u2superClass == 0) ? null : + getClassName(u2superClass); + + // Read the interfaces + int u2interfacesCount = dataInput.readUnsignedShort(); + + libraryClass.interfaceNames = new String[u2interfacesCount]; + for (int index = 0; index < u2interfacesCount; index++) + { + // Store the actual interface name. + int u2interface = dataInput.readUnsignedShort(); + libraryClass.interfaceNames[index] = getClassName(u2interface); + } + + // Read the fields. + int u2fieldsCount = dataInput.readUnsignedShort(); + + // Create the fields array. + LibraryField[] reusableFields = new LibraryField[u2fieldsCount]; + + int visibleFieldsCount = 0; + for (int index = 0; index < u2fieldsCount; index++) + { + LibraryField field = new LibraryField(); + this.visitLibraryMember(libraryClass, field); + + // Only store fields that are visible. + if (AccessUtil.accessLevel(field.getAccessFlags()) >= + (skipNonPublicClassMembers ? AccessUtil.PROTECTED : + AccessUtil.PACKAGE_VISIBLE)) + { + reusableFields[visibleFieldsCount++] = field; + } + } + + // Copy the visible fields (if any) into a fields array of the right size. + if (visibleFieldsCount == 0) + { + libraryClass.fields = EMPTY_LIBRARY_FIELDS; + } + else + { + libraryClass.fields = new LibraryField[visibleFieldsCount]; + System.arraycopy(reusableFields, 0, libraryClass.fields, 0, visibleFieldsCount); + } + + // Read the methods. + int u2methodsCount = dataInput.readUnsignedShort(); + + // Create the methods array. + LibraryMethod[] reusableMethods = new LibraryMethod[u2methodsCount]; + + int visibleMethodsCount = 0; + for (int index = 0; index < u2methodsCount; index++) + { + LibraryMethod method = new LibraryMethod(); + this.visitLibraryMember(libraryClass, method); + + // Only store methods that are visible. + if (AccessUtil.accessLevel(method.getAccessFlags()) >= + (skipNonPublicClassMembers ? AccessUtil.PROTECTED : + AccessUtil.PACKAGE_VISIBLE)) + { + reusableMethods[visibleMethodsCount++] = method; + } + } + + // Copy the visible methods (if any) into a methods array of the right size. + if (visibleMethodsCount == 0) + { + libraryClass.methods = EMPTY_LIBRARY_METHODS; + } + else + { + libraryClass.methods = new LibraryMethod[visibleMethodsCount]; + System.arraycopy(reusableMethods, 0, libraryClass.methods, 0, visibleMethodsCount); + } + + // Skip the class attributes. + skipAttributes(); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember) + { + } + + + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + // Read the general field information. + libraryMember.u2accessFlags = dataInput.readUnsignedShort(); + libraryMember.name = getString(dataInput.readUnsignedShort()); + libraryMember.descriptor = getString(dataInput.readUnsignedShort()); + + // Skip the field attributes. + skipAttributes(); + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + dataInput.skipBytes(4); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + dataInput.skipBytes(8); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + dataInput.skipBytes(4); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + dataInput.skipBytes(8); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + dataInput.skipBytes(2); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + int u2length = dataInput.readUnsignedShort(); + + // Read the UTF-8 bytes. + byte[] bytes = new byte[u2length]; + dataInput.readFully(bytes); + utf8Constant.setBytes(bytes); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + dataInput.skipBytes(4); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.u2nameIndex = dataInput.readUnsignedShort(); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + dataInput.skipBytes(4); + } + + + // Small utility methods. + + /** + * Returns the class name of the ClassConstant at the specified index in the + * reusable constant pool. + */ + private String getClassName(int constantIndex) + { + ClassConstant classEntry = (ClassConstant)constantPool[constantIndex]; + + return getString(classEntry.u2nameIndex); + } + + + /** + * Returns the string of the Utf8Constant at the specified index in the + * reusable constant pool. + */ + private String getString(int constantIndex) + { + return ((Utf8Constant)constantPool[constantIndex]).getString(); + } + + + private Constant createConstant() + { + int u1tag = dataInput.readUnsignedByte(); + + switch (u1tag) + { + case ClassConstants.CONSTANT_Utf8: return new Utf8Constant(); + case ClassConstants.CONSTANT_Integer: return new IntegerConstant(); + case ClassConstants.CONSTANT_Float: return new FloatConstant(); + case ClassConstants.CONSTANT_Long: return new LongConstant(); + case ClassConstants.CONSTANT_Double: return new DoubleConstant(); + case ClassConstants.CONSTANT_String: return new StringConstant(); + case ClassConstants.CONSTANT_Fieldref: return new FieldrefConstant(); + case ClassConstants.CONSTANT_Methodref: return new MethodrefConstant(); + case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant(); + case ClassConstants.CONSTANT_Class: return new ClassConstant(); + case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); + + default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); + } + } + + + private void skipAttributes() + { + int u2attributesCount = dataInput.readUnsignedShort(); + + for (int index = 0; index < u2attributesCount; index++) + { + skipAttribute(); + } + } + + + private void skipAttribute() + { + dataInput.skipBytes(2); + int u4attributeLength = dataInput.readInt(); + dataInput.skipBytes(u4attributeLength); + } +} diff --git a/src/proguard/classfile/io/ProgramClassReader.java b/src/proguard/classfile/io/ProgramClassReader.java new file mode 100644 index 0000000..476a346 --- /dev/null +++ b/src/proguard/classfile/io/ProgramClassReader.java @@ -0,0 +1,862 @@ +/* + * 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.classfile.io; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.DataInput; + +/** + * This ClassVisitor fills out the ProgramClass objects that it visits with data + * from the given DataInput object. + * + * @author Eric Lafortune + */ +public class ProgramClassReader +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final RuntimeDataInput dataInput; + + + /** + * Creates a new ProgramClassReader for reading from the given DataInput. + */ + public ProgramClassReader(DataInput dataInput) + { + this.dataInput = new RuntimeDataInput(dataInput); + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Read and check the magic number. + programClass.u4magic = dataInput.readInt(); + + ClassUtil.checkMagicNumber(programClass.u4magic); + + // Read and check the version numbers. + int u2minorVersion = dataInput.readUnsignedShort(); + int u2majorVersion = dataInput.readUnsignedShort(); + + programClass.u4version = ClassUtil.internalClassVersion(u2majorVersion, + u2minorVersion); + + ClassUtil.checkVersionNumbers(programClass.u4version); + + // Read the constant pool. Note that the first entry is not used. + programClass.u2constantPoolCount = dataInput.readUnsignedShort(); + + programClass.constantPool = new Constant[programClass.u2constantPoolCount]; + for (int index = 1; index < programClass.u2constantPoolCount; index++) + { + Constant constant = createConstant(); + constant.accept(programClass, this); + programClass.constantPool[index] = constant; + + // Long constants and double constants take up two entries in the + // constant pool. + int tag = constant.getTag(); + if (tag == ClassConstants.CONSTANT_Long || + tag == ClassConstants.CONSTANT_Double) + { + programClass.constantPool[++index] = null; + } + } + + // Read the general class information. + programClass.u2accessFlags = dataInput.readUnsignedShort(); + programClass.u2thisClass = dataInput.readUnsignedShort(); + programClass.u2superClass = dataInput.readUnsignedShort(); + + // Read the interfaces. + programClass.u2interfacesCount = dataInput.readUnsignedShort(); + + programClass.u2interfaces = new int[programClass.u2interfacesCount]; + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + programClass.u2interfaces[index] = dataInput.readUnsignedShort(); + } + + // Read the fields. + programClass.u2fieldsCount = dataInput.readUnsignedShort(); + + programClass.fields = new ProgramField[programClass.u2fieldsCount]; + for (int index = 0; index < programClass.u2fieldsCount; index++) + { + ProgramField programField = new ProgramField(); + this.visitProgramField(programClass, programField); + programClass.fields[index] = programField; + } + + // Read the methods. + programClass.u2methodsCount = dataInput.readUnsignedShort(); + + programClass.methods = new ProgramMethod[programClass.u2methodsCount]; + for (int index = 0; index < programClass.u2methodsCount; index++) + { + ProgramMethod programMethod = new ProgramMethod(); + this.visitProgramMethod(programClass, programMethod); + programClass.methods[index] = programMethod; + } + + // Read the class attributes. + programClass.u2attributesCount = dataInput.readUnsignedShort(); + + programClass.attributes = new Attribute[programClass.u2attributesCount]; + for (int index = 0; index < programClass.u2attributesCount; index++) + { + Attribute attribute = createAttribute(programClass); + attribute.accept(programClass, this); + programClass.attributes[index] = attribute; + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Read the general field information. + programField.u2accessFlags = dataInput.readUnsignedShort(); + programField.u2nameIndex = dataInput.readUnsignedShort(); + programField.u2descriptorIndex = dataInput.readUnsignedShort(); + + // Read the field attributes. + programField.u2attributesCount = dataInput.readUnsignedShort(); + + programField.attributes = new Attribute[programField.u2attributesCount]; + for (int index = 0; index < programField.u2attributesCount; index++) + { + Attribute attribute = createAttribute(programClass); + attribute.accept(programClass, programField, this); + programField.attributes[index] = attribute; + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Read the general method information. + programMethod.u2accessFlags = dataInput.readUnsignedShort(); + programMethod.u2nameIndex = dataInput.readUnsignedShort(); + programMethod.u2descriptorIndex = dataInput.readUnsignedShort(); + + // Read the method attributes. + programMethod.u2attributesCount = dataInput.readUnsignedShort(); + + programMethod.attributes = new Attribute[programMethod.u2attributesCount]; + for (int index = 0; index < programMethod.u2attributesCount; index++) + { + Attribute attribute = createAttribute(programClass); + attribute.accept(programClass, programMethod, this); + programMethod.attributes[index] = attribute; + } + } + + + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + integerConstant.u4value = dataInput.readInt(); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + longConstant.u8value = dataInput.readLong(); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + floatConstant.f4value = dataInput.readFloat(); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + doubleConstant.f8value = dataInput.readDouble(); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + stringConstant.u2stringIndex = dataInput.readUnsignedShort(); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + int u2length = dataInput.readUnsignedShort(); + + // Read the UTF-8 bytes. + byte[] bytes = new byte[u2length]; + dataInput.readFully(bytes); + utf8Constant.setBytes(bytes); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + refConstant.u2classIndex = dataInput.readUnsignedShort(); + refConstant.u2nameAndTypeIndex = dataInput.readUnsignedShort(); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.u2nameIndex = dataInput.readUnsignedShort(); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + nameAndTypeConstant.u2nameIndex = dataInput.readUnsignedShort(); + nameAndTypeConstant.u2descriptorIndex = dataInput.readUnsignedShort(); + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // Read the unknown information. + byte[] info = new byte[unknownAttribute.u4attributeLength]; + dataInput.readFully(info); + unknownAttribute.info = info; + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + sourceFileAttribute.u2sourceFileIndex = dataInput.readUnsignedShort(); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + sourceDirAttribute.u2sourceDirIndex = dataInput.readUnsignedShort(); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Read the inner classes. + innerClassesAttribute.u2classesCount = dataInput.readUnsignedShort(); + + innerClassesAttribute.classes = new InnerClassesInfo[innerClassesAttribute.u2classesCount]; + for (int index = 0; index < innerClassesAttribute.u2classesCount; index++) + { + InnerClassesInfo innerClassesInfo = new InnerClassesInfo(); + this.visitInnerClassesInfo(clazz, innerClassesInfo); + innerClassesAttribute.classes[index] = innerClassesInfo; + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + enclosingMethodAttribute.u2classIndex = dataInput.readUnsignedShort(); + enclosingMethodAttribute.u2nameAndTypeIndex = dataInput.readUnsignedShort(); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + // This attribute does not contain any additional information. + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + // This attribute does not contain any additional information. + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + signatureAttribute.u2signatureIndex = dataInput.readUnsignedShort(); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + constantValueAttribute.u2constantValueIndex = dataInput.readUnsignedShort(); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + // Read the exceptions. + exceptionsAttribute.u2exceptionIndexTableLength = dataInput.readUnsignedShort(); + + exceptionsAttribute.u2exceptionIndexTable = new int[exceptionsAttribute.u2exceptionIndexTableLength]; + for (int index = 0; index < exceptionsAttribute.u2exceptionIndexTableLength; index++) + { + exceptionsAttribute.u2exceptionIndexTable[index] = dataInput.readUnsignedShort(); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Read the stack size and local variable frame size. + codeAttribute.u2maxStack = dataInput.readUnsignedShort(); + codeAttribute.u2maxLocals = dataInput.readUnsignedShort(); + + // Read the byte code. + codeAttribute.u4codeLength = dataInput.readInt(); + + byte[] code = new byte[codeAttribute.u4codeLength]; + dataInput.readFully(code); + codeAttribute.code = code; + + // Read the exceptions. + codeAttribute.u2exceptionTableLength = dataInput.readUnsignedShort(); + + codeAttribute.exceptionTable = new ExceptionInfo[codeAttribute.u2exceptionTableLength]; + for (int index = 0; index < codeAttribute.u2exceptionTableLength; index++) + { + ExceptionInfo exceptionInfo = new ExceptionInfo(); + this.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + codeAttribute.exceptionTable[index] = exceptionInfo; + } + + // Read the code attributes. + codeAttribute.u2attributesCount = dataInput.readUnsignedShort(); + + codeAttribute.attributes = new Attribute[codeAttribute.u2attributesCount]; + for (int index = 0; index < codeAttribute.u2attributesCount; index++) + { + Attribute attribute = createAttribute(clazz); + attribute.accept(clazz, method, codeAttribute, this); + codeAttribute.attributes[index] = attribute; + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // Read the stack map frames (only full frames, without tag). + stackMapAttribute.u2stackMapFramesCount = dataInput.readUnsignedShort(); + + stackMapAttribute.stackMapFrames = new FullFrame[stackMapAttribute.u2stackMapFramesCount]; + for (int index = 0; index < stackMapAttribute.u2stackMapFramesCount; index++) + { + FullFrame stackMapFrame = new FullFrame(); + this.visitFullFrame(clazz, method, codeAttribute, index, stackMapFrame); + stackMapAttribute.stackMapFrames[index] = stackMapFrame; + } + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // Read the stack map frames. + stackMapTableAttribute.u2stackMapFramesCount = dataInput.readUnsignedShort(); + + stackMapTableAttribute.stackMapFrames = new StackMapFrame[stackMapTableAttribute.u2stackMapFramesCount]; + for (int index = 0; index < stackMapTableAttribute.u2stackMapFramesCount; index++) + { + StackMapFrame stackMapFrame = createStackMapFrame(); + stackMapFrame.accept(clazz, method, codeAttribute, 0, this); + stackMapTableAttribute.stackMapFrames[index] = stackMapFrame; + } + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Read the line numbers. + lineNumberTableAttribute.u2lineNumberTableLength = dataInput.readUnsignedShort(); + + lineNumberTableAttribute.lineNumberTable = new LineNumberInfo[lineNumberTableAttribute.u2lineNumberTableLength]; + for (int index = 0; index < lineNumberTableAttribute.u2lineNumberTableLength; index++) + { + LineNumberInfo lineNumberInfo = new LineNumberInfo(); + this.visitLineNumberInfo(clazz, method, codeAttribute, lineNumberInfo); + lineNumberTableAttribute.lineNumberTable[index] = lineNumberInfo; + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Read the local variables. + localVariableTableAttribute.u2localVariableTableLength = dataInput.readUnsignedShort(); + + localVariableTableAttribute.localVariableTable = new LocalVariableInfo[localVariableTableAttribute.u2localVariableTableLength]; + for (int index = 0; index < localVariableTableAttribute.u2localVariableTableLength; index++) + { + LocalVariableInfo localVariableInfo = new LocalVariableInfo(); + this.visitLocalVariableInfo(clazz, method, codeAttribute, localVariableInfo); + localVariableTableAttribute.localVariableTable[index] = localVariableInfo; + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Read the local variable types. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = dataInput.readUnsignedShort(); + + localVariableTypeTableAttribute.localVariableTypeTable = new LocalVariableTypeInfo[localVariableTypeTableAttribute.u2localVariableTypeTableLength]; + for (int index = 0; index < localVariableTypeTableAttribute.u2localVariableTypeTableLength; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = new LocalVariableTypeInfo(); + this.visitLocalVariableTypeInfo(clazz, method, codeAttribute, localVariableTypeInfo); + localVariableTypeTableAttribute.localVariableTypeTable[index] = localVariableTypeInfo; + } + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Read the annotations. + annotationsAttribute.u2annotationsCount = dataInput.readUnsignedShort(); + + annotationsAttribute.annotations = new Annotation[annotationsAttribute.u2annotationsCount]; + for (int index = 0; index < annotationsAttribute.u2annotationsCount; index++) + { + Annotation annotation = new Annotation(); + this.visitAnnotation(clazz, annotation); + annotationsAttribute.annotations[index] = annotation; + } + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Read the parameter annotations. + parameterAnnotationsAttribute.u2parametersCount = dataInput.readUnsignedByte(); + + // The java compilers of JDK 1.5, JDK 1.6, and Eclipse all count the + // number of parameters of constructors of non-static inner classes + // incorrectly. Fix it right here. + int parameterStart = 0; + if (method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + int realParametersCount = ClassUtil.internalMethodParameterCount(method.getDescriptor(clazz)); + parameterStart = realParametersCount - parameterAnnotationsAttribute.u2parametersCount; + parameterAnnotationsAttribute.u2parametersCount = realParametersCount; + } + + parameterAnnotationsAttribute.u2parameterAnnotationsCount = new int[parameterAnnotationsAttribute.u2parametersCount]; + parameterAnnotationsAttribute.parameterAnnotations = new Annotation[parameterAnnotationsAttribute.u2parametersCount][]; + + for (int parameterIndex = parameterStart; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++) + { + // Read the parameter annotations of the given parameter. + int u2annotationsCount = dataInput.readUnsignedShort(); + + Annotation[] annotations = new Annotation[u2annotationsCount]; + + for (int index = 0; index < u2annotationsCount; index++) + { + Annotation annotation = new Annotation(); + this.visitAnnotation(clazz, annotation); + annotations[index] = annotation; + } + + parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] = u2annotationsCount; + parameterAnnotationsAttribute.parameterAnnotations[parameterIndex] = annotations; + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Read the default element value. + ElementValue elementValue = createElementValue(); + elementValue.accept(clazz, null, this); + annotationDefaultAttribute.defaultValue = elementValue; + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + innerClassesInfo.u2innerClassIndex = dataInput.readUnsignedShort(); + innerClassesInfo.u2outerClassIndex = dataInput.readUnsignedShort(); + innerClassesInfo.u2innerNameIndex = dataInput.readUnsignedShort(); + innerClassesInfo.u2innerClassAccessFlags = dataInput.readUnsignedShort(); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + exceptionInfo.u2startPC = dataInput.readUnsignedShort(); + exceptionInfo.u2endPC = dataInput.readUnsignedShort(); + exceptionInfo.u2handlerPC = dataInput.readUnsignedShort(); + exceptionInfo.u2catchType = dataInput.readUnsignedShort(); + } + + + // Implementations for StackMapFrameVisitor. + + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + if (sameZeroFrame.getTag() == StackMapFrame.SAME_ZERO_FRAME_EXTENDED) + { + sameZeroFrame.u2offsetDelta = dataInput.readUnsignedShort(); + } + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + if (sameOneFrame.getTag() == StackMapFrame.SAME_ONE_FRAME_EXTENDED) + { + sameOneFrame.u2offsetDelta = dataInput.readUnsignedShort(); + } + + // Read the verification type of the stack entry. + VerificationType verificationType = createVerificationType(); + verificationType.accept(clazz, method, codeAttribute, offset, this); + sameOneFrame.stackItem = verificationType; + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + lessZeroFrame.u2offsetDelta = dataInput.readUnsignedShort(); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + moreZeroFrame.u2offsetDelta = dataInput.readUnsignedShort(); + + // Read the verification types of the additional local variables. + moreZeroFrame.additionalVariables = new VerificationType[moreZeroFrame.additionalVariablesCount]; + for (int index = 0; index < moreZeroFrame.additionalVariablesCount; index++) + { + VerificationType verificationType = createVerificationType(); + verificationType.accept(clazz, method, codeAttribute, offset, this); + moreZeroFrame.additionalVariables[index] = verificationType; + } + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + fullFrame.u2offsetDelta = dataInput.readUnsignedShort(); + + // Read the verification types of the local variables. + fullFrame.variablesCount = dataInput.readUnsignedShort(); + fullFrame.variables = new VerificationType[fullFrame.variablesCount]; + for (int index = 0; index < fullFrame.variablesCount; index++) + { + VerificationType verificationType = createVerificationType(); + verificationType.variablesAccept(clazz, method, codeAttribute, offset, index, this); + fullFrame.variables[index] = verificationType; + } + + // Read the verification types of the stack entries. + fullFrame.stackCount = dataInput.readUnsignedShort(); + fullFrame.stack = new VerificationType[fullFrame.stackCount]; + for (int index = 0; index < fullFrame.stackCount; index++) + { + VerificationType verificationType = createVerificationType(); + verificationType.stackAccept(clazz, method, codeAttribute, offset, index, this); + fullFrame.stack[index] = verificationType; + } + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + // Most verification types don't contain any additional information. + } + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + objectType.u2classIndex = dataInput.readUnsignedShort(); + } + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + uninitializedType.u2newInstructionOffset = dataInput.readUnsignedShort(); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + lineNumberInfo.u2startPC = dataInput.readUnsignedShort(); + lineNumberInfo.u2lineNumber = dataInput.readUnsignedShort(); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.u2startPC = dataInput.readUnsignedShort(); + localVariableInfo.u2length = dataInput.readUnsignedShort(); + localVariableInfo.u2nameIndex = dataInput.readUnsignedShort(); + localVariableInfo.u2descriptorIndex = dataInput.readUnsignedShort(); + localVariableInfo.u2index = dataInput.readUnsignedShort(); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.u2startPC = dataInput.readUnsignedShort(); + localVariableTypeInfo.u2length = dataInput.readUnsignedShort(); + localVariableTypeInfo.u2nameIndex = dataInput.readUnsignedShort(); + localVariableTypeInfo.u2signatureIndex = dataInput.readUnsignedShort(); + localVariableTypeInfo.u2index = dataInput.readUnsignedShort(); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Read the annotation type. + annotation.u2typeIndex = dataInput.readUnsignedShort(); + + // Read the element value pairs. + annotation.u2elementValuesCount = dataInput.readUnsignedShort(); + + annotation.elementValues = new ElementValue[annotation.u2elementValuesCount]; + for (int index = 0; index < annotation.u2elementValuesCount; index++) + { + int u2elementNameIndex = dataInput.readUnsignedShort(); + ElementValue elementValue = createElementValue(); + elementValue.u2elementNameIndex = u2elementNameIndex; + elementValue.accept(clazz, annotation, this); + annotation.elementValues[index] = elementValue; + } + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + constantElementValue.u2constantValueIndex = dataInput.readUnsignedShort(); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + enumConstantElementValue.u2typeNameIndex = dataInput.readUnsignedShort(); + enumConstantElementValue.u2constantNameIndex = dataInput.readUnsignedShort(); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + classElementValue.u2classInfoIndex = dataInput.readUnsignedShort(); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Read the annotation. + Annotation annotationValue = new Annotation(); + this.visitAnnotation(clazz, annotationValue); + annotationElementValue.annotationValue = annotationValue; + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Read the element values. + arrayElementValue.u2elementValuesCount = dataInput.readUnsignedShort(); + + arrayElementValue.elementValues = new ElementValue[arrayElementValue.u2elementValuesCount]; + for (int index = 0; index < arrayElementValue.u2elementValuesCount; index++) + { + ElementValue elementValue = createElementValue(); + elementValue.accept(clazz, annotation, this); + arrayElementValue.elementValues[index] = elementValue; + } + } + + + // Small utility methods. + + private Constant createConstant() + { + int u1tag = dataInput.readUnsignedByte(); + + switch (u1tag) + { + case ClassConstants.CONSTANT_Utf8: return new Utf8Constant(); + case ClassConstants.CONSTANT_Integer: return new IntegerConstant(); + case ClassConstants.CONSTANT_Float: return new FloatConstant(); + case ClassConstants.CONSTANT_Long: return new LongConstant(); + case ClassConstants.CONSTANT_Double: return new DoubleConstant(); + case ClassConstants.CONSTANT_String: return new StringConstant(); + case ClassConstants.CONSTANT_Fieldref: return new FieldrefConstant(); + case ClassConstants.CONSTANT_Methodref: return new MethodrefConstant(); + case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant(); + case ClassConstants.CONSTANT_Class: return new ClassConstant(); + case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); + + default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); + } + } + + + private Attribute createAttribute(Clazz clazz) + { + int u2attributeNameIndex = dataInput.readUnsignedShort(); + int u4attributeLength = dataInput.readInt(); + String attributeName = clazz.getString(u2attributeNameIndex); + Attribute attribute = + attributeName.equals(ClassConstants.ATTR_SourceFile) ? (Attribute)new SourceFileAttribute(): + attributeName.equals(ClassConstants.ATTR_SourceDir) ? (Attribute)new SourceDirAttribute(): + attributeName.equals(ClassConstants.ATTR_InnerClasses) ? (Attribute)new InnerClassesAttribute(): + attributeName.equals(ClassConstants.ATTR_EnclosingMethod) ? (Attribute)new EnclosingMethodAttribute(): + attributeName.equals(ClassConstants.ATTR_Deprecated) ? (Attribute)new DeprecatedAttribute(): + attributeName.equals(ClassConstants.ATTR_Synthetic) ? (Attribute)new SyntheticAttribute(): + attributeName.equals(ClassConstants.ATTR_Signature) ? (Attribute)new SignatureAttribute(): + attributeName.equals(ClassConstants.ATTR_ConstantValue) ? (Attribute)new ConstantValueAttribute(): + attributeName.equals(ClassConstants.ATTR_Exceptions) ? (Attribute)new ExceptionsAttribute(): + attributeName.equals(ClassConstants.ATTR_Code) ? (Attribute)new CodeAttribute(): + attributeName.equals(ClassConstants.ATTR_StackMap) ? (Attribute)new StackMapAttribute(): + attributeName.equals(ClassConstants.ATTR_StackMapTable) ? (Attribute)new StackMapTableAttribute(): + attributeName.equals(ClassConstants.ATTR_LineNumberTable) ? (Attribute)new LineNumberTableAttribute(): + attributeName.equals(ClassConstants.ATTR_LocalVariableTable) ? (Attribute)new LocalVariableTableAttribute(): + attributeName.equals(ClassConstants.ATTR_LocalVariableTypeTable) ? (Attribute)new LocalVariableTypeTableAttribute(): + attributeName.equals(ClassConstants.ATTR_RuntimeVisibleAnnotations) ? (Attribute)new RuntimeVisibleAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleAnnotations) ? (Attribute)new RuntimeInvisibleAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations) ? (Attribute)new RuntimeVisibleParameterAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations) ? (Attribute)new RuntimeInvisibleParameterAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_AnnotationDefault) ? (Attribute)new AnnotationDefaultAttribute(): + (Attribute)new UnknownAttribute(u4attributeLength); + attribute.u2attributeNameIndex = u2attributeNameIndex; + + return attribute; + } + + + private StackMapFrame createStackMapFrame() + { + int u1tag = dataInput.readUnsignedByte(); + + return + u1tag < StackMapFrame.SAME_ONE_FRAME ? (StackMapFrame)new SameZeroFrame(u1tag) : + u1tag < StackMapFrame.SAME_ONE_FRAME_EXTENDED ? (StackMapFrame)new SameOneFrame(u1tag) : + u1tag < StackMapFrame.LESS_ZERO_FRAME ? (StackMapFrame)new SameOneFrame(u1tag) : + u1tag < StackMapFrame.SAME_ZERO_FRAME_EXTENDED ? (StackMapFrame)new LessZeroFrame(u1tag) : + u1tag < StackMapFrame.MORE_ZERO_FRAME ? (StackMapFrame)new SameZeroFrame(u1tag) : + u1tag < StackMapFrame.FULL_FRAME ? (StackMapFrame)new MoreZeroFrame(u1tag) : + (StackMapFrame)new FullFrame(); + } + + + private VerificationType createVerificationType() + { + int u1tag = dataInput.readUnsignedByte(); + + switch (u1tag) + { + case VerificationType.INTEGER_TYPE: return new IntegerType(); + case VerificationType.FLOAT_TYPE: return new FloatType(); + case VerificationType.LONG_TYPE: return new LongType(); + case VerificationType.DOUBLE_TYPE: return new DoubleType(); + case VerificationType.TOP_TYPE: return new TopType(); + case VerificationType.OBJECT_TYPE: return new ObjectType(); + case VerificationType.NULL_TYPE: return new NullType(); + case VerificationType.UNINITIALIZED_TYPE: return new UninitializedType(); + case VerificationType.UNINITIALIZED_THIS_TYPE: return new UninitializedThisType(); + + default: throw new RuntimeException("Unknown verification type ["+u1tag+"] in stack map frame"); + } + } + + + private ElementValue createElementValue() + { + int u1tag = dataInput.readUnsignedByte(); + + switch (u1tag) + { + case ClassConstants.INTERNAL_TYPE_BOOLEAN: + case ClassConstants.INTERNAL_TYPE_BYTE: + case ClassConstants.INTERNAL_TYPE_CHAR: + case ClassConstants.INTERNAL_TYPE_SHORT: + case ClassConstants.INTERNAL_TYPE_INT: + case ClassConstants.INTERNAL_TYPE_FLOAT: + case ClassConstants.INTERNAL_TYPE_LONG: + case ClassConstants.INTERNAL_TYPE_DOUBLE: + case ClassConstants.ELEMENT_VALUE_STRING_CONSTANT: return new ConstantElementValue(u1tag); + + case ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT: return new EnumConstantElementValue(); + case ClassConstants.ELEMENT_VALUE_CLASS: return new ClassElementValue(); + case ClassConstants.ELEMENT_VALUE_ANNOTATION: return new AnnotationElementValue(); + case ClassConstants.ELEMENT_VALUE_ARRAY: return new ArrayElementValue(); + + default: throw new IllegalArgumentException("Unknown element value tag ["+u1tag+"]"); + } + } +} diff --git a/src/proguard/classfile/io/ProgramClassWriter.java b/src/proguard/classfile/io/ProgramClassWriter.java new file mode 100644 index 0000000..e56f08a --- /dev/null +++ b/src/proguard/classfile/io/ProgramClassWriter.java @@ -0,0 +1,690 @@ +/* + * 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.classfile.io; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.*; + +/** + * This ClassVisitor writes out the ProgramClass objects that it visits to the + * given DataOutput object. + * + * @author Eric Lafortune + */ +public class ProgramClassWriter +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor +{ + private RuntimeDataOutput dataOutput; + + private final ConstantBodyWriter constantBodyWriter = new ConstantBodyWriter(); + private final AttributeBodyWriter attributeBodyWriter = new AttributeBodyWriter(); + private final StackMapFrameBodyWriter stackMapFrameBodyWriter = new StackMapFrameBodyWriter(); + private final VerificationTypeBodyWriter verificationTypeBodyWriter = new VerificationTypeBodyWriter(); + private final ElementValueBodyWriter elementValueBodyWriter = new ElementValueBodyWriter(); + + + /** + * Creates a new ProgramClassWriter for reading from the given DataOutput. + */ + public ProgramClassWriter(DataOutput dataOutput) + { + this.dataOutput = new RuntimeDataOutput(dataOutput); + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Write the magic number. + dataOutput.writeInt(programClass.u4magic); + + // Write the version numbers. + dataOutput.writeShort(ClassUtil.internalMinorClassVersion(programClass.u4version)); + dataOutput.writeShort(ClassUtil.internalMajorClassVersion(programClass.u4version)); + + // Write the constant pool. + dataOutput.writeShort(programClass.u2constantPoolCount); + + programClass.constantPoolEntriesAccept(this); + + // Write the general class information. + dataOutput.writeShort(programClass.u2accessFlags); + dataOutput.writeShort(programClass.u2thisClass); + dataOutput.writeShort(programClass.u2superClass); + + // Write the interfaces. + dataOutput.writeShort(programClass.u2interfacesCount); + + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + dataOutput.writeShort(programClass.u2interfaces[index]); + } + + // Write the fields. + dataOutput.writeShort(programClass.u2fieldsCount); + + programClass.fieldsAccept(this); + + // Write the methods. + dataOutput.writeShort(programClass.u2methodsCount); + + programClass.methodsAccept(this); + + // Write the class attributes. + dataOutput.writeShort(programClass.u2attributesCount); + + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Write the general field information. + dataOutput.writeShort(programField.u2accessFlags); + dataOutput.writeShort(programField.u2nameIndex); + dataOutput.writeShort(programField.u2descriptorIndex); + + // Write the field attributes. + dataOutput.writeShort(programField.u2attributesCount); + + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Write the general method information. + dataOutput.writeShort(programMethod.u2accessFlags); + dataOutput.writeShort(programMethod.u2nameIndex); + dataOutput.writeShort(programMethod.u2descriptorIndex); + + // Write the method attributes. + dataOutput.writeShort(programMethod.u2attributesCount); + + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + // Write the tag. + dataOutput.writeByte(constant.getTag()); + + // Write the actual body. + constant.accept(clazz, constantBodyWriter); + } + + + private class ConstantBodyWriter + extends SimplifiedVisitor + implements ConstantVisitor + { + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + dataOutput.writeInt(integerConstant.u4value); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + dataOutput.writeLong(longConstant.u8value); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + dataOutput.writeFloat(floatConstant.f4value); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + dataOutput.writeDouble(doubleConstant.f8value); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + dataOutput.writeShort(stringConstant.u2stringIndex); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + byte[] bytes = utf8Constant.getBytes(); + + dataOutput.writeShort(bytes.length); + dataOutput.write(bytes); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + dataOutput.writeShort(refConstant.u2classIndex); + dataOutput.writeShort(refConstant.u2nameAndTypeIndex); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + dataOutput.writeShort(classConstant.u2nameIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + dataOutput.writeShort(nameAndTypeConstant.u2nameIndex); + dataOutput.writeShort(nameAndTypeConstant.u2descriptorIndex); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + // Write the attribute name index. + dataOutput.writeShort(attribute.u2attributeNameIndex); + + // We'll write the attribute body into an array first, so we can + // automatically figure out its length. + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + // Temporarily replace the current data output. + RuntimeDataOutput oldDataOutput = dataOutput; + dataOutput = new RuntimeDataOutput(new DataOutputStream(byteArrayOutputStream)); + + // Write the attribute body into the array. Note that the + // accept method with two dummy null arguments never throws + // an UnsupportedOperationException. + attribute.accept(clazz, null, null, attributeBodyWriter); + + // Restore the original data output. + dataOutput = oldDataOutput; + + // Write the attribute length and body. + byte[] info = byteArrayOutputStream.toByteArray(); + + dataOutput.writeInt(info.length); + dataOutput.write(info); + } + + + private class AttributeBodyWriter + extends SimplifiedVisitor + implements AttributeVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor + { + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // Write the unknown information. + dataOutput.write(unknownAttribute.info); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + dataOutput.writeShort(sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + dataOutput.writeShort(sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Write the inner classes. + dataOutput.writeShort(innerClassesAttribute.u2classesCount); + + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + dataOutput.writeShort(enclosingMethodAttribute.u2classIndex); + dataOutput.writeShort(enclosingMethodAttribute.u2nameAndTypeIndex); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + // This attribute does not contain any additional information. + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + // This attribute does not contain any additional information. + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + dataOutput.writeShort(signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + dataOutput.writeShort(constantValueAttribute.u2constantValueIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + // Write the exceptions. + dataOutput.writeShort(exceptionsAttribute.u2exceptionIndexTableLength); + + for (int index = 0; index < exceptionsAttribute.u2exceptionIndexTableLength; index++) + { + dataOutput.writeShort(exceptionsAttribute.u2exceptionIndexTable[index]); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Write the stack size and local variable frame size. + dataOutput.writeShort(codeAttribute.u2maxStack); + dataOutput.writeShort(codeAttribute.u2maxLocals); + + // Write the byte code. + dataOutput.writeInt(codeAttribute.u4codeLength); + + dataOutput.write(codeAttribute.code, 0, codeAttribute.u4codeLength); + + // Write the exceptions. + dataOutput.writeShort(codeAttribute.u2exceptionTableLength); + + codeAttribute.exceptionsAccept(clazz, method, this); + + // Write the code attributes. + dataOutput.writeShort(codeAttribute.u2attributesCount); + + codeAttribute.attributesAccept(clazz, method, ProgramClassWriter.this); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // Write the stack map frames (only full frames, without tag). + dataOutput.writeShort(stackMapAttribute.u2stackMapFramesCount); + + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, stackMapFrameBodyWriter); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // Write the stack map frames. + dataOutput.writeShort(stackMapTableAttribute.u2stackMapFramesCount); + + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Write the line numbers. + dataOutput.writeShort(lineNumberTableAttribute.u2lineNumberTableLength); + + lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Write the local variables. + dataOutput.writeShort(localVariableTableAttribute.u2localVariableTableLength); + + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Write the local variable types. + dataOutput.writeShort(localVariableTypeTableAttribute.u2localVariableTypeTableLength); + + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Write the annotations. + dataOutput.writeShort(annotationsAttribute.u2annotationsCount); + + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Write the parameter annotations. + dataOutput.writeByte(parameterAnnotationsAttribute.u2parametersCount); + + for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++) + { + // Write the parameter annotations of the given parameter. + int u2annotationsCount = parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]; + Annotation[] annotations = parameterAnnotationsAttribute.parameterAnnotations[parameterIndex]; + + dataOutput.writeShort(u2annotationsCount); + + for (int index = 0; index < u2annotationsCount; index++) + { + Annotation annotation = annotations[index]; + this.visitAnnotation(clazz, annotation); + } + + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Write the default element value. + annotationDefaultAttribute.defaultValue.accept(clazz, null, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + dataOutput.writeShort(innerClassesInfo.u2innerClassIndex); + dataOutput.writeShort(innerClassesInfo.u2outerClassIndex); + dataOutput.writeShort(innerClassesInfo.u2innerNameIndex); + dataOutput.writeShort(innerClassesInfo.u2innerClassAccessFlags); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + dataOutput.writeShort(exceptionInfo.u2startPC); + dataOutput.writeShort(exceptionInfo.u2endPC); + dataOutput.writeShort(exceptionInfo.u2handlerPC); + dataOutput.writeShort(exceptionInfo.u2catchType); + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + // Write the stack map frame tag. + dataOutput.writeByte(stackMapFrame.getTag()); + + // Write the actual body. + stackMapFrame.accept(clazz, method, codeAttribute, offset, stackMapFrameBodyWriter); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + dataOutput.writeShort(lineNumberInfo.u2startPC); + dataOutput.writeShort(lineNumberInfo.u2lineNumber); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + dataOutput.writeShort(localVariableInfo.u2startPC); + dataOutput.writeShort(localVariableInfo.u2length); + dataOutput.writeShort(localVariableInfo.u2nameIndex); + dataOutput.writeShort(localVariableInfo.u2descriptorIndex); + dataOutput.writeShort(localVariableInfo.u2index); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + dataOutput.writeShort(localVariableTypeInfo.u2startPC); + dataOutput.writeShort(localVariableTypeInfo.u2length); + dataOutput.writeShort(localVariableTypeInfo.u2nameIndex); + dataOutput.writeShort(localVariableTypeInfo.u2signatureIndex); + dataOutput.writeShort(localVariableTypeInfo.u2index); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Write the annotation type. + dataOutput.writeShort(annotation.u2typeIndex); + + // Write the element value pairs. + dataOutput.writeShort(annotation.u2elementValuesCount); + + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + // Write the element name index, if applicable. + int u2elementNameIndex = elementValue.u2elementNameIndex; + if (u2elementNameIndex != 0) + { + dataOutput.writeShort(u2elementNameIndex); + } + + // Write the tag. + dataOutput.writeByte(elementValue.getTag()); + + // Write the actual body. + elementValue.accept(clazz, annotation, elementValueBodyWriter); + } + } + + + private class StackMapFrameBodyWriter + extends SimplifiedVisitor + implements StackMapFrameVisitor, + VerificationTypeVisitor + { + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + if (sameZeroFrame.getTag() == StackMapFrame.SAME_ZERO_FRAME_EXTENDED) + { + dataOutput.writeShort(sameZeroFrame.u2offsetDelta); + } + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + if (sameOneFrame.getTag() == StackMapFrame.SAME_ONE_FRAME_EXTENDED) + { + dataOutput.writeShort(sameOneFrame.u2offsetDelta); + } + + // Write the verification type of the stack entry. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + dataOutput.writeShort(lessZeroFrame.u2offsetDelta); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + dataOutput.writeShort(moreZeroFrame.u2offsetDelta); + + // Write the verification types of the additional local variables. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + dataOutput.writeShort(fullFrame.u2offsetDelta); + + // Write the verification types of the local variables. + dataOutput.writeShort(fullFrame.variablesCount); + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + + // Write the verification types of the stack entries. + dataOutput.writeShort(fullFrame.stackCount); + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + // Write the verification type tag. + dataOutput.writeByte(verificationType.getTag()); + + // Write the actual body. + verificationType.accept(clazz, method, codeAttribute, offset, verificationTypeBodyWriter); + } + } + + + private class VerificationTypeBodyWriter + extends SimplifiedVisitor + implements VerificationTypeVisitor + { + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + // Most verification types don't contain any additional information. + } + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + dataOutput.writeShort(objectType.u2classIndex); + } + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + dataOutput.writeShort(uninitializedType.u2newInstructionOffset); + } + } + + + private class ElementValueBodyWriter + extends SimplifiedVisitor + implements ElementValueVisitor + { + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + dataOutput.writeShort(constantElementValue.u2constantValueIndex); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + dataOutput.writeShort(enumConstantElementValue.u2typeNameIndex); + dataOutput.writeShort(enumConstantElementValue.u2constantNameIndex); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + dataOutput.writeShort(classElementValue.u2classInfoIndex); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Write the annotation. + attributeBodyWriter.visitAnnotation(clazz, annotationElementValue.annotationValue); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Write the element values. + dataOutput.writeShort(arrayElementValue.u2elementValuesCount); + + arrayElementValue.elementValuesAccept(clazz, annotation, attributeBodyWriter); + } + } +} diff --git a/src/proguard/classfile/io/RuntimeDataInput.java b/src/proguard/classfile/io/RuntimeDataInput.java new file mode 100644 index 0000000..104b7c9 --- /dev/null +++ b/src/proguard/classfile/io/RuntimeDataInput.java @@ -0,0 +1,223 @@ +/* + * 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.classfile.io; + +import java.io.*; + +/** + * This class delegates its method calls to the corresponding DataInput methods, + * converting its IOExceptions to RuntimeExceptions. + * + * @author Eric Lafortune + */ +final class RuntimeDataInput +{ + private final DataInput dataInput; + + + public RuntimeDataInput(DataInput dataInput) + { + this.dataInput = dataInput; + } + + + // Methods delegating to DataInput. + + public boolean readBoolean() + { + try + { + return dataInput.readBoolean(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public byte readByte() + { + try + { + return dataInput.readByte(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public char readChar() + { + try + { + return dataInput.readChar(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public double readDouble() + { + try + { + return dataInput.readDouble(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public float readFloat() + { + try + { + return dataInput.readFloat(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public void readFully(byte[] b) + { + try + { + dataInput.readFully(b); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public void readFully(byte[] b, int off, int len) + { + try + { + dataInput.readFully(b, off, len); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public int readInt() + { + try + { + return dataInput.readInt(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public String readLine() + { + try + { + return dataInput.readLine(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public long readLong() + { + try + { + return dataInput.readLong(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public short readShort() + { + try + { + return dataInput.readShort(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public int readUnsignedByte() + { + try + { + return dataInput.readUnsignedByte(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public int readUnsignedShort() + { + try + { + return dataInput.readUnsignedShort(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public String readUTF() + { + try + { + return dataInput.readUTF(); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + public int skipBytes(int n) + { + try + { + return dataInput.skipBytes(n); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } +} diff --git a/src/proguard/classfile/io/RuntimeDataOutput.java b/src/proguard/classfile/io/RuntimeDataOutput.java new file mode 100644 index 0000000..ffde3af --- /dev/null +++ b/src/proguard/classfile/io/RuntimeDataOutput.java @@ -0,0 +1,224 @@ +/* + * 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.classfile.io; + +import java.io.*; + +/** + * This class delegates its method calls to the corresponding DataOutput methods, + * converting its IOExceptions to RuntimeExceptions. + * + * @author Eric Lafortune + */ +final class RuntimeDataOutput +{ + private final DataOutput dataOutput; + + + public RuntimeDataOutput(DataOutput dataOutput) + { + this.dataOutput = dataOutput; + } + + + // Methods delegating to DataOutput. + + public void write(byte[] b) + { + try + { + dataOutput.write(b); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void write(byte[] b, int off, int len) + { + try + { + dataOutput.write(b, off, len); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void write(int b) + { + try + { + dataOutput.write(b); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeBoolean(boolean v) + { + try + { + dataOutput.writeBoolean(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeByte(int v) + { + try + { + dataOutput.writeByte(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeBytes(String s) + { + try + { + dataOutput.writeBytes(s); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeChar(int v) + { + try + { + dataOutput.writeChar(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeChars(String s) + { + try + { + dataOutput.writeChars(s); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeDouble(double v) + { + try + { + dataOutput.writeDouble(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeFloat(float v) + { + try + { + dataOutput.writeFloat(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeInt(int v) + { + try + { + dataOutput.writeInt(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeLong(long v) + { + try + { + dataOutput.writeLong(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeShort(int v) + { + try + { + dataOutput.writeShort(v); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + + public void writeUTF(String str) + { + try + { + dataOutput.writeUTF(str); + } + catch (IOException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } +} diff --git a/src/proguard/classfile/io/package.html b/src/proguard/classfile/io/package.html new file mode 100644 index 0000000..780b917 --- /dev/null +++ b/src/proguard/classfile/io/package.html @@ -0,0 +1,3 @@ +<body> +This package contains classes for reading and writing class files. +</body> diff --git a/src/proguard/classfile/package.html b/src/proguard/classfile/package.html new file mode 100644 index 0000000..fad087c --- /dev/null +++ b/src/proguard/classfile/package.html @@ -0,0 +1,15 @@ +<body> +This package contains classes to represent the various elements of class files. +<p> +A class file is represented by the <code>{@link proguard.classfile.ClassFile +ClassFile}</code> interface. This interface currently has two alternative +representations: +<ul> +<li><code>{@link ProgramClassFile ProgramClassFile}</code>: + a complete representation that can be read, modified, and written back. +<li><code>{@link LibraryClassFile LibraryClassFile}</code>: + an incomplete representation that can be only be read. It is however + more compact than <code>ProgramClassFile</code>, and sufficient for + analyzing class files from library jars. +</ul> +</body> diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java new file mode 100644 index 0000000..3ad6961 --- /dev/null +++ b/src/proguard/classfile/util/AccessUtil.java @@ -0,0 +1,105 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * Utility methods for working with access flags. For convenience, this class + * defines access levels, in ascending order: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, and <code>PUBLIC</code>. + * + * @author Eric Lafortune + */ +public class AccessUtil +{ + public static final int PRIVATE = 0; + public static final int PACKAGE_VISIBLE = 1; + public static final int PROTECTED = 2; + public static final int PUBLIC = 3; + + + // The mask of access flags. + private static final int ACCESS_MASK = + ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PROTECTED; + + + /** + * Returns the corresponding access level of the given access flags. + * @param accessFlags the internal access flags. + * @return the corresponding access level: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, or + * <code>PUBLIC</code>. + */ + public static int accessLevel(int accessFlags) + { + switch (accessFlags & ACCESS_MASK) + { + case ClassConstants.INTERNAL_ACC_PRIVATE: return PRIVATE; + default: return PACKAGE_VISIBLE; + case ClassConstants.INTERNAL_ACC_PROTECTED: return PROTECTED; + case ClassConstants.INTERNAL_ACC_PUBLIC: return PUBLIC; + } + } + + + /** + * Returns the corresponding access flags of the given access level. + * @param accessLevel the access level: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, + * or <code>PUBLIC</code>. + * @return the corresponding internal access flags, the internal access + * flags as a logical bit mask of <code>INTERNAL_ACC_PRIVATE</code>, + * <code>INTERNAL_ACC_PROTECTED</code>, and + * <code>INTERNAL_ACC_PUBLIC</code>. + */ + public static int accessFlags(int accessLevel) + { + switch (accessLevel) + { + case PRIVATE: return ClassConstants.INTERNAL_ACC_PRIVATE; + default: return 0; + case PROTECTED: return ClassConstants.INTERNAL_ACC_PROTECTED; + case PUBLIC: return ClassConstants.INTERNAL_ACC_PUBLIC; + } + } + + + /** + * Replaces the access part of the given access flags. + * @param accessFlags the internal access flags. + * @param accessFlags the new internal access flags. + */ + public static int replaceAccessFlags(int accessFlags, int newAccessFlags) + { + // A private class member should not be explicitly final. + if (newAccessFlags == ClassConstants.INTERNAL_ACC_PRIVATE) + { + accessFlags &= ~ClassConstants.INTERNAL_ACC_FINAL; + } + + return (accessFlags & ~ACCESS_MASK) | + (newAccessFlags & ACCESS_MASK); + } +} diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java new file mode 100644 index 0000000..b1f2c27 --- /dev/null +++ b/src/proguard/classfile/util/ClassReferenceInitializer.java @@ -0,0 +1,545 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +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.visitor.*; + + +/** + * This ClassVisitor initializes the references of all classes that + * it visits. + * <p> + * All class constant pool entries get direct references to the corresponding + * classes. These references make it more convenient to travel up and across + * the class hierarchy. + * <p> + * All field and method reference constant pool entries get direct references + * to the corresponding classes, fields, and methods. + * <p> + * All name and type constant pool entries get a list of direct references to + * the classes listed in the type. + * <p> + * This visitor optionally prints warnings if some items can't be found. + * <p> + * The class hierarchy must be initialized before using this visitor. + * + * @author Eric Lafortune + */ +public class ClassReferenceInitializer +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingClassWarningPrinter; + private final WarningPrinter missingMemberWarningPrinter; + private final WarningPrinter dependencyWarningPrinter; + + private final MemberFinder memberFinder = new MemberFinder(); + + + /** + * Creates a new ClassReferenceInitializer that initializes the references + * of all visited class files, optionally printing warnings if some classes + * or class members can't be found or if they are in the program class pool. + */ + public ClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingClassWarningPrinter, + WarningPrinter missingMemberWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingClassWarningPrinter = missingClassWarningPrinter; + this.missingMemberWarningPrinter = missingMemberWarningPrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Initialize the constant pool entries. + programClass.constantPoolEntriesAccept(this); + + // Initialize all fields and methods. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Initialize the attributes. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Initialize all fields and methods. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + programField.referencedClass = + findReferencedClass(programClass.getName(), + programField.getDescriptor(programClass)); + + // Initialize the attributes. + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + programMethod.referencedClasses = + findReferencedClasses(programClass.getName(), + programMethod.getDescriptor(programClass)); + + // Initialize the attributes. + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + libraryField.referencedClass = + findReferencedClass(libraryClass.getName(), + libraryField.getDescriptor(libraryClass)); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + libraryMethod.referencedClasses = + findReferencedClasses(libraryClass.getName(), + libraryMethod.getDescriptor(libraryClass)); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Fill out the String class. + stringConstant.javaLangStringClass = + findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + String className = refConstant.getClassName(clazz); + + // See if we can find the referenced class. + // Unresolved references are assumed to refer to library classes + // that will not change anyway. + Clazz referencedClass = findClass(clazz.getName(), className); + + if (referencedClass != null && + !ClassUtil.isInternalArrayType(className)) + { + String name = refConstant.getName(clazz); + String type = refConstant.getType(clazz); + + boolean isFieldRef = refConstant.getTag() == ClassConstants.CONSTANT_Fieldref; + + // See if we can find the referenced class member somewhere in the + // hierarchy. + refConstant.referencedMember = memberFinder.findMember(clazz, + referencedClass, + name, + type, + isFieldRef); + refConstant.referencedClass = memberFinder.correspondingClass(); + + if (refConstant.referencedMember == null) + { + // We've haven't found the class member anywhere in the hierarchy. + missingMemberWarningPrinter.print(clazz.getName(), + className, + "Warning: " + + ClassUtil.externalClassName(clazz.getName()) + + ": can't find referenced " + + (isFieldRef ? + "field '" + ClassUtil.externalFullFieldDescription(0, name, type) : + "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) + + "' in class " + + ClassUtil.externalClassName(className)); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + String className = clazz.getName(); + + // Fill out the referenced class. + classConstant.referencedClass = + findClass(className, classConstant.getName(clazz)); + + // Fill out the Class class. + classConstant.javaLangClassClass = + findClass(className, ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + String className = clazz.getName(); + String enclosingClassName = enclosingMethodAttribute.getClassName(clazz); + + // See if we can find the referenced class. + Clazz referencedClass = findClass(className, enclosingClassName); + + if (referencedClass == null) + { + // We couldn't find the enclosing class. + missingClassWarningPrinter.print(className, + enclosingClassName, + "Warning: " + + ClassUtil.externalClassName(className) + + ": can't find enclosing class " + + ClassUtil.externalClassName(enclosingClassName)); + return; + } + + // Make sure there is actually an enclosed method. + if (enclosingMethodAttribute.u2nameAndTypeIndex == 0) + { + return; + } + + String name = enclosingMethodAttribute.getName(clazz); + String type = enclosingMethodAttribute.getType(clazz); + + // See if we can find the method in the referenced class. + Method referencedMethod = referencedClass.findMethod(name, type); + + if (referencedMethod == null) + { + // We couldn't find the enclosing method. + missingMemberWarningPrinter.print(className, + enclosingClassName, + "Warning: " + + ClassUtil.externalClassName(className) + + ": can't find enclosing method '" + + ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) + + "' in class " + + ClassUtil.externalClassName(enclosingClassName)); + return; + } + + // Save the references. + enclosingMethodAttribute.referencedClass = referencedClass; + enclosingMethodAttribute.referencedMethod = referencedMethod; + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Initialize the nested attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Initialize the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Initialize the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + signatureAttribute.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(signatureAttribute.u2signatureIndex)); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Initialize the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Initialize the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Initialize the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.referencedClass = + findReferencedClass(clazz.getName(), + clazz.getString(localVariableInfo.u2descriptorIndex)); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(localVariableTypeInfo.u2signatureIndex)); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(annotation.u2typeIndex)); + + // Initialize the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + initializeElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + initializeElementValue(clazz, annotation, enumConstantElementValue); + + enumConstantElementValue.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(enumConstantElementValue.u2typeNameIndex)); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + initializeElementValue(clazz, annotation, classElementValue); + + classElementValue.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(classElementValue.u2classInfoIndex)); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + initializeElementValue(clazz, annotation, annotationElementValue); + + // Initialize the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + initializeElementValue(clazz, annotation, arrayElementValue); + + // Initialize the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + /** + * Initializes the referenced method of an element value, if any. + */ + private void initializeElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + // See if we have a referenced class. + if (annotation != null && + annotation.referencedClasses != null && + elementValue.u2elementNameIndex != 0) + { + // See if we can find the method in the referenced class + // (ignoring the descriptor). + String name = clazz.getString(elementValue.u2elementNameIndex); + + Clazz referencedClass = annotation.referencedClasses[0]; + elementValue.referencedClass = referencedClass; + elementValue.referencedMethod = referencedClass.findMethod(name, null); + } + } + + + // Small utility methods. + + /** + * Returns the single class referenced by the given descriptor, or + * <code>null</code> if there isn't any useful reference. + */ + private Clazz findReferencedClass(String referencingClassName, + String descriptor) + { + DescriptorClassEnumeration enumeration = + new DescriptorClassEnumeration(descriptor); + + enumeration.nextFluff(); + + if (enumeration.hasMoreClassNames()) + { + return findClass(referencingClassName, enumeration.nextClassName()); + } + + return null; + } + + + /** + * Returns an array of classes referenced by the given descriptor, or + * <code>null</code> if there aren't any useful references. + */ + private Clazz[] findReferencedClasses(String referencingClassName, + String descriptor) + { + DescriptorClassEnumeration enumeration = + new DescriptorClassEnumeration(descriptor); + + int classCount = enumeration.classCount(); + if (classCount > 0) + { + Clazz[] referencedClasses = new Clazz[classCount]; + + boolean foundReferencedClasses = false; + + for (int index = 0; index < classCount; index++) + { + String fluff = enumeration.nextFluff(); + String name = enumeration.nextClassName(); + + Clazz referencedClass = findClass(referencingClassName, name); + + if (referencedClass != null) + { + referencedClasses[index] = referencedClass; + foundReferencedClasses = true; + } + } + + if (foundReferencedClasses) + { + return referencedClasses; + } + } + + return null; + } + + + /** + * Returns the class with the given name, either for the program class pool + * or from the library class pool, or <code>null</code> if it can't be found. + */ + private Clazz findClass(String referencingClassName, String name) + { + // Ignore any primitive array types. + if (ClassUtil.isInternalArrayType(name) && + !ClassUtil.isInternalClassType(name)) + { + return null; + } + + // First look for the class in the program class pool. + Clazz clazz = programClassPool.getClass(name); + + // Otherwise look for the class in the library class pool. + if (clazz == null) + { + clazz = libraryClassPool.getClass(name); + + if (clazz == null && + missingClassWarningPrinter != null) + { + // We didn't find the superclass or interface. Print a warning. + missingClassWarningPrinter.print(referencingClassName, + name, + "Warning: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find referenced class " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " depends on program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java new file mode 100644 index 0000000..30fd526 --- /dev/null +++ b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java @@ -0,0 +1,77 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor adds all classes that it visits to the list of subclasses + * of their superclass. These subclass lists make it more convenient to travel + * + * @author Eric Lafortune + */ +public class ClassSubHierarchyInitializer +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Add this class to the subclasses of its superclass. + addSubclass(programClass, programClass.getSuperClass()); + + // Add this class to the subclasses of its interfaces. + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + addSubclass(programClass, programClass.getInterface(index)); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Add this class to the subclasses of its superclass, + addSubclass(libraryClass, libraryClass.superClass); + + // Add this class to the subclasses of its interfaces. + Clazz[] interfaceClasses = libraryClass.interfaceClasses; + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + // Add this class to the subclasses of the interface class. + addSubclass(libraryClass, interfaceClasses[index]); + } + } + } + + + // Small utility methods. + + private void addSubclass(Clazz subclass, Clazz clazz) + { + if (clazz != null) + { + clazz.addSubClass(subclass); + } + } +} diff --git a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java new file mode 100644 index 0000000..af2a209 --- /dev/null +++ b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java @@ -0,0 +1,168 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor initializes the superclass hierarchy of all classes that + * it visits. + * <p> + * Visited library classes get direct references to their superclasses and + * interfaces, replacing the superclass names and interface names. The direct + * references are equivalent to the names, but they are more efficient to work + * with. + * <p> + * This visitor optionally prints warnings if some superclasses can't be found + * or if they are in the program class pool. + * + * @author Eric Lafortune + */ +public class ClassSuperHierarchyInitializer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingWarningPrinter; + private final WarningPrinter dependencyWarningPrinter; + + + /** + * Creates a new ClassSuperHierarchyInitializer that initializes the super + * hierarchy of all visited class files, optionally printing warnings if + * some classes can't be found or if they are in the program class pool. + */ + public ClassSuperHierarchyInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingWarningPrinter = missingWarningPrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Link to the super class. + programClass.superClassConstantAccept(this); + + // Link to the interfaces. + programClass.interfaceConstantsAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + String className = libraryClass.getName(); + + // Link to the super class. + String superClassName = libraryClass.superClassName; + if (superClassName != null) + { + // Keep a reference to the superclass. + libraryClass.superClass = findClass(className, superClassName); + } + + // Link to the interfaces. + if (libraryClass.interfaceNames != null) + { + String[] interfaceNames = libraryClass.interfaceNames; + Clazz[] interfaceClasses = new Clazz[interfaceNames.length]; + + for (int index = 0; index < interfaceNames.length; index++) + { + // Keep a reference to the interface class. + interfaceClasses[index] = + findClass(className, interfaceNames[index]); + } + + libraryClass.interfaceClasses = interfaceClasses; + } + + // Discard the name Strings. From now on, we'll use the object + // references. + libraryClass.superClassName = null; + libraryClass.interfaceNames = null; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.referencedClass = + findClass(clazz.getName(), classConstant.getName(clazz)); + } + + + // Small utility methods. + + /** + * Returns the class with the given name, either for the program class pool + * or from the library class pool, or <code>null</code> if it can't be found. + */ + private Clazz findClass(String referencingClassName, String name) + { + // First look for the class in the program class pool. + Clazz clazz = programClassPool.getClass(name); + + // Otherwise look for the class in the library class pool. + if (clazz == null) + { + clazz = libraryClassPool.getClass(name); + + if (clazz == null && + missingWarningPrinter != null) + { + // We didn't find the superclass or interface. Print a warning. + missingWarningPrinter.print(referencingClassName, + name, + "Warning: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find superclass or interface " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " extends or implements program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java new file mode 100644 index 0000000..5f25f01 --- /dev/null +++ b/src/proguard/classfile/util/ClassUtil.java @@ -0,0 +1,1154 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + +import java.util.List; + +/** + * Utility methods for converting between internal and external representations + * of names and descriptions. + * + * @author Eric Lafortune + */ +public class ClassUtil +{ + private static final String EMPTY_STRING = ""; + + + /** + * Checks whether the given class magic number is correct. + * @param magicNumber the magic number. + * @throws UnsupportedOperationException when the magic number is incorrect. + */ + public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException + { + if (magicNumber != ClassConstants.MAGIC) + { + throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class"); + } + } + + + /** + * Returns the combined class version number. + * @param majorVersion the major part of the class version number. + * @param minorVersion the minor part of the class version number. + * @return the combined class version number. + */ + public static int internalClassVersion(int majorVersion, int minorVersion) + { + return (majorVersion << 16) | minorVersion; + } + + + /** + * Returns the major part of the given class version number. + * @param classVersion the combined class version number. + * @return the major part of the class version number. + */ + public static int internalMajorClassVersion(int classVersion) + { + return classVersion >>> 16; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalMinorClassVersion(int classVersion) + { + return classVersion & 0xffff; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalClassVersion(String classVersion) + { + return + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 : + 0; + } + + + /** + * Returns the minor part of the given class version number. + * @param classVersion the combined class version number. + * @return the minor part of the class version number. + */ + public static String externalClassVersion(int classVersion) + { + switch (classVersion) + { + case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0; + case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2; + case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3; + case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4; + case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5; + case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6; + default: return null; + } + } + + + /** + * Checks whether the given class version number is supported. + * @param classVersion the combined class version number. + * @throws UnsupportedOperationException when the version is not supported. + */ + public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException + { + if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 || + classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6) + { + throw new UnsupportedOperationException("Unsupported version number ["+ + internalMajorClassVersion(classVersion)+"."+ + internalMinorClassVersion(classVersion)+"] for class format"); + } + } + + + /** + * Converts an external class name into an internal class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassName(String externalClassName) + { + return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class description into an external class description. + * @param accessFlags the access flags of the class. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class description, + * e.g. "<code>public java.lang.Object</code>". + */ + public static String externalFullClassDescription(int accessFlags, + String internalClassName) + { + return externalClassAccessFlags(accessFlags) + + externalClassName(internalClassName); + } + + + /** + * Converts an internal class name into an external class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class name, + * e.g. "<code>java.lang.Object</code>". + */ + public static String externalClassName(String internalClassName) + { + return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) && + //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ? + //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) : + internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class name into an external short class name, without + * package specification. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the external short class name, + * e.g. "<code>Object</code>". + */ + public static String externalShortClassName(String externalClassName) + { + int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + return externalClassName.substring(index+1); + } + + + /** + * Returns whether the given internal type is an array type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is an array type, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayType(String internalType) + { + return internalType.length() > 1 && + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY; + } + + + /** + * Returns the number of dimensions of the given internal type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int internalArrayTypeDimensionCount(String internalType) + { + int dimensions = 0; + while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + dimensions++; + } + + return dimensions; + } + + + /** + * Returns whether the given internal class name is one of the interfaces + * that is implemented by all array types. These class names are + * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and + * "<code>java/io/Serializable</code>" + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return <code>true</code> if the given type is an array interface name, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayInterfaceName(String internalClassName) + { + return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName); + } + + + /** + * Returns whether the given internal type is a plain primitive type + * (not void). + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalPrimitiveType(char internalType) + { + return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN || + internalType == ClassConstants.INTERNAL_TYPE_BYTE || + internalType == ClassConstants.INTERNAL_TYPE_CHAR || + internalType == ClassConstants.INTERNAL_TYPE_SHORT || + internalType == ClassConstants.INTERNAL_TYPE_INT || + internalType == ClassConstants.INTERNAL_TYPE_FLOAT || + internalType == ClassConstants.INTERNAL_TYPE_LONG || + internalType == ClassConstants.INTERNAL_TYPE_DOUBLE; + } + + + /** + * Returns whether the given internal type is a primitive Category 2 type. + * @param internalType the internal type, + * e.g. "<code>L</code>". + * @return <code>true</code> if the given type is a Category 2 type, + * <code>false</code> otherwise. + */ + public static boolean isInternalCategory2Type(String internalType) + { + return internalType.length() == 1 && + (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG || + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE); + } + + + /** + * Returns whether the given internal type is a plain class type + * (including an array type of a plain class type). + * @param internalType the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalClassType(String internalType) + { + int length = internalType.length(); + return length > 1 && +// internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_CLASS_START && + internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END; + } + + + /** + * Returns the internal type of a given class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalTypeFromClassName(String internalClassName) + { + return internalArrayTypeFromClassName(internalClassName, 0); + } + + + /** + * Returns the internal array type of a given class name with a given number + * of dimensions. If the number of dimensions is 0, the class name itself is + * returned. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @param dimensionCount the number of array dimensions. + * @return the internal array type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalArrayTypeFromClassName(String internalClassName, + int dimensionCount) + { + StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2); + + for (int dimension = 0; dimension < dimensionCount; dimension++) + { + buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + } + + return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START) + .append(internalClassName) + .append(ClassConstants.INTERNAL_TYPE_CLASS_END) + .toString(); + } + + + /** + * Returns the internal element type of a given internal array type. + * @param internalArrayType the internal array type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the internal type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>I</code>". + */ + public static String internalTypeFromArrayType(String internalArrayType) + { + int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY); + return internalArrayType.substring(index+1); + } + + + /** + * Returns the internal class name of a given internal class type + * (including an array type). Types involving primitive types are returned + * unchanged. + * @param internalClassType the internal class type, + * e.g. "<code>[Ljava/lang/Object;</code>", + * "<code>Ljava/lang/Object;</code>", or + * "<code>java/lang/Object</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassNameFromClassType(String internalClassType) + { + return isInternalClassType(internalClassType) ? + internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1, + internalClassType.length()-1) : + internalClassType; + } + + + /** + * Returns the internal class name of any given internal descriptor type, + * disregarding array prefixes. + * @param internalClassType the internal class type, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>[[I</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>" or + * <code>null</code>. + */ + public static String internalClassNameFromType(String internalClassType) + { + if (!isInternalClassType(internalClassType)) + { + return null; + } + + // Is it an array type? + if (isInternalArrayType(internalClassType)) + { + internalClassType = internalTypeFromArrayType(internalClassType); + } + + return internalClassNameFromClassType(internalClassType); + } + + + /** + * Returns the internal type of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the internal return type, + * e.g. "<code>Z</code>". + */ + public static String internalMethodReturnType(String internalMethodDescriptor) + { + int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + return internalMethodDescriptor.substring(index + 1); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the number of parameters, + * e.g. 2. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int counter = 0; + while (internalTypeEnumeration.hasMoreTypes()) + { + internalTypeEnumeration.nextType(); + + counter++; + } + + return counter; + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the size taken up on the stack, + * e.g. 3. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor) + { + return internalMethodParameterSize(internalMethodDescriptor, true); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param accessFlags the access flags of the method, + * e.g. 0. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + int accessFlags) + { + return internalMethodParameterSize(internalMethodDescriptor, + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two spaces, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param isStatic specifies whether the method is static, + * e.g. false. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + boolean isStatic) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int size = isStatic ? 0 : 1; + while (internalTypeEnumeration.hasMoreTypes()) + { + String internalType = internalTypeEnumeration.nextType(); + + size += internalTypeSize(internalType); + } + + return size; + } + + + /** + * Returns the size taken up on the stack by the given internal type. + * The size is 1, except for long and double types, for which it is 2, + * and for the void type, for which 0 is returned. + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return the size taken up on the stack, + * e.g. 1. + */ + public static int internalTypeSize(String internalType) + { + if (internalType.length() == 1) + { + char internalPrimitiveType = internalType.charAt(0); + if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG || + internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE) + { + return 2; + } + else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID) + { + return 0; + } + } + + return 1; + } + + + /** + * Converts an external type into an internal type. + * @param externalType the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + * @return the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + */ + public static String internalType(String externalType) + { + // Strip the array part, if any. + int dimensionCount = externalArrayTypeDimensionCount(externalType); + if (dimensionCount > 0) + { + externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length()); + } + + // Analyze the actual type part. + char internalTypeChar = + externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID ) ? + ClassConstants.INTERNAL_TYPE_VOID : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ? + ClassConstants.INTERNAL_TYPE_BOOLEAN : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE ) ? + ClassConstants.INTERNAL_TYPE_BYTE : + externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR ) ? + ClassConstants.INTERNAL_TYPE_CHAR : + externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT ) ? + ClassConstants.INTERNAL_TYPE_SHORT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_INT ) ? + ClassConstants.INTERNAL_TYPE_INT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT ) ? + ClassConstants.INTERNAL_TYPE_FLOAT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG ) ? + ClassConstants.INTERNAL_TYPE_LONG : + externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ? + ClassConstants.INTERNAL_TYPE_DOUBLE : + externalType.equals("%" ) ? + '%' : + (char)0; + + String internalType = + internalTypeChar != 0 ? String.valueOf(internalTypeChar) : + ClassConstants.INTERNAL_TYPE_CLASS_START + + internalClassName(externalType) + + ClassConstants.INTERNAL_TYPE_CLASS_END; + + // Prepend the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType; + } + + return internalType; + } + + + /** + * Returns the number of dimensions of the given external type. + * @param externalType the external type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int externalArrayTypeDimensionCount(String externalType) + { + int dimensions = 0; + int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length(); + int offset = externalType.length() - length; + while (externalType.regionMatches(offset, + ClassConstants.EXTERNAL_TYPE_ARRAY, + 0, + length)) + { + dimensions++; + offset -= length; + } + + return dimensions; + } + + + /** + * Converts an internal type into an external type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + */ + public static String externalType(String internalType) + { + // Strip the array part, if any. + int dimensionCount = internalArrayTypeDimensionCount(internalType); + if (dimensionCount > 0) + { + internalType = internalType.substring(dimensionCount); + } + + // Analyze the actual type part. + char internalTypeChar = internalType.charAt(0); + + String externalType = + internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID ? + ClassConstants.EXTERNAL_TYPE_VOID : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN ? + ClassConstants.EXTERNAL_TYPE_BOOLEAN : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE ? + ClassConstants.EXTERNAL_TYPE_BYTE : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR ? + ClassConstants.EXTERNAL_TYPE_CHAR : + internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT ? + ClassConstants.EXTERNAL_TYPE_SHORT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_INT ? + ClassConstants.EXTERNAL_TYPE_INT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT ? + ClassConstants.EXTERNAL_TYPE_FLOAT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG ? + ClassConstants.EXTERNAL_TYPE_LONG : + internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE ? + ClassConstants.EXTERNAL_TYPE_DOUBLE : + internalTypeChar == '%' ? + "%" : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ? + externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) : + null; + + if (externalType == null) + { + throw new IllegalArgumentException("Unknown type ["+internalType+"]"); + } + + // Append the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + externalType += ClassConstants.EXTERNAL_TYPE_ARRAY; + } + + return externalType; + } + + + /** + * Returns whether the given internal descriptor String represents a method + * descriptor. + * @param internalDescriptor the internal descriptor String, + * e.g. "<code>(II)Z</code>". + * @return <code>true</code> if the given String is a method descriptor, + * <code>false</code> otherwise. + */ + public static boolean isInternalMethodDescriptor(String internalDescriptor) + { + return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN; + } + + + /** + * Returns whether the given member String represents an external method + * name with arguments. + * @param externalMemberNameAndArguments the external member String, + * e.g. "<code>myField</code>" or + * e.g. "<code>myMethod(int,int)</code>". + * @return <code>true</code> if the given String refers to a method, + * <code>false</code> otherwise. + */ + public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) + { + return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0; + } + + + /** + * Returns the name part of the given external method name and arguments. + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the name part of the String, e.g. "<code>myMethod</code>". + */ + public static String externalMethodName(String externalMethodNameAndArguments) + { + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + return externalTypeEnumeration.methodName(); + } + + + /** + * Converts the given external method return type and name and arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + String externalMethodNameAndArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + while (externalTypeEnumeration.hasMoreTypes()) + { + internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts the given external method return type and List of arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalArguments the external method arguments, + * e.g. <code>{ "int", "int" }</code>. + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + List externalArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + for (int index = 0; index < externalArguments.size(); index++) + { + internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts an internal field description into an external full field description. + * @param accessFlags the access flags of the field. + * @param fieldName the field name, + * e.g. "<code>myField</code>". + * @param internalFieldDescriptor the internal field descriptor, + * e.g. "<code>Z</code>". + * @return the external full field description, + * e.g. "<code>public boolean myField</code>". + */ + public static String externalFullFieldDescription(int accessFlags, + String fieldName, + String internalFieldDescriptor) + { + return externalFieldAccessFlags(accessFlags) + + externalType(internalFieldDescriptor) + + ' ' + + fieldName; + } + + + /** + * Converts an internal method description into an external full method description. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param accessFlags the access flags of the method. + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external full method description, + * e.g. "<code>public boolean myMethod(int,int)</code>" or + * "<code>public MyClass(int,int)</code>". + */ + public static String externalFullMethodDescription(String internalClassName, + int accessFlags, + String internalMethodName, + String internalMethodDescriptor) + { + return externalMethodAccessFlags(accessFlags) + + externalMethodReturnTypeAndName(internalClassName, + internalMethodName, + internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + + externalMethodArguments(internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE; + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags) + { + return externalClassAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags) + { + return externalFieldAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @return the external method access description, + * e.g. "<code>public synchronized </code>". + */ + public static String externalMethodAccessFlags(int accessFlags) + { + return externalMethodAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external method access description, + * e.g. "public synchronized ". + */ + public static String externalMethodAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts an internal method descriptor into an external method return type. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type, + * e.g. "<code>boolean</code>". + */ + public static String externalMethodReturnType(String internalMethodDescriptor) + { + return externalType(internalMethodReturnType(internalMethodDescriptor)); + } + + + /** + * Converts an internal class name, method name, and method descriptor to + * an external method return type and name. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type and name, + * e.g. "<code>boolean myMethod</code>" or + * "<code>MyClass</code>". + */ + private static String externalMethodReturnTypeAndName(String internalClassName, + String internalMethodName, + String internalMethodDescriptor) + { + return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + externalShortClassName(externalClassName(internalClassName)) : + (externalMethodReturnType(internalMethodDescriptor) + + ' ' + + internalMethodName); + } + + + /** + * Converts an internal method descriptor into an external method argument + * description. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method argument description, + * e.g. "<code>int,int</code>". + */ + public static String externalMethodArguments(String internalMethodDescriptor) + { + StringBuffer externalMethodNameAndArguments = new StringBuffer(); + + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + while (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType())); + if (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR); + } + } + + return externalMethodNameAndArguments.toString(); + } + + + /** + * Returns the internal package name of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package name, + * e.g. "<code>java/lang</code>". + */ + public static String internalPackageName(String internalClassName) + { + String internalPackagePrefix = internalPackagePrefix(internalClassName); + int length = internalPackagePrefix.length(); + return length > 0 ? + internalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the internal package prefix of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package prefix, + * e.g. "<code>java/lang/</code>". + */ + public static String internalPackagePrefix(String internalClassName) + { + return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + internalClassName.length() - 2) + 1); + } + + + /** + * Returns the external package name of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package name, + * e.g. "<code>java.lang</code>". + */ + public static String externalPackageName(String externalClassName) + { + String externalPackagePrefix = externalPackagePrefix(externalClassName); + int length = externalPackagePrefix.length(); + return length > 0 ? + externalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the external package prefix of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package prefix, + * e.g. "<code>java.lang.</code>". + */ + public static String externalPackagePrefix(String externalClassName) + { + return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + externalClassName.length() - 2) + 1); + } +} diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java new file mode 100644 index 0000000..0bee2d5 --- /dev/null +++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java @@ -0,0 +1,236 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + +import java.util.Stack; + +/** + * A <code>DescriptorClassEnumeration</code> provides an enumeration of all + * classes mentioned in a given descriptor or signature. + * + * @author Eric Lafortune + */ +public class DescriptorClassEnumeration +{ + private String descriptor; + + private int index; + private int nestingLevel; + private boolean isInnerClassName; + private String accumulatedClassName; + private Stack accumulatedClassNames; + + + /** + * Creates a new DescriptorClassEnumeration for the given descriptor. + */ + public DescriptorClassEnumeration(String descriptor) + { + this.descriptor = descriptor; + } + + + /** + * Returns the number of classes contained in the descriptor. This + * is the number of class names that the enumeration will return. + */ + public int classCount() + { + int count = 0; + + nextFluff(); + while (hasMoreClassNames()) + { + count++; + + nextClassName(); + nextFluff(); + } + + index = 0; + + return count; + } + + + /** + * Returns whether the enumeration can provide more class names from the + * descriptor. + */ + public boolean hasMoreClassNames() + { + return index < descriptor.length(); + } + + + /** + * Returns the next fluff (surrounding class names) from the descriptor. + */ + public String nextFluff() + { + int fluffStartIndex = index; + + // Find the first token marking the start of a class name 'L' or '.'. + loop: while (index < descriptor.length()) + { + switch (descriptor.charAt(index++)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + nestingLevel++; + + // Make sure we have a stack. + if (accumulatedClassNames == null) + { + accumulatedClassNames = new Stack(); + } + + // Remember the accumulated class name. + accumulatedClassNames.push(accumulatedClassName); + + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + { + nestingLevel--; + + // Return to the accumulated class name outside the + // generic block. + accumulatedClassName = (String)accumulatedClassNames.pop(); + + continue loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND: + { + continue loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_START: + { + // We've found the start of an ordinary class name. + nestingLevel += 2; + isInnerClassName = false; + break loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_END: + { + nestingLevel -= 2; + break; + } + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + // We've found the start of an inner class name in a signature. + isInnerClassName = true; + break loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + // We've found the start of a type identifier. Skip to the end. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END); + break; + } + } + + if (nestingLevel == 1 && + descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END) + { + // We're at the start of a type parameter. Skip to the start + // of the bounds. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND); + } + } + + return descriptor.substring(fluffStartIndex, index); + } + + + /** + * Returns the next class name from the descriptor. + */ + public String nextClassName() + { + int classNameStartIndex = index; + + // Find the first token marking the end of a class name '<' or ';'. + loop: while (true) + { + switch (descriptor.charAt(index)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + case ClassConstants.INTERNAL_TYPE_CLASS_END: + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + break loop; + } + } + + index++; + } + + String className = descriptor.substring(classNameStartIndex, index); + + // Recompose the inner class name if necessary. + accumulatedClassName = isInnerClassName ? + accumulatedClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR + className : + className; + + return accumulatedClassName; + } + + + /** + * Returns whether the most recently returned class name was a recomposed + * inner class name from a signature. + */ + public boolean isInnerClassName() + { + return isInnerClassName; + } + + + /** + * A main method for testing the class name enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + while (enumeration.hasMoreClassNames()) + { + System.out.println(" Name: ["+enumeration.nextClassName()+"]"); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java new file mode 100644 index 0000000..09ffdd0 --- /dev/null +++ b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java @@ -0,0 +1,478 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.util.StringMatcher; + +/** + * This InstructionVisitor initializes any constant <code>Class.forName</code> or + * <code>.class</code> references of all classes it visits. More specifically, + * it fills out the references of string constant pool entries that refer to a + * class in the program class pool or in the library class pool. + * <p> + * It optionally prints notes if on usage of + * <code>(SomeClass)Class.forName(variable).newInstance()</code>. + * <p> + * The class hierarchy must be initialized before using this visitor. + * + * @see ClassSuperHierarchyInitializer + * + * @author Eric Lafortune + */ +public class DynamicClassReferenceInitializer +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + AttributeVisitor +{ + public static final int X = InstructionSequenceMatcher.X; + public static final int Y = InstructionSequenceMatcher.Y; + public static final int Z = InstructionSequenceMatcher.Z; + + public static final int A = InstructionSequenceMatcher.A; + public static final int B = InstructionSequenceMatcher.B; + public static final int C = InstructionSequenceMatcher.C; + public static final int D = InstructionSequenceMatcher.D; + + + private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[] + { + // 0 + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME), + + // 6 + new MethodrefConstant(1, 7, null, null), + new NameAndTypeConstant(8, 9), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE), + + // 10 + new MethodrefConstant(1, 11, null, null), + new NameAndTypeConstant(12, 13), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE), + }; + + // Class.forName("SomeClass"). + private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // (SomeClass)Class.forName(someName).newInstance(). + private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6), + new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), + }; + + +// private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] +// { +// new MethodrefConstant(A, 1, null, null), +// new NameAndTypeConstant(2, 3), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), +// }; + + private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] + { + new MethodrefConstant(A, 1, null, null), + new NameAndTypeConstant(B, 2), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), + }; + + // SomeClass.class = class$("SomeClass") (javac). + private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + +// private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] +// { +// new MethodrefConstant(A, 1, null, null), +// new NameAndTypeConstant(2, 3), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), +// }; + + private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] + { + new MethodrefConstant(A, 1, null, null), + new NameAndTypeConstant(B, 2), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), + }; + + // SomeClass.class = class("SomeClass", false) (jikes). + private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // return Class.forName(v0). + private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + // return Class.forName(v0), if (!v1) .getComponentType(). + private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new VariableInstruction(InstructionConstants.OP_ALOAD_1), + new BranchInstruction(InstructionConstants.OP_IFNE, +6), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + // return Class.forName(v0).getComponentType(). + private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingNotePrinter; + private final WarningPrinter dependencyWarningPrinter; + private final WarningPrinter notePrinter; + private final StringMatcher noteExceptionMatcher; + + + private final InstructionSequenceMatcher constantClassForNameMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS); + + private final InstructionSequenceMatcher classForNameCastMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + CLASS_FOR_NAME_CAST_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJavacMatcher = + new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS, + DOT_CLASS_JAVAC_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesMatcher = + new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS, + DOT_CLASS_JIKES_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJavacImplementationMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesImplementationMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2); + + + // A field acting as a return variable for the visitors. + private boolean isClassForNameInvocation; + + + /** + * Creates a new DynamicClassReferenceInitializer that optionally prints + * warnings and notes, with optional class specifications for which never + * to print notes. + */ + public DynamicClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingNotePrinter, + WarningPrinter dependencyWarningPrinter, + WarningPrinter notePrinter, + StringMatcher noteExceptionMatcher) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingNotePrinter = missingNotePrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + this.notePrinter = notePrinter; + this.noteExceptionMatcher = noteExceptionMatcher; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Try to match the Class.forName("SomeClass") construct. + instruction.accept(clazz, method, codeAttribute, offset, + constantClassForNameMatcher); + + // Did we find a match? + if (constantClassForNameMatcher.isMatching()) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this); + + // Don't look for the dynamic construct. + classForNameCastMatcher.reset(); + } + + // Try to match the (SomeClass)Class.forName(someName).newInstance() + // construct. + instruction.accept(clazz, method, codeAttribute, offset, + classForNameCastMatcher); + + // Did we find a match? + if (classForNameCastMatcher.isMatching()) + { + // Print out a note about the construct. + clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this); + } + + // Try to match the javac .class construct. + instruction.accept(clazz, method, codeAttribute, offset, + dotClassJavacMatcher); + + // Did we find a match? + if (dotClassJavacMatcher.isMatching() && + isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0))) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this); + } + + // Try to match the jikes .class construct. + instruction.accept(clazz, method, codeAttribute, offset, + dotClassJikesMatcher); + + // Did we find a match? + if (dotClassJikesMatcher.isMatching() && + isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0))) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this); + } + } + + + // Implementations for ConstantVisitor. + + /** + * Fills out the link to the referenced class. + */ + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Save a reference to the corresponding class. + String externalClassName = stringConstant.getString(clazz); + String internalClassName = ClassUtil.internalClassName(externalClassName); + + stringConstant.referencedClass = findClass(clazz.getName(), internalClassName); + } + + + /** + * Prints out a note about the class cast to this class, if applicable. + */ + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Print out a note about the class cast. + if (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(classConstant.getName(clazz))) + { + notePrinter.print(clazz.getName(), + classConstant.getName(clazz), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " calls '(" + + ClassUtil.externalClassName(classConstant.getName(clazz)) + + ")Class.forName(variable).newInstance()'"); + } + } + + + /** + * Checks whether the referenced method is a .class method. + */ + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + String methodType = methodrefConstant.getType(clazz); + + // Do the method's class and type match? + if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) || + methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES)) + { + String methodName = methodrefConstant.getName(clazz); + + // Does the method's name match one of the special names? + isClassForNameInvocation = + methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) || + methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES); + + if (isClassForNameInvocation) + { + return; + } + + String className = methodrefConstant.getClassName(clazz); + + // Note that we look for the class by name, since the referenced + // class has not been initialized yet. + Clazz referencedClass = programClassPool.getClass(className); + if (referencedClass != null) + { + // Check if the code of the referenced method is .class code. + // Note that we look for the method by name and type, since the + // referenced method has not been initialized yet. + referencedClass.methodAccept(methodName, + methodType, + new AllAttributeVisitor(this)); + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Check whether this is class$(String), as generated by javac, or + // class(String, boolean), as generated by jikes, or an optimized + // version. + isClassForNameInvocation = + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJavacImplementationMatcher, 5) || + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJikesImplementationMatcher, 12) || + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJikesImplementationMatcher2, 8); + } + + + // Small utility methods. + + /** + * Returns whether the given method reference corresponds to a .class + * method, as generated by javac or by jikes. + */ + private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex) + { + isClassForNameInvocation = false; + + // Check if the code of the referenced method is .class code. + clazz.constantPoolEntryAccept(methodrefConstantIndex, this); + + return isClassForNameInvocation; + } + + + /** + * Returns whether the first whether the first instructions of the + * given code attribute match with the given instruction matcher. + */ + private boolean isDotClassMethodCode(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + InstructionSequenceMatcher codeMatcher, + int codeLength) + { + // Check the minimum code length. + if (codeAttribute.u4codeLength < codeLength) + { + return false; + } + + // Check the actual instructions. + codeMatcher.reset(); + codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher); + return codeMatcher.isMatching(); + } + + + /** + * Returns the class with the given name, either for the program class pool + * or from the library class pool, or <code>null</code> if it can't be found. + */ + private Clazz findClass(String referencingClassName, String name) + { + // Ignore any primitive array types. + if (ClassUtil.isInternalArrayType(name) && + !ClassUtil.isInternalClassType(name)) + { + return null; + } + + // First look for the class in the program class pool. + Clazz clazz = programClassPool.getClass(name); + + // Otherwise look for the class in the library class pool. + if (clazz == null) + { + clazz = libraryClassPool.getClass(name); + + if (clazz == null && + missingNotePrinter != null) + { + // We didn't find the superclass or interface. Print a note. + missingNotePrinter.print(referencingClassName, + name, + "Note: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find dynamically referenced class " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " depends dynamically on program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java new file mode 100644 index 0000000..1f57708 --- /dev/null +++ b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java @@ -0,0 +1,604 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.visitor.*; +import proguard.util.StringMatcher; + +/** + * This InstructionVisitor initializes any constant + * <code>Class.get[Declared]{Field,Method}</code> references of all instructions + * it visits. More specifically, it fills out the references of string constant + * pool entries that refer to a class member in the program class pool or in the + * library class pool. + * <p> + * It optionally prints notes if on usage of + * <code>(SomeClass)Class.forName(variable).newInstance()</code>. + * <p> + * The class hierarchy and references must be initialized before using this + * visitor. + * + * @see ClassSuperHierarchyInitializer + * @see ClassReferenceInitializer + * + * @author Eric Lafortune + */ +public class DynamicMemberReferenceInitializer +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + AttributeVisitor, + MemberVisitor +{ + public static final int X = InstructionSequenceMatcher.X; + public static final int Y = InstructionSequenceMatcher.Y; + public static final int Z = InstructionSequenceMatcher.Z; + + public static final int A = InstructionSequenceMatcher.A; + public static final int B = InstructionSequenceMatcher.B; + public static final int C = InstructionSequenceMatcher.C; + public static final int D = InstructionSequenceMatcher.D; + + + private final Constant[] GET_FIELD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_FIELD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_FIELD), + }; + + private final Constant[] GET_DECLARED_FIELD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_FIELD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_FIELD), + }; + + private final Constant[] GET_METHOD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_METHOD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_METHOD), + }; + + private final Constant[] GET_DECLARED_METHOD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_METHOD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_METHOD), + }; + + // SomeClass.class.get[Declared]Field("someField"). + private final Instruction[] CONSTANT_GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] {}). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS0 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class }). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS1 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class, B.class }). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS2 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_2), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Field("someField"). + private final Instruction[] GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] {}). + private final Instruction[] GET_METHOD_INSTRUCTIONS0 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] { A.class }). + private final Instruction[] GET_METHOD_INSTRUCTIONS1 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] { A.class, B.class }). + private final Instruction[] GET_METHOD_INSTRUCTIONS2 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_2), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter notePrinter; + private final StringMatcher noteFieldExceptionMatcher; + private final StringMatcher noteMethodExceptionMatcher; + + + private final InstructionSequenceMatcher constantGetFieldMatcher = + new InstructionSequenceMatcher(GET_FIELD_CONSTANTS, + CONSTANT_GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetDeclaredFieldMatcher = + new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS, + CONSTANT_GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetMethodMatcher0 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher0 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher constantGetMethodMatcher1 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher1 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher constantGetMethodMatcher2 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher2 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher getFieldMatcher = + new InstructionSequenceMatcher(GET_FIELD_CONSTANTS, + GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher getDeclaredFieldMatcher = + new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS, + GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher getMethodMatcher0 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher0 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher getMethodMatcher1 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher1 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getMethodMatcher2 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher2 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS2); + + private final MemberFinder memberFinder = new MemberFinder(); + + + // Fields acting as parameters for the visitors. + private Clazz referencedClass; + private boolean isDeclared; + private boolean isField; + + + + /** + * Creates a new DynamicMemberReferenceInitializer. + */ + public DynamicMemberReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter notePrinter, + StringMatcher noteFieldExceptionMatcher, + StringMatcher noteMethodExceptionMatcher) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.notePrinter = notePrinter; + this.noteFieldExceptionMatcher = noteFieldExceptionMatcher; + this.noteMethodExceptionMatcher = noteMethodExceptionMatcher; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Try to match the SomeClass.class.getField("someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetFieldMatcher, + getFieldMatcher, true, false); + + // Try to match the SomeClass.class.getDeclaredField("someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredFieldMatcher, + getDeclaredFieldMatcher, true, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher0, + getMethodMatcher0, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher0, + getDeclaredMethodMatcher0, false, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher1, + getMethodMatcher1, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher1, + getDeclaredMethodMatcher1, false, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher2, + getMethodMatcher2, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher2, + getDeclaredMethodMatcher2, false, true); + } + + + /** + * Tries to match the next instruction and fills out the string constant + * or prints out a note accordingly. + */ + private void matchGetMember(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction, + InstructionSequenceMatcher constantSequenceMatcher, + InstructionSequenceMatcher variableSequenceMatcher, + boolean isField, + boolean isDeclared) + { + // Try to match the next instruction in the constant sequence. + instruction.accept(clazz, method, codeAttribute, offset, + constantSequenceMatcher); + + // Did we find a match to fill out the string constant? + if (constantSequenceMatcher.isMatching()) + { + this.isField = isField; + this.isDeclared = isDeclared; + + // Get the member's class. + clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(X), this); + + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(Y), this); + + // Don't look for the dynamic construct. + variableSequenceMatcher.reset(); + } + + // Try to match the next instruction in the variable sequence. + instruction.accept(clazz, method, codeAttribute, offset, + variableSequenceMatcher); + + // Did we find a match to print out a note? + if (variableSequenceMatcher.isMatching()) + { + // Print out a note about the dynamic invocation. + printDynamicInvocationNote(clazz, + variableSequenceMatcher, + isField, + isDeclared); + } + } + + + // Implementations for ConstantVisitor. + + /** + * Remembers the referenced class. + */ + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Remember the referenced class. + referencedClass = ClassUtil.isInternalArrayType(classConstant.getName(clazz)) ? + null : + classConstant.referencedClass; + } + + + /** + * Fills out the link to the referenced class member. + */ + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (referencedClass != null) + { + String name = stringConstant.getString(clazz); + + // See if we can find the referenced class member locally, or + // somewhere in the hierarchy. + Member referencedMember = isDeclared ? isField ? + (Member)referencedClass.findField(name, null) : + (Member)referencedClass.findMethod(name, null) : + (Member)memberFinder.findMember(clazz, + referencedClass, + name, + null, + isField); + if (referencedMember != null) + { + stringConstant.referencedMember = referencedMember; + stringConstant.referencedClass = isDeclared ? + referencedClass : + memberFinder.correspondingClass(); + } + } + } + + + // Small utility methods. + + /** + * Prints out a note on the matched dynamic invocation, if necessary. + */ + private void printDynamicInvocationNote(Clazz clazz, + InstructionSequenceMatcher noteSequenceMatcher, + boolean isField, + boolean isDeclared) + { + // Print out a note about the dynamic invocation. + if (notePrinter != null && + notePrinter.accepts(clazz.getName())) + { + // Is the class member name in the list of exceptions? + StringMatcher noteExceptionMatcher = isField ? + noteFieldExceptionMatcher : + noteMethodExceptionMatcher; + + int memberNameIndex = noteSequenceMatcher.matchedConstantIndex(Y); + String memberName = clazz.getStringString(memberNameIndex); + + if (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(memberName)) + { + // Compose the external member name and partial descriptor. + String externalMemberDescription = memberName; + + if (!isField) + { + externalMemberDescription += '('; + for (int count = 0; count < 2; count++) + { + int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count); + if (memberArgumentIndex > 0) + { + if (count > 0) + { + externalMemberDescription += ','; + } + String className = clazz.getClassName(memberArgumentIndex); + externalMemberDescription += ClassUtil.isInternalArrayType(className) ? + ClassUtil.externalType(className) : + ClassUtil.externalClassName(className); + } + } + externalMemberDescription += ')'; + } + + // Print out the actual note. + notePrinter.print(clazz.getName(), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " accesses a " + + (isDeclared ? "declared " : "") + + (isField ? "field" : "method") + + " '" + + externalMemberDescription + + "' dynamically"); + + // Print out notes about potential candidates. + ClassVisitor classVisitor; + + if (isField) + { + classVisitor = + new AllFieldVisitor( + new MemberNameFilter(memberName, this)); + } + else + { + // Compose the partial method descriptor. + String methodDescriptor = "("; + for (int count = 0; count < 2; count++) + { + int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count); + if (memberArgumentIndex > 0) + { + if (count > 0) + { + methodDescriptor += ','; + } + String className = clazz.getClassName(memberArgumentIndex); + methodDescriptor += ClassUtil.isInternalArrayType(className) ? + className : + ClassUtil.internalTypeFromClassName(className); + } + } + methodDescriptor += ")L///;"; + + classVisitor = + new AllMethodVisitor( + new MemberNameFilter(memberName, + new MemberDescriptorFilter(methodDescriptor, this))); + } + + programClassPool.classesAcceptAlphabetically(classVisitor); + libraryClassPool.classesAcceptAlphabetically(classVisitor); + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program field '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program method '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(null, 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library field '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) + + "; }'"); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library method '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(null, 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) + + "; }'"); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java new file mode 100644 index 0000000..6371888 --- /dev/null +++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java @@ -0,0 +1,106 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * An <code>ExternalTypeEnumeration</code> provides an enumeration of all + * types listed in a given external descriptor string. The method name can + * be retrieved separately. + * <p> + * A <code>ExternalTypeEnumeration</code> object can be reused for processing + * different subsequent descriptors, by means of the <code>setDescriptor</code> + * method. + * + * @author Eric Lafortune + */ +public class ExternalTypeEnumeration +{ + private String descriptor; + private int index; + + + public ExternalTypeEnumeration(String descriptor) + { + setDescriptor(descriptor); + } + + + ExternalTypeEnumeration() + { + } + + + void setDescriptor(String descriptor) + { + this.descriptor = descriptor; + + reset(); + } + + + public void reset() + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) + 1; + + if (index < 1) + { + throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]"); + } + } + + + public boolean hasMoreTypes() + { + return index < descriptor.length() - 1; + } + + + public String nextType() + { + int startIndex = index; + + // Find the next separating comma. + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR, + startIndex); + + // Otherwise find the closing parenthesis. + if (index < 0) + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE, + startIndex); + if (index < 0) + { + throw new IllegalArgumentException("Missing closing parenthesis in descriptor ["+descriptor+"]"); + } + } + + return descriptor.substring(startIndex, index++).trim(); + } + + + public String methodName() + { + return descriptor.substring(0, descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN)).trim(); + } +} diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/src/proguard/classfile/util/InstructionSequenceMatcher.java new file mode 100644 index 0000000..8a689d5 --- /dev/null +++ b/src/proguard/classfile/util/InstructionSequenceMatcher.java @@ -0,0 +1,634 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This InstructionVisitor checks whether a given pattern instruction sequence + * occurs in the instructions that are visited. The arguments of the + * instruction sequence can be wildcards that are matched. + * + * @author Eric Lafortune + */ +public class InstructionSequenceMatcher +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor +{ + /* + private static boolean DEBUG = false; + public static boolean DEBUG_MORE = false; + /*/ + private static final boolean DEBUG = false; + private static final boolean DEBUG_MORE = false; + //*/ + + public static final int X = 0x40000000; + public static final int Y = 0x40000001; + public static final int Z = 0x40000002; + + public static final int A = 0x40000003; + public static final int B = 0x40000004; + public static final int C = 0x40000005; + public static final int D = 0x40000006; + + + private final Constant[] patternConstants; + private final Instruction[] patternInstructions; + + private boolean matching; + private boolean matchingAnyWildCards; + private int patternInstructionIndex; + private final int[] matchedInstructionOffsets; + private int matchedArgumentFlags; + private final int[] matchedArguments = new int[7]; + private long matchedConstantFlags; + private final int[] matchedConstantIndices; + + // Fields acting as a parameter and a return value for visitor methods. + private Constant patternConstant; + private boolean matchingConstant; + + + /** + * Creates a new InstructionSequenceMatcher. + * @param patternConstants any constants referenced by the pattern + * instruction. + * @param patternInstructions the pattern instruction sequence. + */ + public InstructionSequenceMatcher(Constant[] patternConstants, + Instruction[] patternInstructions) + { + this.patternConstants = patternConstants; + this.patternInstructions = patternInstructions; + + matchedInstructionOffsets = new int[patternInstructions.length]; + matchedConstantIndices = new int[patternConstants.length]; + } + + + /** + * Starts matching from the first instruction again next time. + */ + public void reset() + { + patternInstructionIndex = 0; + matchedArgumentFlags = 0; + matchedConstantFlags = 0L; + } + + + public boolean isMatching() + { + return matching; + } + + + public boolean isMatchingAnyWildcards() + { + return matchingAnyWildCards; + } + + + public int instructionCount() + { + return patternInstructions.length; + } + + + public int matchedInstructionOffset(int index) + { + return matchedInstructionOffsets[index]; + } + + + public int matchedArgument(int argument) + { + int argumentIndex = argument - X; + return argumentIndex < 0 ? + argument : + matchedArguments[argumentIndex]; + } + + + public int[] matchedArguments(int[] arguments) + { + int[] matchedArguments = new int[arguments.length]; + + for (int index = 0; index < arguments.length; index++) + { + matchedArguments[index] = matchedArgument(arguments[index]); + } + + return matchedArguments; + } + + + public int matchedConstantIndex(int constantIndex) + { + int argumentIndex = constantIndex - X; + return argumentIndex < 0 ? + matchedConstantIndices[constantIndex] : + matchedArguments[argumentIndex]; + } + + + public int matchedBranchOffset(int offset, int branchOffset) + { + int argumentIndex = branchOffset - X; + return argumentIndex < 0 ? + branchOffset : + matchedArguments[argumentIndex] - offset; + } + + + public int[] matchedJumpOffsets(int offset, int[] jumpOffsets) + { + int[] matchedJumpOffsets = new int[jumpOffsets.length]; + + for (int index = 0; index < jumpOffsets.length; index++) + { + matchedJumpOffsets[index] = matchedBranchOffset(offset, + jumpOffsets[index]); + } + + return matchedJumpOffsets; + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(simpleInstruction, patternInstruction) && + matchingArguments(simpleInstruction.constant, + ((SimpleInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + simpleInstruction); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(variableInstruction, patternInstruction) && + matchingArguments(variableInstruction.variableIndex, + ((VariableInstruction)patternInstruction).variableIndex) && + matchingArguments(variableInstruction.constant, + ((VariableInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + variableInstruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(constantInstruction, patternInstruction) && + matchingConstantIndices(clazz, + constantInstruction.constantIndex, + ((ConstantInstruction)patternInstruction).constantIndex) && + matchingArguments(constantInstruction.constant, + ((ConstantInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + constantInstruction); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the from + // sequence. + boolean condition = + matchingOpcodes(branchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + branchInstruction.branchOffset, + ((BranchInstruction)patternInstruction).branchOffset); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + branchInstruction); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(tableSwitchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + tableSwitchInstruction.defaultOffset, + ((TableSwitchInstruction)patternInstruction).defaultOffset) && + matchingArguments(tableSwitchInstruction.lowCase, + ((TableSwitchInstruction)patternInstruction).lowCase) && + matchingArguments(tableSwitchInstruction.highCase, + ((TableSwitchInstruction)patternInstruction).highCase) && + matchingJumpOffsets(offset, + tableSwitchInstruction.jumpOffsets, + ((TableSwitchInstruction)patternInstruction).jumpOffsets); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + tableSwitchInstruction); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(lookUpSwitchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + lookUpSwitchInstruction.defaultOffset, + ((LookUpSwitchInstruction)patternInstruction).defaultOffset) && + matchingArguments(lookUpSwitchInstruction.cases, + ((LookUpSwitchInstruction)patternInstruction).cases) && + matchingJumpOffsets(offset, + lookUpSwitchInstruction.jumpOffsets, + ((LookUpSwitchInstruction)patternInstruction).jumpOffsets); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + lookUpSwitchInstruction); + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant; + + // Compare the integer values. + matchingConstant = integerConstant.getValue() == + integerPatternConstant.getValue(); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + LongConstant longPatternConstant = (LongConstant)patternConstant; + + // Compare the long values. + matchingConstant = longConstant.getValue() == + longPatternConstant.getValue(); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + FloatConstant floatPatternConstant = (FloatConstant)patternConstant; + + // Compare the float values. + matchingConstant = floatConstant.getValue() == + floatPatternConstant.getValue(); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant; + + // Compare the double values. + matchingConstant = doubleConstant.getValue() == + doublePatternConstant.getValue(); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + StringConstant stringPatternConstant = (StringConstant)patternConstant; + + // Check the UTF-8 constant. + matchingConstant = + matchingConstantIndices(clazz, + stringConstant.u2stringIndex, + stringPatternConstant.u2stringIndex); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant; + + // Compare the actual strings. + matchingConstant = utf8Constant.getString().equals( + utf8PatternConstant.getString()); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + RefConstant refPatternConstant = (RefConstant)patternConstant; + + // Check the class and the name and type. + matchingConstant = + matchingConstantIndices(clazz, + refConstant.getClassIndex(), + refPatternConstant.getClassIndex()) && + matchingConstantIndices(clazz, + refConstant.getNameAndTypeIndex(), + refPatternConstant.getNameAndTypeIndex()); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + ClassConstant classPatternConstant = (ClassConstant)patternConstant; + + // Check the class name. + matchingConstant = + matchingConstantIndices(clazz, + classConstant.u2nameIndex, + classPatternConstant.u2nameIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant; + + // Check the name and the descriptor. + matchingConstant = + matchingConstantIndices(clazz, + nameAndTypeConstant.u2nameIndex, + typePatternConstant.u2nameIndex) && + matchingConstantIndices(clazz, + nameAndTypeConstant.u2descriptorIndex, + typePatternConstant.u2descriptorIndex); + } + + + // Small utility methods. + + private boolean matchingOpcodes(Instruction instruction1, + Instruction instruction2) + { + // Check the opcode. + return instruction1.opcode == instruction2.opcode || + instruction1.canonicalOpcode() == instruction2.opcode; + } + + + private boolean matchingArguments(int argument1, + int argument2) + { + int argumentIndex = argument2 - X; + if (argumentIndex < 0) + { + // Check the literal argument. + return argument1 == argument2; + } + else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) + { + // Store a wildcard argument. + matchedArguments[argumentIndex] = argument1; + matchedArgumentFlags |= 1 << argumentIndex; + + return true; + } + else + { + // Check the previously stored wildcard argument. + return matchedArguments[argumentIndex] == argument1; + } + } + + + private boolean matchingArguments(int[] arguments1, + int[] arguments2) + { + if (arguments1.length != arguments2.length) + { + return false; + } + + for (int index = 0; index < arguments1.length; index++) + { + if (!matchingArguments(arguments1[index], arguments2[index])) + { + return false; + } + } + + return true; + } + + + private boolean matchingConstantIndices(Clazz clazz, + int constantIndex1, + int constantIndex2) + { + if (constantIndex2 >= X) + { + // Check the constant index. + return matchingArguments(constantIndex1, constantIndex2); + } + else if ((matchedConstantFlags & (1L << constantIndex2)) == 0) + { + // Check the actual constant. + matchingConstant = false; + patternConstant = patternConstants[constantIndex2]; + + if (clazz.getTag(constantIndex1) == patternConstant.getTag()) + { + clazz.constantPoolEntryAccept(constantIndex1, this); + + if (matchingConstant) + { + // Store the constant index. + matchedConstantIndices[constantIndex2] = constantIndex1; + matchedConstantFlags |= 1L << constantIndex2; + } + } + + return matchingConstant; + } + else + { + // Check a previously stored constant index. + return matchedConstantIndices[constantIndex2] == constantIndex1; + } + } + + + private boolean matchingBranchOffsets(int offset, + int branchOffset1, + int branchOffset2) + { + int argumentIndex = branchOffset2 - X; + if (argumentIndex < 0) + { + // Check the literal argument. + return branchOffset1 == branchOffset2; + } + else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) + { + // Store a wildcard argument. + matchedArguments[argumentIndex] = offset + branchOffset1; + matchedArgumentFlags |= 1 << argumentIndex; + + return true; + } + else + { + // Check the previously stored wildcard argument. + return matchedArguments[argumentIndex] == offset + branchOffset1; + } + } + + + private boolean matchingJumpOffsets(int offset, + int[] jumpOffsets1, + int[] jumpOffsets2) + { + if (jumpOffsets1.length != jumpOffsets2.length) + { + return false; + } + + for (int index = 0; index < jumpOffsets1.length; index++) + { + if (!matchingBranchOffsets(offset, + jumpOffsets1[index], + jumpOffsets2[index])) + { + return false; + } + } + + return true; + } + + + private void checkMatch(boolean condition, + Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) + { + if (DEBUG_MORE) + { + System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t ")+instruction.toString(offset)); + } + + // Did the instruction match? + if (condition) + { + // Remember the offset of the matching instruction. + matchedInstructionOffsets[patternInstructionIndex] = offset; + + // Try to match the next instruction next time. + patternInstructionIndex++; + + // Did we match all instructions in the sequence? + matching = patternInstructionIndex == patternInstructions.length; + + // Did we match any wildcards along the way? + matchingAnyWildCards = matchedArgumentFlags != 0; + + if (matching) + { + if (DEBUG) + { + System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + for (int index = 0; index < patternInstructionIndex; index++) + { + System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index])); + } + } + + // Start matching from the first instruction again next time. + reset(); + } + } + else + { + // The instruction didn't match. + matching = false; + + // Is this a failed second instruction? + boolean retry = patternInstructionIndex == 1; + + // Start matching from the first instruction next time. + reset(); + + // Retry a failed second instruction as a first instruction. + if (retry) + { + instruction.accept(clazz, method, codeAttribute, offset, this); + } + } + } +} diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java new file mode 100644 index 0000000..76f7e84 --- /dev/null +++ b/src/proguard/classfile/util/InternalTypeEnumeration.java @@ -0,0 +1,204 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * An <code>InternalTypeEnumeration</code> provides an enumeration of all + * parameter types listed in a given internal method descriptor or signature. + * The signature can also be a class signature. The return type of a method + * descriptor can retrieved separately. + * + * @author Eric Lafortune + */ +public class InternalTypeEnumeration +{ + private String descriptor; + private int firstIndex; + private int lastIndex; + private int index; + + + /** + * Creates a new InternalTypeEnumeration for the given method descriptor. + */ + public InternalTypeEnumeration(String descriptor) + { + this.descriptor = descriptor; + this.firstIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + this.lastIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + this.index = firstIndex + 1; + + if (lastIndex < 0) + { + lastIndex = descriptor.length(); + } + } + + + /** + * Returns the formal type parameters from the descriptor, assuming it's a + * method descriptor. + */ + public String formalTypeParameters() + { + return descriptor.substring(0, firstIndex); + } + + + /** + * Returns whether the enumeration can provide more types from the method + * descriptor. + */ + public boolean hasMoreTypes() + { + return index < lastIndex; + } + + + /** + * Returns the next type from the method descriptor. + */ + public String nextType() + { + int startIndex = index; + + skipArray(); + + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_CLASS_START: + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + skipClass(); + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + skipGeneric(); + break; + } + } + + return descriptor.substring(startIndex, index); + } + + + /** + * Returns the return type from the descriptor, assuming it's a method + * descriptor. + */ + public String returnType() + { + return descriptor.substring(lastIndex + 1); + } + + + // Small utility methods. + + private void skipArray() + { + while (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + index++; + } + } + + + private void skipClass() + { + while (true) + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + skipGeneric(); + break; + + case ClassConstants.INTERNAL_TYPE_CLASS_END: + return; + } + } + } + + + private void skipGeneric() + { + int nestingLevel = 1; + + do + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + nestingLevel++; + break; + + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + nestingLevel--; + break; + } + } + while (nestingLevel > 0); + } + + + /** + * A main method for testing the type enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + InternalTypeEnumeration enumeration = new InternalTypeEnumeration(descriptor); + + if (enumeration.firstIndex >= 0) + { + System.out.println(" Formal type parameters ["+enumeration.formalTypeParameters()+"]"); + } + + while (enumeration.hasMoreTypes()) + { + System.out.println(" Type ["+enumeration.nextType()+"]"); + } + + if (enumeration.lastIndex < descriptor.length()) + { + System.out.println(" Return type ["+enumeration.returnType()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java new file mode 100644 index 0000000..0fdeec0 --- /dev/null +++ b/src/proguard/classfile/util/MemberFinder.java @@ -0,0 +1,197 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +/** + * This class provides methods to find class members in a given class or in its + * hierarchy. + * + * @author Eric Lafortune + */ +public class MemberFinder +extends SimplifiedVisitor +implements MemberVisitor +{ + private static class MemberFoundException extends RuntimeException {} + private static final MemberFoundException MEMBER_FOUND = new MemberFoundException(); + + private Clazz clazz; + private Member member; + + + /** + * Finds the field with the given name and descriptor in the given + * class or its hierarchy. + */ + public Field findField(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Field)findMember(referencingClass, clazz, name, descriptor, true); + } + + + /** + * Finds the method with the given name and descriptor in the given + * class or its hierarchy. + */ + public Method findMethod(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Method)findMember(referencingClass, clazz, name, descriptor, false); + } + + + /** + * Finds the class member with the given name and descriptor in the given + * class or its hierarchy. + */ + public Member findMember(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor, + boolean isField) + { + // Organize a search in the hierarchy of superclasses and interfaces. + // The class member may be in a different class, if the code was + // compiled with "-target 1.2" or higher (the default in JDK 1.4). + try + { + this.clazz = null; + this.member = null; + clazz.hierarchyAccept(true, true, true, false, isField ? + (ClassVisitor)new NamedFieldVisitor(name, descriptor, + new MemberClassAccessFilter(referencingClass, this)) : + (ClassVisitor)new NamedMethodVisitor(name, descriptor, + new MemberClassAccessFilter(referencingClass, this))); + } + catch (MemberFoundException ex) + { + // We've found the member we were looking for. + } + + return member; + } + + + /** + * Returns the corresponding class of the most recently found class + * member. + */ + public Clazz correspondingClass() + { + return clazz; + } + + + /** + * Returns whether the given method is overridden anywhere down the class + * hierarchy. + */ + public boolean isOverriden(Clazz clazz, + Method method) + { + String name = method.getName(clazz); + String descriptor = method.getDescriptor(clazz); + + // Go looking for the method down the class hierarchy. + try + { + this.clazz = null; + this.member = null; + + clazz.hierarchyAccept(false, false, false, true, + new NamedMethodVisitor(name, descriptor, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this))); + } + catch (MemberFoundException ex) + { + // We've found an overriding method. + return true; + } + + return false; + } + + + /** + * Returns whether the given field is shadowed anywhere down the class + * hierarchy. + */ + public boolean isShadowed(Clazz clazz, + Field field) + { + String name = field.getName(clazz); + String descriptor = field.getDescriptor(clazz); + + // Go looking for the field down the class hierarchy. + try + { + this.clazz = null; + this.member = null; + clazz.hierarchyAccept(false, false, false, true, + new NamedFieldVisitor(name, descriptor, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this))); + } + catch (MemberFoundException ex) + { + // We've found a shadowing field. + return true; + } + + return false; + } + + +// // Implementations for ClassVisitor. +// +// private void visitAnyClass(Clazz clazz) +// { +// if (member == null) +// { +// member = isField ? +// (Member)clazz.findField(name, descriptor) : +// (Member)clazz.findMethod(name, descriptor); +// +// if (member != null) +// { +// this.clazz = clazz; +// } +// } +// } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + this.clazz = clazz; + this.member = member; + + throw MEMBER_FOUND; + } +} diff --git a/src/proguard/classfile/util/MethodLinker.java b/src/proguard/classfile/util/MethodLinker.java new file mode 100644 index 0000000..5f2ea6f --- /dev/null +++ b/src/proguard/classfile/util/MethodLinker.java @@ -0,0 +1,165 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * This ClassVisitor links all corresponding non-private methods in the class + * hierarchies of all visited classes. Visited classes are typically all class + * files that are not being subclassed. Chains of links that have been created + * in previous invocations are merged with new chains of links, in order to + * create a consistent set of chains. + * <p> + * As a MemberVisitor, it links all corresponding class members that it visits, + * including fields and private class members. + * <p> + * Class initialization methods and constructors are always ignored. + * + * @author Eric Lafortune + */ +public class MethodLinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + // An object that is reset and reused every time. + // The map: [class member name+' '+descriptor - class member info] + private final Map memberMap = new HashMap(); + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Collect all non-private members in this class hierarchy. + clazz.hierarchyAccept(true, true, true, false, + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + this))); + + // Clean up for the next class hierarchy. + memberMap.clear(); + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Get the class member's name and descriptor. + String name = member.getName(clazz); + String descriptor = member.getDescriptor(clazz); + + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // See if we've already come across a method with the same name and + // descriptor. + String key = name + ' ' + descriptor; + Member otherMember = (Member)memberMap.get(key); + + if (otherMember == null) + { + // Get the last method in the chain. + Member thisLastMember = lastMember(member); + + // Store the new class method in the map. + memberMap.put(key, thisLastMember); + } + else + { + // Link both members. + link(member, otherMember); + } + } + + + // Small utility methods. + + /** + * Links the two given class members. + */ + private static void link(Member member1, Member member2) + { + // Get the last methods in the both chains. + Member lastMember1 = lastMember(member1); + Member lastMember2 = lastMember(member2); + + // Check if both link chains aren't already ending in the same element. + if (!lastMember1.equals(lastMember2)) + { + // Merge the two chains, with the library members last. + if (lastMember2 instanceof LibraryMember) + { + lastMember1.setVisitorInfo(lastMember2); + } + else + { + lastMember2.setVisitorInfo(lastMember1); + } + } + } + + + /** + * Finds the last class member in the linked list of related class members. + * @param member the given class member. + * @return the last class member in the linked list. + */ + public static Member lastMember(Member member) + { + Member lastMember = member; + while (lastMember.getVisitorInfo() != null && + lastMember.getVisitorInfo() instanceof Member) + { + lastMember = (Member)lastMember.getVisitorInfo(); + } + + return lastMember; + } + + + /** + * Finds the last visitor accepter in the linked list of visitors. + * @param visitorAccepter the given method. + * @return the last method in the linked list. + */ + public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter) + { + VisitorAccepter lastVisitorAccepter = visitorAccepter; + while (lastVisitorAccepter.getVisitorInfo() != null && + lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter) + { + lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo(); + } + + return lastVisitorAccepter; + } +} diff --git a/src/proguard/classfile/util/SimplifiedVisitor.java b/src/proguard/classfile/util/SimplifiedVisitor.java new file mode 100644 index 0000000..87b7fe4 --- /dev/null +++ b/src/proguard/classfile/util/SimplifiedVisitor.java @@ -0,0 +1,810 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.constant.*; +import proguard.classfile.instruction.*; + +/** + * This abstract utility class allows to implement various visitor interfaces + * with simplified methods. The provided methods delegate to other versions + * with fewer arguments or more general arguments. + * + * @author Eric Lafortune + * @noinspection AbstractClassWithoutAbstractMethods + */ +public abstract class SimplifiedVisitor +{ + // Simplifications for ClassVisitor. + + /** + * Visits any type of class member of the given class. + */ + public void visitAnyClass(Clazz Clazz) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitProgramClass(ProgramClass programClass) + { + visitAnyClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + visitAnyClass(libraryClass); + } + + + // Simplifications for MemberVisitor. + + /** + * Visits any type of class member of the given class. + */ + public void visitAnyMember(Clazz clazz, Member member) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + /** + * Visits any type of class member of the given program class. + */ + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + visitAnyMember(programClass, programMember); + } + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + visitProgramMember(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + visitProgramMember(programClass, programMethod); + } + + + /** + * Visits any type of class member of the given library class. + */ + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + visitAnyMember(libraryClass, libraryMember); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + visitLibraryMember(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + visitLibraryMember(libraryClass, libraryMethod); + } + + + // Simplifications for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + visitAnyConstant(clazz, integerConstant); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + visitAnyConstant(clazz, longConstant); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + visitAnyConstant(clazz, floatConstant); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + visitAnyConstant(clazz, doubleConstant); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + visitAnyConstant(clazz, stringConstant); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + visitAnyConstant(clazz, utf8Constant); + } + + + /** + * Visits any type of RefConstant of the given class. + */ + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + visitAnyConstant(clazz, refConstant); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + visitAnyRefConstant(clazz, fieldrefConstant); + } + + + /** + * Visits any type of method RefConstant of the given class. + */ + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + visitAnyRefConstant(clazz, refConstant); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + visitAnyMethodrefConstant(clazz, interfaceMethodrefConstant); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + visitAnyMethodrefConstant(clazz, methodrefConstant); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + visitAnyConstant(clazz, classConstant); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + visitAnyConstant(clazz, nameAndTypeConstant); + } + + + // Simplifications for AttributeVisitor. + + /** + * Visit any type of attribute. + */ + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + visitAnyAttribute(clazz, unknownAttribute); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + visitAnyAttribute(clazz, sourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + visitAnyAttribute(clazz, sourceDirAttribute); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + visitAnyAttribute(clazz, innerClassesAttribute); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + visitAnyAttribute(clazz, enclosingMethodAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + visitAnyAttribute(clazz, deprecatedAttribute); + } + + + /** + * Visits the given DeprecatedAttribute of any type of class member. + */ + public void visitDeprecatedAttribute(Clazz clazz, Member member, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, deprecatedAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, (Member)field, deprecatedAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, (Member)method, deprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + visitAnyAttribute(clazz, syntheticAttribute); + } + + + /** + * Visits the given SyntheticAttribute of any type of class member. + */ + public void visitSyntheticAttribute(Clazz clazz, Member member, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, syntheticAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, (Member)field, syntheticAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, (Member)method, syntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + visitAnyAttribute(clazz, signatureAttribute); + } + + + /** + * Visits the given SignatureAttribute of any type of class member. + */ + public void visitSignatureAttribute(Clazz clazz, Member member, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, signatureAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, (Member)field, signatureAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, (Member)method, signatureAttribute); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + visitAnyAttribute(clazz, constantValueAttribute); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + visitAnyAttribute(clazz, exceptionsAttribute); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + visitAnyAttribute(clazz, codeAttribute); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + visitAnyAttribute(clazz, stackMapAttribute); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + visitAnyAttribute(clazz, stackMapTableAttribute); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + visitAnyAttribute(clazz, lineNumberTableAttribute); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + visitAnyAttribute(clazz, localVariableTableAttribute); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + visitAnyAttribute(clazz, localVariableTypeTableAttribute); + } + + + /** + * Visits any type of AnnotationsAttribute of a class. + */ + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + visitAnyAttribute(clazz, annotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitAnyAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + + + /** + * Visits the given RuntimeVisibleAnnotationsAttribute of any type of class member. + */ + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)field, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)method, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitAnyAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + + + /** + * Visits the given RuntimeInvisibleAnnotationsAttribute of any type of class member. + */ + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)field, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)method, runtimeInvisibleAnnotationsAttribute); + } + + + /** + * Visits any type of ParameterAnnotationsAttribute. + */ + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + visitAnyAttribute(clazz, parameterAnnotationsAttribute); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + visitAnyParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + visitAnyParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + visitAnyAttribute(clazz, annotationDefaultAttribute); + } + + + // Simplifications for InstructionVisitor. + + /** + * Visits any type of Instruction. + */ + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + + + /** + * Visits either type of SwitchInstruction. + */ + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, switchInstruction); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + visitAnySwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + visitAnySwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction); + } + + + // Simplifications for StackMapFrameVisitor. + + /** + * Visits any type of VerificationType. + */ + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameZeroFrame); + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, lessZeroFrame); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); + } + + + // Simplifications for VerificationTypeVisitor. + + /** + * Visits any type of VerificationType. + */ + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + public void visitStackIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType) + { + visitIntegerType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitStackFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType) + { + visitFloatType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitStackLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType) + { + visitLongType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitStackDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType) + { + visitDoubleType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitStackTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType) + { + visitTopType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitStackObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType) + { + visitObjectType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitStackNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType) + { + visitNullType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitStackUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType) + { + visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitStackUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType) + { + visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + + public void visitVariablesIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType) + { + visitIntegerType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitVariablesFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType) + { + visitFloatType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitVariablesLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType) + { + visitLongType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitVariablesDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType) + { + visitDoubleType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitVariablesTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType) + { + visitTopType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitVariablesObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType) + { + visitObjectType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitVariablesNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType) + { + visitNullType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitVariablesUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType) + { + visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitVariablesUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType) + { + visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + // Simplifications for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + /** + * Visits the given Annotation of any type of class member. + */ + public void visitAnnotation(Clazz clazz, Member member, Annotation annotation) + { + visitAnnotation(clazz, annotation); + } + + + public void visitAnnotation(Clazz clazz, Field field, Annotation annotation) + { + visitAnnotation(clazz, (Member)field, annotation); + } + + + public void visitAnnotation(Clazz clazz, Method method, Annotation annotation) + { + visitAnnotation(clazz, (Member)method, annotation); + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + visitAnnotation(clazz, method, annotation); + } + + + // Simplifications for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + visitAnyElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + visitAnyElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + visitAnyElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + visitAnyElementValue(clazz, annotation, annotationElementValue); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + visitAnyElementValue(clazz, annotation, arrayElementValue); + } +} diff --git a/src/proguard/classfile/util/StringReferenceInitializer.java b/src/proguard/classfile/util/StringReferenceInitializer.java new file mode 100644 index 0000000..3884a04 --- /dev/null +++ b/src/proguard/classfile/util/StringReferenceInitializer.java @@ -0,0 +1,89 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This ConstantVisitor initializes any class references of all string constants + * it visits. More specifically, it fills out the references of string constant + * pool entries that happen to refer to a class in the program class pool or in + * the library class pool. + * + * @author Eric Lafortune + */ +public class StringReferenceInitializer +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + + + /** + * Creates a new StringReferenceInitializer. + */ + public StringReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (stringConstant.referencedClass == null) + { + // See if we can find the referenced class. + stringConstant.referencedClass = + findClass(ClassUtil.internalClassName(stringConstant.getString(clazz))); + } + } + + + // Small utility methods. + + /** + * Returns the class with the given name, either for the program class pool + * or from the library class pool, or <code>null</code> if it can't be found. + */ + private Clazz findClass(String name) + { + // First look for the class in the program class pool. + Clazz clazz = programClassPool.getClass(name); + + // Otherwise look for the class in the library class pool. + if (clazz == null) + { + clazz = libraryClassPool.getClass(name); + } + + return clazz; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/util/StringSharer.java b/src/proguard/classfile/util/StringSharer.java new file mode 100644 index 0000000..56de7c5 --- /dev/null +++ b/src/proguard/classfile/util/StringSharer.java @@ -0,0 +1,155 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor shares strings in the class files that it visits. + * + * @author Eric Lafortune + */ +public class StringSharer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + AttributeVisitor +{ + // A fields acting as an argument for the visitor methods. + private String name; + private String type; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Replace name strings in the constant pool by shared strings. + programClass.constantPoolEntriesAccept(this); + + // Replace attribute name strings in the constant pool by internalized + // strings. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Replace the super class name string by the shared name string. + Clazz superClass = libraryClass.superClass; + if (superClass != null) + { + libraryClass.superClassName = superClass.getName(); + } + } + + + // Implementations for ConstantVisitor. + + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyStringConstant(Clazz clazz, StringConstant stringConstant) + { + Member referencedMember = stringConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = stringConstant.referencedClass; + + // Put the actual class member's name in the class pool. + name = referencedMember.getName(referencedClass); + clazz.constantPoolEntryAccept(stringConstant.u2stringIndex, this); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = refConstant.referencedClass; + + // Put the actual class member's name and type strings in the class + // pool. + name = referencedMember.getName(referencedClass); + type = referencedMember.getDescriptor(referencedClass); + clazz.constantPoolEntryAccept(refConstant.u2nameAndTypeIndex, this); + } + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + if (name != null) + { + // Put the actual class member's name and type strings in the class + // pool. + clazz.constantPoolEntryAccept(nameAndTypeConstant.u2nameIndex, this); + name = type; + clazz.constantPoolEntryAccept(nameAndTypeConstant.u2descriptorIndex, this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + // Put the actual class's name string in the class pool. + name = referencedClass.getName(); + clazz.constantPoolEntryAccept(classConstant.u2nameIndex, this); + } + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + // Do we have a new string to put into this constant? + if (name != null) + { + // Replace the string, if it's actually the same. + if (name.equals(utf8Constant.getString())) + { + utf8Constant.setString(name); + } + + name = null; + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + // Put the internalized attribute's name string in the class pool. + name = attribute.getAttributeName(clazz).intern(); + clazz.constantPoolEntryAccept(attribute.u2attributeNameIndex, this); + } +} diff --git a/src/proguard/classfile/util/WarningPrinter.java b/src/proguard/classfile/util/WarningPrinter.java new file mode 100644 index 0000000..87d8978 --- /dev/null +++ b/src/proguard/classfile/util/WarningPrinter.java @@ -0,0 +1,136 @@ +/* + * 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.classfile.util; + +import proguard.util.*; + +import java.io.PrintStream; +import java.util.List; + +/** + * This class prints out and counts warnings. + * + * @author Eric Lafortune + */ +public class WarningPrinter +{ + private final PrintStream printStream; + private final StringMatcher classFilter; + private int warningCount; + + + /** + * Creates a new WarningPrinter that prints to the System.err print stream. + */ + public WarningPrinter() + { + this(System.err); + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream. + */ + public WarningPrinter(PrintStream printStream) + { + this.printStream = printStream; + this.classFilter = null; + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream, + * except if the names of any involved classes matches the given filter. + */ + public WarningPrinter(PrintStream printStream, List classFilter) + { + this.printStream = printStream; + this.classFilter = classFilter == null ? null : + new ListParser(new ClassNameParser()).parse(classFilter); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class name passes the class name filter. + */ + public void print(String className, String warning) + { + if (accepts(className)) + { + print(warning); + } + } + + + /** + * Returns whether the given class name passes the class name filter. + */ + public boolean accepts(String className) + { + return classFilter == null || + !classFilter.matches(className); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class names pass the class name filter. + */ + public void print(String className1, String className2, String warning) + { + if (accepts(className1, className2)) + { + print(warning); + } + } + + + /** + * Returns whether the given class names pass the class name filter. + */ + public boolean accepts(String className1, String className2) + { + return classFilter == null || + !(classFilter.matches(className1) || + classFilter.matches(className2)); + } + + + /** + * Prints out the given warning and increments the warning count. + */ + private void print(String warning) + { + printStream.println(warning); + + warningCount++; + } + + + /** + * Returns the number of warnings printed so far. + */ + public int getWarningCount() + { + return warningCount; + } +} diff --git a/src/proguard/classfile/util/package.html b/src/proguard/classfile/util/package.html new file mode 100644 index 0000000..b1b881e --- /dev/null +++ b/src/proguard/classfile/util/package.html @@ -0,0 +1,3 @@ +<body> +This package contains utility classes for processing class files. +</body> diff --git a/src/proguard/classfile/visitor/AllClassVisitor.java b/src/proguard/classfile/visitor/AllClassVisitor.java new file mode 100644 index 0000000..06aca2c --- /dev/null +++ b/src/proguard/classfile/visitor/AllClassVisitor.java @@ -0,0 +1,47 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.ClassPool; + + +/** + * This ClassPoolVisitor lets a given ClassVisitor visit all Clazz + * objects of the class pools it visits. + * + * @author Eric Lafortune + */ +public class AllClassVisitor implements ClassPoolVisitor +{ + private final ClassVisitor classVisitor; + + + public AllClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + public void visitClassPool(ClassPool classPool) + { + classPool.classesAccept(classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/src/proguard/classfile/visitor/AllFieldVisitor.java new file mode 100644 index 0000000..8bff7d4 --- /dev/null +++ b/src/proguard/classfile/visitor/AllFieldVisitor.java @@ -0,0 +1,55 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This ClassVisitor lets a given MemberVisitor visit all FieldMember + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllFieldVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllFieldVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllMemberVisitor.java b/src/proguard/classfile/visitor/AllMemberVisitor.java new file mode 100644 index 0000000..448470e --- /dev/null +++ b/src/proguard/classfile/visitor/AllMemberVisitor.java @@ -0,0 +1,57 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This ClassVisitor lets a given MemberVisitor visit all Member + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllMemberVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldsAccept(memberVisitor); + programClass.methodsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldsAccept(memberVisitor); + libraryClass.methodsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/src/proguard/classfile/visitor/AllMethodVisitor.java new file mode 100644 index 0000000..75b919d --- /dev/null +++ b/src/proguard/classfile/visitor/AllMethodVisitor.java @@ -0,0 +1,55 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This ClassVisitor lets a given MemberVisitor visit all MethodMember + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllMethodVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllMethodVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.methodsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.methodsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/BottomClassFilter.java b/src/proguard/classfile/visitor/BottomClassFilter.java new file mode 100644 index 0000000..8f5bdd1 --- /dev/null +++ b/src/proguard/classfile/visitor/BottomClassFilter.java @@ -0,0 +1,69 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, but only when visiting classes that don't + * have any subclasses. + * + * @author Eric Lafortune + */ +public class BottomClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ProgramClassFilter. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public BottomClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Is this a bottom class in the class hierarchy? + if (programClass.subClasses == null) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Is this a bottom class in the class hierarchy? + if (libraryClass.subClasses == null) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} diff --git a/src/proguard/classfile/visitor/ClassAccessFilter.java b/src/proguard/classfile/visitor/ClassAccessFilter.java new file mode 100644 index 0000000..a8815b6 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassAccessFilter.java @@ -0,0 +1,88 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, but only when the visited class + * has the proper access flags. + * + * @see ClassConstants + * + * @author Eric Lafortune + */ +public class ClassAccessFilter implements ClassVisitor +{ + private final int requiredSetAccessFlags; + private final int requiredUnsetAccessFlags; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassAccessFilter. + * @param requiredSetAccessFlags the class access flags that should be + * set. + * @param requiredUnsetAccessFlags the class access flags that should be + * unset. + * @param classVisitor the <code>ClassVisitor</code> to + * which visits will be delegated. + */ + public ClassAccessFilter(int requiredSetAccessFlags, + int requiredUnsetAccessFlags, + ClassVisitor classVisitor) + { + this.requiredSetAccessFlags = requiredSetAccessFlags; + this.requiredUnsetAccessFlags = requiredUnsetAccessFlags; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (accepted(programClass.getAccessFlags())) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (accepted(libraryClass.getAccessFlags())) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean accepted(int accessFlags) + { + return (requiredSetAccessFlags & ~accessFlags) == 0 && + (requiredUnsetAccessFlags & accessFlags) == 0; + } +} diff --git a/src/proguard/classfile/visitor/ClassCleaner.java b/src/proguard/classfile/visitor/ClassCleaner.java new file mode 100644 index 0000000..36165ef --- /dev/null +++ b/src/proguard/classfile/visitor/ClassCleaner.java @@ -0,0 +1,275 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>ClassVisitor</code> removes all visitor information of the + * classes it visits. + * + * @author Eric Lafortune + */ +public class ClassCleaner +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + ExceptionInfoVisitor, + InnerClassesInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + clean(programClass); + + programClass.constantPoolEntriesAccept(this); + + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + clean(libraryClass); + + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + clean(constant); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + clean(programMember); + + programMember.attributesAccept(programClass, this); + } + + + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + clean(libraryMember); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + clean(attribute); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + clean(innerClassesAttribute); + + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + clean(exceptionsAttribute); + + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + clean(codeAttribute); + + codeAttribute.exceptionsAccept(clazz, method, this); + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + clean(stackMapAttribute); + + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + clean(stackMapTableAttribute); + + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + clean(annotationsAttribute); + + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + clean(parameterAnnotationsAttribute); + + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + clean(annotationDefaultAttribute); + + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + clean(innerClassesInfo); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + clean(exceptionInfo); + } + + + // Implementations for StackMapFrameVisitor. + + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + clean(sameZeroFrame); + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + clean(sameOneFrame); + + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + clean(lessZeroFrame); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + clean(moreZeroFrame); + + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + clean(fullFrame); + + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + clean(verificationType); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + clean(annotation); + + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + clean(elementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + clean(annotationElementValue); + + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + clean(arrayElementValue); + } + + + // Small utility methods. + + private void clean(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(null); + } +} diff --git a/src/proguard/classfile/visitor/ClassCollector.java b/src/proguard/classfile/visitor/ClassCollector.java new file mode 100644 index 0000000..a69fe76 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassCollector.java @@ -0,0 +1,58 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.util.SimplifiedVisitor; + +import java.util.Set; + +/** + * This <code>ClassVisitor</code> collects the classes that it visits in the + * given collection. + * + * @author Eric Lafortune + */ +public class ClassCollector +extends SimplifiedVisitor +implements ClassVisitor +{ + private final Set set; + + + /** + * Creates a new ClassCollector. + * @param set the <code>Set</code> in which all class names will be + * collected. + */ + public ClassCollector(Set set) + { + this.set = set; + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + set.add(clazz); + } +} diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/visitor/ClassCounter.java new file mode 100644 index 0000000..c58c090 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassCounter.java @@ -0,0 +1,56 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This ClassVisitor counts the number of classes that has been visited. + * + * @author Eric Lafortune + */ +public class ClassCounter implements ClassVisitor +{ + private int count; + + + /** + * Returns the number of classes that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) + { + count++; + } + + + public void visitProgramClass(ProgramClass programClass) + { + count++; + } +} diff --git a/src/proguard/classfile/visitor/ClassForNameClassVisitor.java b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java new file mode 100644 index 0000000..ee028f8 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java @@ -0,0 +1,66 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + + +/** + * This ConstantVisitor lets a given <code>ClassVisitor</code> visit all + * constant classes involved in any <code>Class.forName</code> constructs that + * it visits. + * + * @see DotClassClassVisitor + * + * @author Eric Lafortune + */ +public class ClassForNameClassVisitor +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public ClassForNameClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Visit the referenced class from the Class.forName construct, if any. + stringConstant.referencedClassAccept(classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java new file mode 100644 index 0000000..2e1755e --- /dev/null +++ b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java @@ -0,0 +1,91 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> lets a given <code>ClassVisitor</code> + * optionally travel to the visited class, its superclass, its interfaces, and + * its subclasses. + * + * @author Eric Lafortune + */ +public class ClassHierarchyTraveler implements ClassVisitor +{ + private final boolean visitThisClass; + private final boolean visitSuperClass; + private final boolean visitInterfaces; + private final boolean visitSubclasses; + + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param visitThisClass specifies whether to visit the originally visited + * classes. + * @param visitSuperClass specifies whether to visit the super classes of + * the visited classes. + * @param visitInterfaces specifies whether to visit the interfaces of + * the visited classes. + * @param visitSubclasses specifies whether to visit the subclasses of + * the visited classes. + * @param classVisitor the <code>ClassVisitor</code> to + * which visits will be delegated. + */ + public ClassHierarchyTraveler(boolean visitThisClass, + boolean visitSuperClass, + boolean visitInterfaces, + boolean visitSubclasses, + ClassVisitor classVisitor) + { + this.visitThisClass = visitThisClass; + this.visitSuperClass = visitSuperClass; + this.visitInterfaces = visitInterfaces; + this.visitSubclasses = visitSubclasses; + + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.hierarchyAccept(visitThisClass, + visitSuperClass, + visitInterfaces, + visitSubclasses, + classVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.hierarchyAccept(visitThisClass, + visitSuperClass, + visitInterfaces, + visitSubclasses, + classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/ClassNameFilter.java b/src/proguard/classfile/visitor/ClassNameFilter.java new file mode 100644 index 0000000..c016a34 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassNameFilter.java @@ -0,0 +1,112 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.util.*; + +import java.util.List; + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, but only when the visited class has a name that + * matches a given regular expression. + * + * @author Eric Lafortune + */ +public class ClassNameFilter implements ClassVisitor +{ + private final StringMatcher regularExpressionMatcher; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which class names + * will be matched. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public ClassNameFilter(String regularExpression, + ClassVisitor classVisitor) + { + this(new ListParser(new ClassNameParser()).parse(regularExpression), + classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which class names + * will be matched. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public ClassNameFilter(List regularExpression, + ClassVisitor classVisitor) + { + this(new ListParser(new ClassNameParser()).parse(regularExpression), + classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpressionMatcher the regular expression against which + * class names will be matched. + * @param classVisitor the <code>ClassVisitor</code> to which + * visits will be delegated. + */ + public ClassNameFilter(StringMatcher regularExpressionMatcher, + ClassVisitor classVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (accepted(programClass.getName())) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (accepted(libraryClass.getName())) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean accepted(String name) + { + return regularExpressionMatcher.matches(name); + } +} diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/ClassPoolFiller.java new file mode 100644 index 0000000..e1773de --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPoolFiller.java @@ -0,0 +1,55 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; + + +/** + * This ClassVisitor collects all the classes it visits in a given + * class pool. + * + * @author Eric Lafortune + */ +public class ClassPoolFiller +extends SimplifiedVisitor +implements ClassVisitor +{ + private final ClassPool classPool; + + + /** + * Creates a new ClassPoolFiller. + */ + public ClassPoolFiller(ClassPool classPool) + { + this.classPool = classPool; + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + classPool.addClass(clazz); + } +} diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/src/proguard/classfile/visitor/ClassPoolVisitor.java new file mode 100644 index 0000000..0b659dc --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPoolVisitor.java @@ -0,0 +1,37 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.ClassPool; + + +/** + * This interface specifies the methods for a visitor of + * <code>ClassPool</code> objects. Note that there is only a single + * implementation of <code>ClassPool</code>, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface ClassPoolVisitor +{ + public void visitClassPool(ClassPool classPool); +} diff --git a/src/proguard/classfile/visitor/ClassPresenceFilter.java b/src/proguard/classfile/visitor/ClassPresenceFilter.java new file mode 100644 index 0000000..429c340 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPresenceFilter.java @@ -0,0 +1,93 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to one of two + * <code>ClassVisitor</code> instances, depending on whether the name of + * the visited class file is present in a given <code>ClassPool</code> or not. + * + * @author Eric Lafortune + */ +public class ClassPresenceFilter implements ClassVisitor +{ + private final ClassPool classPool; + private final ClassVisitor presentClassVisitor; + private final ClassVisitor missingClassVisitor; + + + /** + * Creates a new ClassPresenceFilter. + * @param classPool the <code>ClassPool</code> in which the + * presence will be tested. + * @param presentClassVisitor the <code>ClassVisitor</code> to which visits + * of present class files will be delegated. + * @param missingClassVisitor the <code>ClassVisitor</code> to which visits + * of missing class files will be delegated. + */ + public ClassPresenceFilter(ClassPool classPool, + ClassVisitor presentClassVisitor, + ClassVisitor missingClassVisitor) + { + this.classPool = classPool; + this.presentClassVisitor = presentClassVisitor; + this.missingClassVisitor = missingClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + ClassVisitor classFileVisitor = classFileVisitor(programClass); + + if (classFileVisitor != null) + { + classFileVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + ClassVisitor classFileVisitor = classFileVisitor(libraryClass); + + if (classFileVisitor != null) + { + classFileVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + /** + * Returns the appropriate <code>ClassVisitor</code>. + */ + private ClassVisitor classFileVisitor(Clazz clazz) + { + return classPool.getClass(clazz.getName()) != null ? + presentClassVisitor : + missingClassVisitor; + } +} diff --git a/src/proguard/classfile/visitor/ClassPrinter.java b/src/proguard/classfile/visitor/ClassPrinter.java new file mode 100644 index 0000000..1da7d16 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPrinter.java @@ -0,0 +1,954 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; + +import java.io.PrintStream; + + +/** + * This <code>ClassVisitor</code> prints out the complete internal + * structure of the classes it visits. + * + * @author Eric Lafortune + */ +public class ClassPrinter +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + ExceptionInfoVisitor, + InnerClassesInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor, + InstructionVisitor +{ + private static final String INDENTATION = " "; + + private final PrintStream ps; + private int indentation; + + + /** + * Creates a new ClassPrinter that prints to <code>System.out</code>. + */ + public ClassPrinter() + { + this(System.out); + } + + + /** + * Creates a new ClassPrinter that prints to the given + * <code>PrintStream</code>. + */ + public ClassPrinter(PrintStream printStream) + { + ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + println("_____________________________________________________________________"); + println(visitorInfo(programClass) + " " + + "Program class: " + programClass.getName()); + indent(); + println("Superclass: " + programClass.getSuperName()); + println("Major version: 0x" + Integer.toHexString(ClassUtil.internalMajorClassVersion(programClass.u4version))); + println("Minor version: 0x" + Integer.toHexString(ClassUtil.internalMinorClassVersion(programClass.u4version))); + println("Access flags: 0x" + Integer.toHexString(programClass.u2accessFlags)); + println(" = " + + ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0 ? "@ " : "") + + ClassUtil.externalClassAccessFlags(programClass.u2accessFlags) + + ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0 ? "enum " : + (programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ? "class " : + "") + + ClassUtil.externalClassName(programClass.getName()) + + (programClass.u2superClass == 0 ? "" : " extends " + + ClassUtil.externalClassName(programClass.getSuperName()))); + outdent(); + println(); + + println("Interfaces (count = " + programClass.u2interfacesCount + "):"); + indent(); + programClass.interfaceConstantsAccept(this); + outdent(); + println(); + + println("Constant Pool (count = " + programClass.u2constantPoolCount + "):"); + indent(); + programClass.constantPoolEntriesAccept(this); + outdent(); + println(); + + println("Fields (count = " + programClass.u2fieldsCount + "):"); + indent(); + programClass.fieldsAccept(this); + outdent(); + println(); + + println("Methods (count = " + programClass.u2methodsCount + "):"); + indent(); + programClass.methodsAccept(this); + outdent(); + println(); + + println("Class file attributes (count = " + programClass.u2attributesCount + "):"); + indent(); + programClass.attributesAccept(this); + outdent(); + println(); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + println("_____________________________________________________________________"); + println(visitorInfo(libraryClass) + " " + + "Library class: " + libraryClass.getName()); + indent(); + println("Superclass: " + libraryClass.getSuperName()); + println("Access flags: 0x" + Integer.toHexString(libraryClass.u2accessFlags)); + println(" = " + + ((libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0 ? "@ " : "") + + ClassUtil.externalClassAccessFlags(libraryClass.u2accessFlags) + + ((libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0 ? "enum " : + (libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ? "class " : + "") + + ClassUtil.externalClassName(libraryClass.getName()) + + (libraryClass.getSuperName() == null ? "" : " extends " + + ClassUtil.externalClassName(libraryClass.getSuperName()))); + outdent(); + println(); + + println("Interfaces (count = " + libraryClass.interfaceClasses.length + "):"); + for (int index = 0; index < libraryClass.interfaceClasses.length; index++) + { + Clazz interfaceClass = libraryClass.interfaceClasses[index]; + if (interfaceClass != null) + { + println(" + " + interfaceClass.getName()); + } + } + + println("Fields (count = " + libraryClass.fields.length + "):"); + libraryClass.fieldsAccept(this); + + println("Methods (count = " + libraryClass.methods.length + "):"); + libraryClass.methodsAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + println(visitorInfo(integerConstant) + " Integer [" + + integerConstant.getValue() + "]"); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + println(visitorInfo(longConstant) + " Long [" + + longConstant.getValue() + "]"); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + println(visitorInfo(floatConstant) + " Float [" + + floatConstant.getValue() + "]"); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + println(visitorInfo(doubleConstant) + " Double [" + + doubleConstant.getValue() + "]"); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + println(visitorInfo(stringConstant) + " String [" + + clazz.getString(stringConstant.u2stringIndex) + "]"); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + println(visitorInfo(utf8Constant) + " Utf8 [" + + utf8Constant.getString() + "]"); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + println(visitorInfo(fieldrefConstant) + " Fieldref [" + + clazz.getClassName(fieldrefConstant.u2classIndex) + "." + + clazz.getName(fieldrefConstant.u2nameAndTypeIndex) + " " + + clazz.getType(fieldrefConstant.u2nameAndTypeIndex) + "]"); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + println(visitorInfo(interfaceMethodrefConstant) + " InterfaceMethodref [" + + clazz.getClassName(interfaceMethodrefConstant.u2classIndex) + "." + + clazz.getName(interfaceMethodrefConstant.u2nameAndTypeIndex) + " " + + clazz.getType(interfaceMethodrefConstant.u2nameAndTypeIndex) + "]"); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + println(visitorInfo(methodrefConstant) + " Methodref [" + + clazz.getClassName(methodrefConstant.u2classIndex) + "." + + clazz.getName(methodrefConstant.u2nameAndTypeIndex) + " " + + clazz.getType(methodrefConstant.u2nameAndTypeIndex) + "]"); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + println(visitorInfo(classConstant) + " Class [" + + clazz.getString(classConstant.u2nameIndex) + "]"); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + println(visitorInfo(nameAndTypeConstant) + " NameAndType [" + + clazz.getString(nameAndTypeConstant.u2nameIndex) + " " + + clazz.getString(nameAndTypeConstant.u2descriptorIndex) + "]"); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + println(visitorInfo(programField) + " " + + "Field: " + + programField.getName(programClass) + " " + + programField.getDescriptor(programClass)); + + indent(); + println("Access flags: 0x" + Integer.toHexString(programField.u2accessFlags)); + println(" = " + + ClassUtil.externalFullFieldDescription(programField.u2accessFlags, + programField.getName(programClass), + programField.getDescriptor(programClass))); + + visitMember(programClass, programField); + outdent(); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + println(visitorInfo(programMethod) + " " + + "Method: " + + programMethod.getName(programClass) + + programMethod.getDescriptor(programClass)); + + indent(); + println("Access flags: 0x" + Integer.toHexString(programMethod.u2accessFlags)); + println(" = " + + ClassUtil.externalFullMethodDescription(programClass.getName(), + programMethod.u2accessFlags, + programMethod.getName(programClass), + programMethod.getDescriptor(programClass))); + + visitMember(programClass, programMethod); + outdent(); + } + + + private void visitMember(ProgramClass programClass, ProgramMember programMember) + { + if (programMember.u2attributesCount > 0) + { + println("Class member attributes (count = " + programMember.u2attributesCount + "):"); + programMember.attributesAccept(programClass, this); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + println(visitorInfo(libraryField) + " " + + "Field: " + + libraryField.getName(libraryClass) + " " + + libraryField.getDescriptor(libraryClass)); + + indent(); + println("Access flags: 0x" + Integer.toHexString(libraryField.u2accessFlags)); + println(" = " + + ClassUtil.externalFullFieldDescription(libraryField.u2accessFlags, + libraryField.getName(libraryClass), + libraryField.getDescriptor(libraryClass))); + outdent(); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + println(visitorInfo(libraryMethod) + " " + + "Method: " + + libraryMethod.getName(libraryClass) + " " + + libraryMethod.getDescriptor(libraryClass)); + + indent(); + println("Access flags: 0x" + Integer.toHexString(libraryMethod.u2accessFlags)); + println(" = " + + ClassUtil.externalFullMethodDescription(libraryClass.getName(), + libraryMethod.u2accessFlags, + libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass))); + outdent(); + } + + + // Implementations for AttributeVisitor. + // Note that attributes are typically only referenced once, so we don't + // test if they are marked already. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + println(visitorInfo(unknownAttribute) + + " Unknown attribute (" + clazz.getString(unknownAttribute.u2attributeNameIndex) + ")"); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + println(visitorInfo(sourceFileAttribute) + + " Source file attribute:"); + + indent(); + clazz.constantPoolEntryAccept(sourceFileAttribute.u2sourceFileIndex, this); + outdent(); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + println(visitorInfo(sourceDirAttribute) + + " Source dir attribute:"); + + indent(); + clazz.constantPoolEntryAccept(sourceDirAttribute.u2sourceDirIndex, this); + outdent(); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + println(visitorInfo(innerClassesAttribute) + + " Inner classes attribute (count = " + innerClassesAttribute.u2classesCount + ")"); + + indent(); + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + outdent(); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + println(visitorInfo(enclosingMethodAttribute) + + " Enclosing method attribute:"); + + indent(); + clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2classIndex, this); + + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2nameAndTypeIndex, this); + } + outdent(); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + println(visitorInfo(deprecatedAttribute) + + " Deprecated attribute"); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + println(visitorInfo(syntheticAttribute) + + " Synthetic attribute"); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + println(visitorInfo(signatureAttribute) + + " Signature attribute:"); + + indent(); + clazz.constantPoolEntryAccept(signatureAttribute.u2signatureIndex, this); + outdent(); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + println(visitorInfo(constantValueAttribute) + + " Constant value attribute:"); + + clazz.constantPoolEntryAccept(constantValueAttribute.u2constantValueIndex, this); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + println(visitorInfo(exceptionsAttribute) + + " Exceptions attribute (count = " + exceptionsAttribute.u2exceptionIndexTableLength + ")"); + + indent(); + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this); + outdent(); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + println(visitorInfo(codeAttribute) + + " Code attribute instructions (code length = "+ codeAttribute.u4codeLength + + ", locals = "+ codeAttribute.u2maxLocals + + ", stack = "+ codeAttribute.u2maxStack + "):"); + + indent(); + + codeAttribute.instructionsAccept(clazz, method, this); + + println("Code attribute exceptions (count = " + + codeAttribute.u2exceptionTableLength + "):"); + + codeAttribute.exceptionsAccept(clazz, method, this); + + println("Code attribute attributes (attribute count = " + + codeAttribute.u2attributesCount + "):"); + + codeAttribute.attributesAccept(clazz, method, this); + + outdent(); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + println(visitorInfo(codeAttribute) + + " Stack map attribute (count = "+ + stackMapAttribute.u2stackMapFramesCount + "):"); + + indent(); + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + outdent(); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + println(visitorInfo(codeAttribute) + + " Stack map table attribute (count = "+ + stackMapTableAttribute.u2stackMapFramesCount + "):"); + + indent(); + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + outdent(); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + println(visitorInfo(lineNumberTableAttribute) + + " Line number table attribute (count = " + + lineNumberTableAttribute.u2lineNumberTableLength + ")"); + + indent(); + lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); + outdent(); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + println(visitorInfo(localVariableTableAttribute) + + " Local variable table attribute (count = " + + localVariableTableAttribute.u2localVariableTableLength + ")"); + + indent(); + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + outdent(); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + println(visitorInfo(localVariableTypeTableAttribute) + + " Local variable type table attribute (count = "+ + localVariableTypeTableAttribute.u2localVariableTypeTableLength + ")"); + + indent(); + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + outdent(); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + println(visitorInfo(runtimeVisibleAnnotationsAttribute) + + " Runtime visible annotations attribute:"); + + indent(); + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, this); + outdent(); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + println(visitorInfo(runtimeInvisibleAnnotationsAttribute) + + " Runtime invisible annotations attribute:"); + + indent(); + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, this); + outdent(); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + println(visitorInfo(runtimeVisibleParameterAnnotationsAttribute) + + " Runtime visible parameter annotations attribute (parameter count = " + runtimeVisibleParameterAnnotationsAttribute.u2parametersCount + "):"); + + indent(); + runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + outdent(); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + println(visitorInfo(runtimeInvisibleParameterAnnotationsAttribute) + + " Runtime invisible parameter annotations attribute (parameter count = " + runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount + "):"); + + indent(); + runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + outdent(); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + println(visitorInfo(annotationDefaultAttribute) + + " Annotation default attribute:"); + + indent(); + annotationDefaultAttribute.defaultValueAccept(clazz, this); + outdent(); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + println(visitorInfo(innerClassesInfo) + + " InnerClassesInfo:"); + + indent(); + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfo.outerClassConstantAccept(clazz, this); + innerClassesInfo.innerNameConstantAccept(clazz, this); + outdent(); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + println(instruction.toString(offset)); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + println(constantInstruction.toString(offset)); + + indent(); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + outdent(); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + println(tableSwitchInstruction.toString(offset)); + + indent(); + + int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; + + for (int index = 0; index < jumpOffsets.length; index++) + { + int jumpOffset = jumpOffsets[index]; + println(Integer.toString(tableSwitchInstruction.lowCase + index) + ": offset = " + jumpOffset + ", target = " + (offset + jumpOffset)); + } + + int defaultOffset = tableSwitchInstruction.defaultOffset; + println("default: offset = " + defaultOffset + ", target = "+ (offset + defaultOffset)); + + outdent(); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + println(lookUpSwitchInstruction.toString(offset)); + + indent(); + + int[] cases = lookUpSwitchInstruction.cases; + int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets; + + for (int index = 0; index < jumpOffsets.length; index++) + { + int jumpOffset = jumpOffsets[index]; + println(Integer.toString(cases[index]) + ": offset = " + jumpOffset + ", target = " + (offset + jumpOffset)); + } + + int defaultOffset = lookUpSwitchInstruction.defaultOffset; + println("default: offset = " + defaultOffset + ", target = "+ (offset + defaultOffset)); + + outdent(); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + println(visitorInfo(exceptionInfo) + + " ExceptionInfo (" + + exceptionInfo.u2startPC + " -> " + + exceptionInfo.u2endPC + ": " + + exceptionInfo.u2handlerPC + "):"); + + if (exceptionInfo.u2catchType != 0) + { + clazz.constantPoolEntryAccept(exceptionInfo.u2catchType, this); + } + } + + + // Implementations for StackMapFrameVisitor. + + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + println(visitorInfo(sameZeroFrame) + + " [" + offset + "]" + + " Var: ..., Stack: (empty)"); + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + print(visitorInfo(sameOneFrame) + + " [" + offset + "]" + + " Var: ..., Stack: "); + + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + + println(); + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + println(visitorInfo(lessZeroFrame) + + " [" + offset + "]" + + " Var: -" + lessZeroFrame.choppedVariablesCount + + ", Stack: (empty)"); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + print(visitorInfo(moreZeroFrame) + + " [" + offset + "]" + + " Var: ..."); + + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + + ps.println(", Stack: (empty)"); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + print(visitorInfo(fullFrame) + + " [" + offset + "]" + + " Var: "); + + fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); + + ps.print(", Stack: "); + + fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); + + println(); + } + + + // Implementations for VerificationTypeVisitor. + + public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType) + { + ps.print("[i]"); + } + + + public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType) + { + ps.print("[f]"); + } + + + public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType) + { + ps.print("[l]"); + } + + + public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType) + { + ps.print("[d]"); + } + + + public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType) + { + ps.print("[T]"); + } + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + ps.print("[a:" + clazz.getClassName(objectType.u2classIndex) + "]"); + } + + + public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType) + { + ps.print("[n]"); + } + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + ps.print("[u:" + uninitializedType.u2newInstructionOffset + "]"); + } + + + public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType) + { + ps.print("[u:this]"); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + println("[" + lineNumberInfo.u2startPC + "] -> line " + + lineNumberInfo.u2lineNumber); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + println("#" + localVariableInfo.u2index + ": " + + localVariableInfo.u2startPC + " -> " + + (localVariableInfo.u2startPC + localVariableInfo.u2length) + " [" + + clazz.getString(localVariableInfo.u2descriptorIndex) + " " + + clazz.getString(localVariableInfo.u2nameIndex) + "]"); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + println("#" + localVariableTypeInfo.u2index + ": " + + localVariableTypeInfo.u2startPC + " -> " + + (localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length) + " [" + + clazz.getString(localVariableTypeInfo.u2signatureIndex) + " " + + clazz.getString(localVariableTypeInfo.u2nameIndex) + "]"); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + println(visitorInfo(annotation) + + " Annotation [" + clazz.getString(annotation.u2typeIndex) + "]:"); + + indent(); + annotation.elementValuesAccept(clazz, this); + outdent(); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + println(visitorInfo(constantElementValue) + + " Constant element value [" + + (constantElementValue.u2elementNameIndex == 0 ? "(default)" : + clazz.getString(constantElementValue.u2elementNameIndex)) + " '" + + constantElementValue.u1tag + "']"); + + indent(); + clazz.constantPoolEntryAccept(constantElementValue.u2constantValueIndex, this); + outdent(); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + println(visitorInfo(enumConstantElementValue) + + " Enum constant element value [" + + (enumConstantElementValue.u2elementNameIndex == 0 ? "(default)" : + clazz.getString(enumConstantElementValue.u2elementNameIndex)) + ", " + + clazz.getString(enumConstantElementValue.u2typeNameIndex) + ", " + + clazz.getString(enumConstantElementValue.u2constantNameIndex) + "]"); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + println(visitorInfo(classElementValue) + + " Class element value [" + + (classElementValue.u2elementNameIndex == 0 ? "(default)" : + clazz.getString(classElementValue.u2elementNameIndex)) + ", " + + clazz.getString(classElementValue.u2classInfoIndex) + "]"); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + println(visitorInfo(annotationElementValue) + + " Annotation element value [" + + (annotationElementValue.u2elementNameIndex == 0 ? "(default)" : + clazz.getString(annotationElementValue.u2elementNameIndex)) + "]:"); + + indent(); + annotationElementValue.annotationAccept(clazz, this); + outdent(); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + println(visitorInfo(arrayElementValue) + + " Array element value [" + + (arrayElementValue.u2elementNameIndex == 0 ? "(default)" : + clazz.getString(arrayElementValue.u2elementNameIndex)) + "]:"); + + indent(); + arrayElementValue.elementValuesAccept(clazz, annotation, this); + outdent(); + } + + + // Small utility methods. + + private void indent() + { + indentation++; + } + + private void outdent() + { + indentation--; + } + + private void println(String string) + { + print(string); + println(); + + } + + private void print(String string) + { + for (int index = 0; index < indentation; index++) + { + ps.print(INDENTATION); + } + + ps.print(string); + } + + private void println() + { + ps.println(); + } + + + private String visitorInfo(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == null ? "-" : "+"; + } +} diff --git a/src/proguard/classfile/visitor/ClassVersionFilter.java b/src/proguard/classfile/visitor/ClassVersionFilter.java new file mode 100644 index 0000000..578cabf --- /dev/null +++ b/src/proguard/classfile/visitor/ClassVersionFilter.java @@ -0,0 +1,72 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to program classes to + * another given <code>ClassVisitor</code>, but only when the class version + * number of the visited program class lies in a given range. + * + * @author Eric Lafortune + */ +public class ClassVersionFilter implements ClassVisitor +{ + private final int minimumClassVersion; + private final int maximumClassVersion; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassVersionFilter. + * @param minimumClassVersion the minimum class version number. + * @param maximumClassVersion the maximum class version number. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public ClassVersionFilter(int minimumClassVersion, + int maximumClassVersion, + ClassVisitor classVisitor) + { + this.minimumClassVersion = minimumClassVersion; + this.maximumClassVersion = maximumClassVersion; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (programClass.u4version >= minimumClassVersion && + programClass.u4version <= maximumClassVersion) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Library classes don't have version numbers. + } +} diff --git a/src/proguard/classfile/visitor/ClassVersionSetter.java b/src/proguard/classfile/visitor/ClassVersionSetter.java new file mode 100644 index 0000000..34dfbc1 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassVersionSetter.java @@ -0,0 +1,83 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +import java.util.Set; + +/** + * This <code>ClassVisitor</code> sets the version number of the program classes + * that it visits. + * + * @author Eric Lafortune + */ +public class ClassVersionSetter implements ClassVisitor +{ + private final int classVersion; + + private final Set newerClassVersions; + + + /** + * Creates a new ClassVersionSetter. + * @param classVersion the class version number. + */ + public ClassVersionSetter(int classVersion) + { + this(classVersion, null); + } + + + /** + * Creates a new ClassVersionSetter that also stores any newer class version + * numbers that it encounters while visiting program classes. + * @param classVersion the class version number. + * @param newerClassVersions the <code>Set</code> in which newer class + * version numbers can be collected. + */ + public ClassVersionSetter(int classVersion, + Set newerClassVersions) + { + this.classVersion = classVersion; + this.newerClassVersions = newerClassVersions; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (programClass.u4version > classVersion && + newerClassVersions != null) + { + newerClassVersions.add(new Integer(programClass.u4version)); + } + + programClass.u4version = classVersion; + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Library classes don't have version numbers. + } +} diff --git a/src/proguard/classfile/visitor/ClassVisitor.java b/src/proguard/classfile/visitor/ClassVisitor.java new file mode 100644 index 0000000..fdba2df --- /dev/null +++ b/src/proguard/classfile/visitor/ClassVisitor.java @@ -0,0 +1,36 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>Clazz</code> objects. + * + * @author Eric Lafortune + */ +public interface ClassVisitor +{ + public void visitProgramClass(ProgramClass programClass); + public void visitLibraryClass(LibraryClass libraryClass); +} diff --git a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java new file mode 100644 index 0000000..ec3fe68 --- /dev/null +++ b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java @@ -0,0 +1,100 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> lets a given <code>ClassVisitor</code> + * travel to the first concrete subclasses down in its hierarchy of abstract + * classes and concrete classes. + * + * @author Eric Lafortune + */ +public class ConcreteClassDownTraveler +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ConcreteClassDownTraveler. + * @param classVisitor the <code>ClassVisitor</code> to + * which visits will be delegated. + */ + public ConcreteClassDownTraveler(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Is this an abstract class or an interface? + if ((programClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0) + { + // Travel down the hierarchy. + Clazz[] subClasses = programClass.subClasses; + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(this); + } + } + } + else + { + // Visit the class. Don't descend any further. + programClass.accept(classVisitor); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Is this an abstract class or interface? + if ((libraryClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0) + { + // Travel down the hierarchy. + Clazz[] subClasses = libraryClass.subClasses; + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(this); + } + } + } + else + { + // Visit the class. Don't descend any further. + libraryClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/visitor/DotClassClassVisitor.java b/src/proguard/classfile/visitor/DotClassClassVisitor.java new file mode 100644 index 0000000..263dbd5 --- /dev/null +++ b/src/proguard/classfile/visitor/DotClassClassVisitor.java @@ -0,0 +1,91 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + + +/** + * This InstructionVisitor lets a given <code>ClassVisitor</code> visit all + * classes involved in any <code>.class</code> constructs that it visits. + * <p> + * Note that before JDK 1.5, <code>.class</code> constructs are actually + * compiled differently, using <code>Class.forName</code> constructs. + * + * @see ClassForNameClassVisitor + * + * @author Eric Lafortune + */ +public class DotClassClassVisitor +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public DotClassClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + byte opcode = constantInstruction.opcode; + + // Could this instruction be a .class construct? + if (opcode == InstructionConstants.OP_LDC || + opcode == InstructionConstants.OP_LDC_W) + { + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + this); + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Visit the referenced class from the .class construct. + classConstant.referencedClassAccept(classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/ExceptClassFilter.java b/src/proguard/classfile/visitor/ExceptClassFilter.java new file mode 100644 index 0000000..924485e --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptClassFilter.java @@ -0,0 +1,69 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, except for one given class. + * + * @author Eric Lafortune + */ +public class ExceptClassFilter implements ClassVisitor +{ + private final Clazz exceptClass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassNameFilter. + * @param exceptClass the class that will not be visited. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public ExceptClassFilter(Clazz exceptClass, + ClassVisitor classVisitor) + { + this.exceptClass = exceptClass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!programClass.equals(exceptClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!libraryClass.equals(exceptClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptClassesFilter.java b/src/proguard/classfile/visitor/ExceptClassesFilter.java new file mode 100644 index 0000000..7380c40 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptClassesFilter.java @@ -0,0 +1,90 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, except for classes are in a given list. + * + * @author Eric Lafortune + */ +public class ExceptClassesFilter implements ClassVisitor +{ + private final Clazz[] exceptClasses; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ExceptClassesFilter. + * @param exceptClasses the classes that will not be visited. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public ExceptClassesFilter(Clazz[] exceptClasses, + ClassVisitor classVisitor) + { + this.exceptClasses = exceptClasses; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!present(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!present(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean present(Clazz clazz) + { + if (exceptClasses == null) + { + return false; + } + + for (int index = 0; index < exceptClasses.length; index++) + { + if (exceptClasses[index].equals(clazz)) + { + return true; + } + } + + return false; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionCounter.java b/src/proguard/classfile/visitor/ExceptionCounter.java new file mode 100644 index 0000000..c324129 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionCounter.java @@ -0,0 +1,52 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor counts the number of exceptions that has been visited. + * + * @author Eric Lafortune + */ +public class ExceptionCounter implements ExceptionInfoVisitor +{ + private int count; + + + /** + * Returns the number of exceptions that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + count++; + } +} diff --git a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java new file mode 100644 index 0000000..3911e39 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java @@ -0,0 +1,64 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This <code>ExceptionInfoVisitor</code> delegates its visits to another given + * <code>ExceptionInfoVisitor</code>, but only when the visited exception + * does not cover the instruction at the given offset. + * + * @author Eric Lafortune + */ +public class ExceptionExcludedOffsetFilter +implements ExceptionInfoVisitor +{ + private final int instructionOffset; + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + /** + * Creates a new ExceptionExcludedOffsetFilter. + * @param instructionOffset the instruction offset. + * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits + * will be delegated. + */ + public ExceptionExcludedOffsetFilter(int instructionOffset, + ExceptionInfoVisitor exceptionInfoVisitor) + { + this.instructionOffset = instructionOffset; + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (!exceptionInfo.isApplicable(instructionOffset)) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + } + } +} diff --git a/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java new file mode 100644 index 0000000..e0fdec3 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java @@ -0,0 +1,62 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This <code>ExceptionInfoVisitor</code> lets a given + * <code>ConstantVisitor</code> visit all catch class constants of exceptions + * that it visits. + * + * @author Eric Lafortune + */ +public class ExceptionHandlerConstantVisitor +implements ExceptionInfoVisitor +{ + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ExceptionHandlerConstantVisitor. + * @param constantVisitor the ConstantVisitor that will visit the catch + * class constants. + */ + public ExceptionHandlerConstantVisitor(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + int catchType = exceptionInfo.u2catchType; + if (catchType != 0) + { + clazz.constantPoolEntryAccept(catchType, constantVisitor); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionHandlerFilter.java b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java new file mode 100644 index 0000000..a90fb56 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java @@ -0,0 +1,70 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This <code>ExceptionInfoVisitor</code> delegates its visits to another given + * <code>ExceptionInfoVisitor</code>, but only when the visited exception + * targets an instruction in the given range of offsets. + * + * @author Eric Lafortune + */ +public class ExceptionHandlerFilter +implements ExceptionInfoVisitor +{ + private final int startOffset; + private final int endOffset; + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + /** + * Creates a new ExceptionHandlerFilter. + * @param startOffset the start of the instruction offset range. + * @param endOffset the end of the instruction offset range. + * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits + * will be delegated. + */ + public ExceptionHandlerFilter(int startOffset, + int endOffset, + ExceptionInfoVisitor exceptionInfoVisitor) + { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + int handlerPC = exceptionInfo.u2handlerPC; + if (handlerPC >= startOffset && + handlerPC < endOffset) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java new file mode 100644 index 0000000..e2a4fc9 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java @@ -0,0 +1,64 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This <code>ExceptionInfoVisitor</code> delegates its visits to another given + * <code>ExceptionInfoVisitor</code>, but only when the visited exception + * covers the instruction at the given offset. + * + * @author Eric Lafortune + */ +public class ExceptionOffsetFilter +implements ExceptionInfoVisitor +{ + private final int instructionOffset; + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + /** + * Creates a new ExceptionOffsetFilter. + * @param instructionOffset the instruction offset. + * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits + * will be delegated. + */ + public ExceptionOffsetFilter(int instructionOffset, + ExceptionInfoVisitor exceptionInfoVisitor) + { + this.instructionOffset = instructionOffset; + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (exceptionInfo.isApplicable(instructionOffset)) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + } + } +} diff --git a/src/proguard/classfile/visitor/ExceptionRangeFilter.java b/src/proguard/classfile/visitor/ExceptionRangeFilter.java new file mode 100644 index 0000000..c541b1f --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionRangeFilter.java @@ -0,0 +1,68 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This <code>ExceptionInfoVisitor</code> delegates its visits to another given + * <code>ExceptionInfoVisitor</code>, but only when the visited exception + * overlaps with the given instruction range. + * + * @author Eric Lafortune + */ +public class ExceptionRangeFilter +implements ExceptionInfoVisitor +{ + private final int startOffset; + private final int endOffset; + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + /** + * Creates a new ExceptionRangeFilter. + * @param startOffset the start offset of the instruction range. + * @param endOffset the end offset of the instruction range. + * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits + * will be delegated. + */ + public ExceptionRangeFilter(int startOffset, + int endOffset, + ExceptionInfoVisitor exceptionInfoVisitor) + { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (exceptionInfo.isApplicable(startOffset, endOffset)) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + } + } +} diff --git a/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java new file mode 100644 index 0000000..6fe2e7d --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java @@ -0,0 +1,69 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>ConstantVisitor</code> delegates its visits to class constants + * to another given <code>ConstantVisitor</code>, except for classes that + * extend or implement a given class. This exception includes the class itself. + * + * @author Eric Lafortune + */ +public class ImplementedClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final Clazz implementedClass; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ImplementedClassConstantFilter. + * @param implementedClass the class whose implementations will not be + * visited. + * @param constantVisitor the <code>ConstantVisitor</code> to which visits + * will be delegated. + */ + public ImplementedClassConstantFilter(Clazz implementedClass, + ConstantVisitor constantVisitor) + { + this.implementedClass = implementedClass; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass == null || + !referencedClass.extendsOrImplements(implementedClass)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ImplementedClassFilter.java b/src/proguard/classfile/visitor/ImplementedClassFilter.java new file mode 100644 index 0000000..955a74e --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementedClassFilter.java @@ -0,0 +1,71 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, except for classes that extend or implement + * a given class. + * + * @author Eric Lafortune + */ +public class ImplementedClassFilter implements ClassVisitor +{ + private final Clazz implementedClass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ImplementedClassFilter. + * @param implementedClass the class whose implementations will not be + * visited. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public ImplementedClassFilter(Clazz implementedClass, + ClassVisitor classVisitor) + { + this.implementedClass = implementedClass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!programClass.extendsOrImplements(implementedClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!libraryClass.extendsOrImplements(implementedClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java new file mode 100644 index 0000000..9e9cea3 --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java @@ -0,0 +1,70 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>ConstantVisitor</code> delegates its visits to class constants + * to another given <code>ConstantVisitor</code>, except for classes that + * are extended or implemented by a given class. This exception includes the + * class itself. + * + * @author Eric Lafortune + */ +public class ImplementingClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final Clazz implementingClass; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ImplementingClassConstantFilter. + * @param implementingClass the class whose superclasses and interfaces will + * not be visited. + * @param constantVisitor the <code>ConstantVisitor</code> to which visits + * will be delegated. + */ + public ImplementingClassConstantFilter(Clazz implementingClass, + ConstantVisitor constantVisitor) + { + this.implementingClass = implementingClass; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass == null || + !implementingClass.extendsOrImplements(referencedClass)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/LibraryClassFilter.java b/src/proguard/classfile/visitor/LibraryClassFilter.java new file mode 100644 index 0000000..0e40f2f --- /dev/null +++ b/src/proguard/classfile/visitor/LibraryClassFilter.java @@ -0,0 +1,60 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, but only when visiting library classes. + * + * @author Eric Lafortune + */ +public class LibraryClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new LibraryClassFilter. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public LibraryClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Don't delegate visits to program classes. + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + classVisitor.visitLibraryClass(libraryClass); + } +} diff --git a/src/proguard/classfile/visitor/LibraryMemberFilter.java b/src/proguard/classfile/visitor/LibraryMemberFilter.java new file mode 100644 index 0000000..0ee80e5 --- /dev/null +++ b/src/proguard/classfile/visitor/LibraryMemberFilter.java @@ -0,0 +1,73 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when visiting members of library + * classes. + * + * @author Eric Lafortune + */ +public class LibraryMemberFilter implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new ProgramMemberFilter. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public LibraryMemberFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Don't delegate visits to program members. + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Don't delegate visits to program members. + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } +} diff --git a/src/proguard/classfile/visitor/MemberAccessFilter.java b/src/proguard/classfile/visitor/MemberAccessFilter.java new file mode 100644 index 0000000..6fd32e3 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberAccessFilter.java @@ -0,0 +1,122 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when the visited member has the proper + * access flags. + * <p> + * If conflicting access flags (public/private/protected) are specified, + * having one of them set will be considered sufficient. + * + * @see ClassConstants + * + * @author Eric Lafortune + */ +public class MemberAccessFilter +implements MemberVisitor +{ + // A mask of conflicting access flags. These are interpreted in a special + // way if more of them are required at the same time. In that case, one + // of them being set is sufficient. + private static final int ACCESS_MASK = + ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PROTECTED; + + private final int requiredSetAccessFlags; + private final int requiredUnsetAccessFlags; + private final int requiredOneSetAccessFlags; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberAccessFilter. + * @param requiredSetAccessFlags the class access flags that should be + * set. + * @param requiredUnsetAccessFlags the class access flags that should be + * unset. + * @param memberVisitor the <code>MemberVisitor</code> to + * which visits will be delegated. + */ + public MemberAccessFilter(int requiredSetAccessFlags, + int requiredUnsetAccessFlags, + MemberVisitor memberVisitor) + { + this.requiredSetAccessFlags = requiredSetAccessFlags & ~ACCESS_MASK; + this.requiredUnsetAccessFlags = requiredUnsetAccessFlags; + this.requiredOneSetAccessFlags = requiredSetAccessFlags & ACCESS_MASK; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (accepted(programField.getAccessFlags())) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (accepted(programMethod.getAccessFlags())) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (accepted(libraryField.getAccessFlags())) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (accepted(libraryMethod.getAccessFlags())) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + private boolean accepted(int accessFlags) + { + return (requiredSetAccessFlags & ~accessFlags) == 0 && + (requiredUnsetAccessFlags & accessFlags) == 0 && + (requiredOneSetAccessFlags == 0 || + (requiredOneSetAccessFlags & accessFlags) != 0); + } +} diff --git a/src/proguard/classfile/visitor/MemberClassAccessFilter.java b/src/proguard/classfile/visitor/MemberClassAccessFilter.java new file mode 100644 index 0000000..85272ff --- /dev/null +++ b/src/proguard/classfile/visitor/MemberClassAccessFilter.java @@ -0,0 +1,106 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.*; + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when the visited member is accessible + * from the given referencing class. + * + * @author Eric Lafortune + */ +public class MemberClassAccessFilter +implements MemberVisitor +{ + private final Clazz referencingClass; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberAccessFilter. + * @param referencingClass the class that is accessing the member. + * @param memberVisitor the <code>MemberVisitor</code> to which visits + * will be delegated. + */ + public MemberClassAccessFilter(Clazz referencingClass, + MemberVisitor memberVisitor) + { + this.referencingClass = referencingClass; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (accepted(programClass, programField.getAccessFlags())) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (accepted(programClass, programMethod.getAccessFlags())) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (accepted(libraryClass, libraryField.getAccessFlags())) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (accepted(libraryClass, libraryMethod.getAccessFlags())) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + private boolean accepted(Clazz clazz, int memberAccessFlags) + { + int accessLevel = AccessUtil.accessLevel(memberAccessFlags); + + return + (accessLevel >= AccessUtil.PUBLIC ) || + (accessLevel >= AccessUtil.PRIVATE && referencingClass.equals(clazz) ) || + (accessLevel >= AccessUtil.PACKAGE_VISIBLE && (ClassUtil.internalPackageName(referencingClass.getName()).equals( + ClassUtil.internalPackageName(clazz.getName())))) || + (accessLevel >= AccessUtil.PROTECTED && (referencingClass.extends_(clazz) || + referencingClass.extendsOrImplements(clazz)) ); + } +} diff --git a/src/proguard/classfile/visitor/MemberCollector.java b/src/proguard/classfile/visitor/MemberCollector.java new file mode 100644 index 0000000..ec68b2d --- /dev/null +++ b/src/proguard/classfile/visitor/MemberCollector.java @@ -0,0 +1,59 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; + +import java.util.Set; + +/** + * This MemberVisitor collects the concatenated name/descriptor strings of + * class members that have been visited. + * + * @author Eric Lafortune + */ +public class MemberCollector +extends SimplifiedVisitor +implements MemberVisitor +{ + private final Set set; + + + /** + * Creates a new MemberCollector. + * @param set the <code>Set</code> in which all method names/descriptor + * strings will be collected. + */ + public MemberCollector(Set set) + { + this.set = set; + } + + + // Implementations for MemberVisitor. + + + public void visitAnyMember(Clazz clazz, Member member) + { + set.add(member.getName(clazz) + member.getDescriptor(clazz)); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/MemberCounter.java b/src/proguard/classfile/visitor/MemberCounter.java new file mode 100644 index 0000000..c2da72e --- /dev/null +++ b/src/proguard/classfile/visitor/MemberCounter.java @@ -0,0 +1,72 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This MemberVisitor counts the number of class members that have been visited. + * + * @author Eric Lafortune + */ +public class MemberCounter implements MemberVisitor +{ + private int count; + + + /** + * Returns the number of class members that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for MemberVisitor. + + public void visitLibraryField(LibraryClass libraryClass, + LibraryField libraryField) + { + count++; + } + + + public void visitLibraryMethod(LibraryClass libraryClass, + LibraryMethod libraryMethod) + { + count++; + } + + + public void visitProgramField(ProgramClass programClass, + ProgramField programField) + { + count++; + } + + + public void visitProgramMethod(ProgramClass programClass, + ProgramMethod programMethod) + { + count++; + } +} diff --git a/src/proguard/classfile/visitor/MemberDescriptorFilter.java b/src/proguard/classfile/visitor/MemberDescriptorFilter.java new file mode 100644 index 0000000..bd69304 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberDescriptorFilter.java @@ -0,0 +1,113 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.util.*; + + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when the visited member + * has a descriptor that matches a given regular expression. + * + * @author Eric Lafortune + */ +public class MemberDescriptorFilter implements MemberVisitor +{ + private final StringMatcher regularExpressionMatcher; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberDescriptorFilter. + * @param regularExpression the regular expression against which member + * descriptors will be matched. + * @param memberVisitor the <code>MemberVisitor</code> to which visits + * will be delegated. + */ + public MemberDescriptorFilter(String regularExpression, + MemberVisitor memberVisitor) + { + this(new ClassNameParser().parse(regularExpression), memberVisitor); + } + + + /** + * Creates a new MemberDescriptorFilter. + * @param regularExpressionMatcher the regular expression against which + * member descriptors will be matched. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public MemberDescriptorFilter(StringMatcher regularExpressionMatcher, + MemberVisitor memberVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (accepted(programField.getDescriptor(programClass))) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (accepted(programMethod.getDescriptor(programClass))) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (accepted(libraryField.getDescriptor(libraryClass))) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (accepted(libraryMethod.getDescriptor(libraryClass))) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + private boolean accepted(String name) + { + return regularExpressionMatcher.matches(name); + } +} diff --git a/src/proguard/classfile/visitor/MemberNameFilter.java b/src/proguard/classfile/visitor/MemberNameFilter.java new file mode 100644 index 0000000..0fe450e --- /dev/null +++ b/src/proguard/classfile/visitor/MemberNameFilter.java @@ -0,0 +1,113 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.util.*; + + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when the visited member + * has a name that matches a given regular expression. + * + * @author Eric Lafortune + */ +public class MemberNameFilter implements MemberVisitor +{ + private final StringMatcher regularExpressionMatcher; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberNameFilter. + * @param regularExpression the regular expression against which member + * names will be matched. + * @param memberVisitor the <code>MemberVisitor</code> to which visits + * will be delegated. + */ + public MemberNameFilter(String regularExpression, + MemberVisitor memberVisitor) + { + this(new NameParser().parse(regularExpression), memberVisitor); + } + + + /** + * Creates a new MemberNameFilter. + * @param regularExpressionMatcher the regular expression against which + * member names will be matched. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public MemberNameFilter(StringMatcher regularExpressionMatcher, + MemberVisitor memberVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (accepted(programField.getName(programClass))) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (accepted(programMethod.getName(programClass))) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (accepted(libraryField.getName(libraryClass))) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (accepted(libraryMethod.getName(libraryClass))) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + private boolean accepted(String name) + { + return regularExpressionMatcher.matches(name); + } +} diff --git a/src/proguard/classfile/visitor/MemberToClassVisitor.java b/src/proguard/classfile/visitor/MemberToClassVisitor.java new file mode 100644 index 0000000..a405cfc --- /dev/null +++ b/src/proguard/classfile/visitor/MemberToClassVisitor.java @@ -0,0 +1,90 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This MemberVisitor delegates all visits to a given ClassVisitor. + * The latter visits the class of each visited class member, although + * never twice in a row. + * + * @author Eric Lafortune + */ +public class MemberToClassVisitor implements MemberVisitor +{ + private final ClassVisitor classVisitor; + + private Clazz lastVisitedClass; + + + public MemberToClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (!programClass.equals(lastVisitedClass)) + { + classVisitor.visitProgramClass(programClass); + + lastVisitedClass = programClass; + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (!programClass.equals(lastVisitedClass)) + { + classVisitor.visitProgramClass(programClass); + + lastVisitedClass = programClass; + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (!libraryClass.equals(lastVisitedClass)) + { + classVisitor.visitLibraryClass(libraryClass); + + lastVisitedClass = libraryClass; + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (!libraryClass.equals(lastVisitedClass)) + { + classVisitor.visitLibraryClass(libraryClass); + + lastVisitedClass = libraryClass; + } + } +} diff --git a/src/proguard/classfile/visitor/MemberVisitor.java b/src/proguard/classfile/visitor/MemberVisitor.java new file mode 100644 index 0000000..01fdf71 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberVisitor.java @@ -0,0 +1,40 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This interface specifies the methods for a visitor of + * <code>ProgramMember</code> objects and <code>LibraryMember</code> + * objects. + * + * @author Eric Lafortune + */ +public interface MemberVisitor +{ + public void visitProgramField( ProgramClass programClass, ProgramField programField); + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod); + + public void visitLibraryField( LibraryClass libraryClass, LibraryField libraryField); + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod); +} diff --git a/src/proguard/classfile/visitor/MethodImplementationFilter.java b/src/proguard/classfile/visitor/MethodImplementationFilter.java new file mode 100644 index 0000000..57d923a --- /dev/null +++ b/src/proguard/classfile/visitor/MethodImplementationFilter.java @@ -0,0 +1,70 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>MemberVisitor</code> delegates its visits to methods to + * another given <code>MemberVisitor</code>, but only when the visited + * method may have implementations. + * + * @see Clazz#mayHaveImplementations(Method) + * @author Eric Lafortune + */ +public class MethodImplementationFilter +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MethodImplementationFilter. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public MethodImplementationFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (programClass.mayHaveImplementations(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (libraryClass.mayHaveImplementations(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +} diff --git a/src/proguard/classfile/visitor/MethodImplementationTraveler.java b/src/proguard/classfile/visitor/MethodImplementationTraveler.java new file mode 100644 index 0000000..dc0ea36 --- /dev/null +++ b/src/proguard/classfile/visitor/MethodImplementationTraveler.java @@ -0,0 +1,128 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This <code>MemberVisitor</code> lets a given <code>MemberVisitor</code> + * travel to all concrete and abstract implementations of the visited methods + * in their class hierarchies. + * + * @author Eric Lafortune + */ +public class MethodImplementationTraveler +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean visitThisMethod; + private final boolean visitSuperMethods; + private final boolean visitInterfaceMethods; + private final boolean visitOverridingMethods; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MethodImplementationTraveler. + * @param visitThisMethod specifies whether to visit the originally + * visited methods. + * @param visitSuperMethods specifies whether to visit the method in + * the super classes. + * @param visitInterfaceMethods specifies whether to visit the method in + * the interface classes. + * @param visitOverridingMethods specifies whether to visit the method in + * the subclasses. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public MethodImplementationTraveler(boolean visitThisMethod, + boolean visitSuperMethods, + boolean visitInterfaceMethods, + boolean visitOverridingMethods, + MemberVisitor memberVisitor) + { + this.visitThisMethod = visitThisMethod; + this.visitSuperMethods = visitSuperMethods; + this.visitInterfaceMethods = visitInterfaceMethods; + this.visitOverridingMethods = visitOverridingMethods; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (visitThisMethod) + { + programMethod.accept(programClass, memberVisitor); + } + + if (!isSpecial(programClass, programMethod)) + { + programClass.hierarchyAccept(false, + visitSuperMethods, + visitInterfaceMethods, + visitOverridingMethods, + new NamedMethodVisitor(programMethod.getName(programClass), + programMethod.getDescriptor(programClass), + new MemberAccessFilter(0, + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC, + memberVisitor))); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (visitThisMethod) + { + libraryMethod.accept(libraryClass, memberVisitor); + } + + if (!isSpecial(libraryClass, libraryMethod)) + { + libraryClass.hierarchyAccept(false, + visitSuperMethods, + visitInterfaceMethods, + visitOverridingMethods, + new NamedMethodVisitor(libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass), + new MemberAccessFilter(0, + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC, + memberVisitor))); + } + } + + + // Small utility methods. + + private boolean isSpecial(Clazz clazz, Method method) + { + return (method.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC)) != 0 || + method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + } +} diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java new file mode 100644 index 0000000..044d55a --- /dev/null +++ b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java @@ -0,0 +1,88 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.ClassPool; + + +/** + * This ClassPoolVisitor delegates all visits to each ClassPoolVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiClassPoolVisitor implements ClassPoolVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + private ClassPoolVisitor[] classPoolVisitors; + private int classPoolVisitorCount; + + + public MultiClassPoolVisitor() + { + } + + + public MultiClassPoolVisitor(ClassPoolVisitor[] classPoolVisitors) + { + this.classPoolVisitors = classPoolVisitors; + this.classPoolVisitorCount = classPoolVisitors.length; + } + + + public void addClassPoolVisitor(ClassPoolVisitor classPoolVisitor) + { + ensureArraySize(); + + classPoolVisitors[classPoolVisitorCount++] = classPoolVisitor; + } + + + private void ensureArraySize() + { + if (classPoolVisitors == null) + { + classPoolVisitors = new ClassPoolVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (classPoolVisitors.length == classPoolVisitorCount) + { + ClassPoolVisitor[] newClassPoolVisitors = + new ClassPoolVisitor[classPoolVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(classPoolVisitors, 0, + newClassPoolVisitors, 0, + classPoolVisitorCount); + classPoolVisitors = newClassPoolVisitors; + } + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + for (int index = 0; index < classPoolVisitorCount; index++) + { + classPoolVisitors[index].visitClassPool(classPool); + } + } +} diff --git a/src/proguard/classfile/visitor/MultiClassVisitor.java b/src/proguard/classfile/visitor/MultiClassVisitor.java new file mode 100644 index 0000000..d34d91e --- /dev/null +++ b/src/proguard/classfile/visitor/MultiClassVisitor.java @@ -0,0 +1,97 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This ClassVisitor delegates all visits to each ClassVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiClassVisitor implements ClassVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + private ClassVisitor[] classVisitors; + private int classVisitorCount; + + + public MultiClassVisitor() + { + } + + + public MultiClassVisitor(ClassVisitor[] classVisitors) + { + this.classVisitors = classVisitors; + this.classVisitorCount = classVisitors.length; + } + + + public void addClassVisitor(ClassVisitor classVisitor) + { + ensureArraySize(); + + classVisitors[classVisitorCount++] = classVisitor; + } + + + private void ensureArraySize() + { + if (classVisitors == null) + { + classVisitors = new ClassVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (classVisitors.length == classVisitorCount) + { + ClassVisitor[] newClassVisitors = + new ClassVisitor[classVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(classVisitors, 0, + newClassVisitors, 0, + classVisitorCount); + classVisitors = newClassVisitors; + } + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + for (int index = 0; index < classVisitorCount; index++) + { + classVisitors[index].visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + for (int index = 0; index < classVisitorCount; index++) + { + classVisitors[index].visitLibraryClass(libraryClass); + } + } +} diff --git a/src/proguard/classfile/visitor/MultiMemberVisitor.java b/src/proguard/classfile/visitor/MultiMemberVisitor.java new file mode 100644 index 0000000..cc4629c --- /dev/null +++ b/src/proguard/classfile/visitor/MultiMemberVisitor.java @@ -0,0 +1,113 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This MemberVisitor delegates all visits to each MemberVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiMemberVisitor implements MemberVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + private MemberVisitor[] memberVisitors; + private int memberVisitorCount; + + + public MultiMemberVisitor() + { + } + + + public MultiMemberVisitor(MemberVisitor[] memberVisitors) + { + this.memberVisitors = memberVisitors; + this.memberVisitorCount = memberVisitors.length; + } + + + public void addMemberVisitor(MemberVisitor memberVisitor) + { + ensureArraySize(); + + memberVisitors[memberVisitorCount++] = memberVisitor; + } + + + private void ensureArraySize() + { + if (memberVisitors == null) + { + memberVisitors = new MemberVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (memberVisitors.length == memberVisitorCount) + { + MemberVisitor[] newMemberVisitors = + new MemberVisitor[memberVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(memberVisitors, 0, + newMemberVisitors, 0, + memberVisitorCount); + memberVisitors = newMemberVisitors; + } + } + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + for (int index = 0; index < memberVisitorCount; index++) + { + memberVisitors[index].visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + for (int index = 0; index < memberVisitorCount; index++) + { + memberVisitors[index].visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + for (int index = 0; index < memberVisitorCount; index++) + { + memberVisitors[index].visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + for (int index = 0; index < memberVisitorCount; index++) + { + memberVisitors[index].visitLibraryMethod(libraryClass, libraryMethod); + } + } +} diff --git a/src/proguard/classfile/visitor/NamedClassVisitor.java b/src/proguard/classfile/visitor/NamedClassVisitor.java new file mode 100644 index 0000000..a14d04a --- /dev/null +++ b/src/proguard/classfile/visitor/NamedClassVisitor.java @@ -0,0 +1,49 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.ClassPool; + + +/** + * This class visits Clazz objects with the given name. + * + * @author Eric Lafortune + */ +public class NamedClassVisitor implements ClassPoolVisitor +{ + private final ClassVisitor classVisitor; + private final String name; + + + public NamedClassVisitor(ClassVisitor classVisitor, + String name) + { + this.classVisitor = classVisitor; + this.name = name; + } + + + public void visitClassPool(ClassPool classPool) + { + classPool.classAccept(name, classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/src/proguard/classfile/visitor/NamedFieldVisitor.java new file mode 100644 index 0000000..76b66c6 --- /dev/null +++ b/src/proguard/classfile/visitor/NamedFieldVisitor.java @@ -0,0 +1,61 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This class visits ProgramMember objects referring to fields, identified by + * a name and descriptor pair. + * + * @author Eric Lafortune + */ +public class NamedFieldVisitor implements ClassVisitor +{ + private final String name; + private final String descriptor; + private final MemberVisitor memberVisitor; + + + public NamedFieldVisitor(String name, + String descriptor, + MemberVisitor memberVisitor) + { + this.name = name; + this.descriptor = descriptor; + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldAccept(name, descriptor, memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldAccept(name, descriptor, memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/src/proguard/classfile/visitor/NamedMethodVisitor.java new file mode 100644 index 0000000..d4611c1 --- /dev/null +++ b/src/proguard/classfile/visitor/NamedMethodVisitor.java @@ -0,0 +1,61 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This class visits ProgramMember objects referring to methods, identified by + * a name and descriptor pair. + * + * @author Eric Lafortune + */ +public class NamedMethodVisitor implements ClassVisitor +{ + private final String name; + private final String descriptor; + private final MemberVisitor memberVisitor; + + + public NamedMethodVisitor(String name, + String descriptor, + MemberVisitor memberVisitor) + { + this.name = name; + this.descriptor = descriptor; + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.methodAccept(name, descriptor, memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.methodAccept(name, descriptor, memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/ProgramClassFilter.java b/src/proguard/classfile/visitor/ProgramClassFilter.java new file mode 100644 index 0000000..fba3b21 --- /dev/null +++ b/src/proguard/classfile/visitor/ProgramClassFilter.java @@ -0,0 +1,60 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, but only when visiting program classes. + * + * @author Eric Lafortune + */ +public class ProgramClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ProgramClassFilter. + * @param classVisitor the <code>ClassVisitor</code> to which visits + * will be delegated. + */ + public ProgramClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + classVisitor.visitProgramClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Don't delegate visits to library classes. + } +} diff --git a/src/proguard/classfile/visitor/ProgramMemberFilter.java b/src/proguard/classfile/visitor/ProgramMemberFilter.java new file mode 100644 index 0000000..048a1e6 --- /dev/null +++ b/src/proguard/classfile/visitor/ProgramMemberFilter.java @@ -0,0 +1,73 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when visiting members of program + * classes. + * + * @author Eric Lafortune + */ +public class ProgramMemberFilter implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new ProgramMemberFilter. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public ProgramMemberFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + memberVisitor.visitProgramField(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Don't delegate visits to library members. + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Don't delegate visits to library members. + } +} diff --git a/src/proguard/classfile/visitor/ReferencedClassVisitor.java b/src/proguard/classfile/visitor/ReferencedClassVisitor.java new file mode 100644 index 0000000..986c3f9 --- /dev/null +++ b/src/proguard/classfile/visitor/ReferencedClassVisitor.java @@ -0,0 +1,248 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +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.SimplifiedVisitor; + +/** + * This ClassVisitor, MemberVisitor, ConstantVisitor, AttributeVisitor, etc. + * lets a given ClassVisitor visit all the referenced classes of the elements + * that it visits. Only downstream elements are considered (in order to avoid + * loops and repeated visits). + * + * @author Eric Lafortune + */ +public class ReferencedClassVisitor +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final ClassVisitor classVisitor; + + + public ReferencedClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Visit the constant pool entries. + programClass.constantPoolEntriesAccept(this); + + // Visit the fields and methods. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Visit the attributes. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Visit the superclass and interfaces. + libraryClass.superClassAccept(classVisitor); + libraryClass.interfacesAccept(classVisitor); + + // Visit the fields and methods. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Let the visitor visit the classes referenced in the descriptor string. + programMember.referencedClassesAccept(classVisitor); + + // Visit the attributes. + programMember.attributesAccept(programClass, this); + } + + + public void visitLibraryMember(LibraryClass programClass, LibraryMember libraryMember) + { + // Let the visitor visit the classes referenced in the descriptor string. + libraryMember.referencedClassesAccept(classVisitor); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Let the visitor visit the class referenced in the string constant. + stringConstant.referencedClassAccept(classVisitor); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + // Let the visitor visit the class referenced in the reference constant. + refConstant.referencedClassAccept(classVisitor); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Let the visitor visit the class referenced in the class constant. + classConstant.referencedClassAccept(classVisitor); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + // Let the visitor visit the class of the enclosing method. + enclosingMethodAttribute.referencedClassAccept(classVisitor); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Visit the attributes of the code attribute. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Visit the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Visit the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Let the visitor visit the classes referenced in the signature string. + signatureAttribute.referencedClassesAccept(classVisitor); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Visit the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Visit the parameter annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Visit the default element value. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Let the visitor visit the class referenced in the local variable. + localVariableInfo.referencedClassAccept(classVisitor); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Let the visitor visit the classes referenced in the local variable type. + localVariableTypeInfo.referencedClassesAccept(classVisitor); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Let the visitor visit the classes referenced in the annotation. + annotation.referencedClassesAccept(classVisitor); + + // Visit the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {} + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Let the visitor visit the classes referenced in the constant element value. + enumConstantElementValue.referencedClassesAccept(classVisitor); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Let the visitor visit the classes referenced in the class element value. + classElementValue.referencedClassesAccept(classVisitor); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Visit the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java new file mode 100644 index 0000000..c4d34b8 --- /dev/null +++ b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java @@ -0,0 +1,73 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor and ElementValueVisitor lets a given MemberVisitor + * visit all the referenced class members of the elements that it visits. + * + * @author Eric Lafortune + */ +public class ReferencedMemberVisitor +extends SimplifiedVisitor +implements ConstantVisitor, + ElementValueVisitor +{ + private final MemberVisitor memberVisitor; + + + public ReferencedMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + stringConstant.referencedMemberAccept(memberVisitor); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + refConstant.referencedMemberAccept(memberVisitor); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + elementValue.referencedMethodAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/SimilarMemberVisitor.java b/src/proguard/classfile/visitor/SimilarMemberVisitor.java new file mode 100644 index 0000000..6dc06af --- /dev/null +++ b/src/proguard/classfile/visitor/SimilarMemberVisitor.java @@ -0,0 +1,125 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>MemberVisitor</code> lets a given <code>MemberVisitor</code> + * visit all members that have the same name and type as the visited methods + * in the class hierarchy of a given target class. + * + * @author Eric Lafortune + */ +public class SimilarMemberVisitor +implements MemberVisitor +{ + private final Clazz targetClass; + private final boolean visitThisMember; + private final boolean visitSuperMembers; + private final boolean visitInterfaceMembers; + private final boolean visitOverridingMembers; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new SimilarMemberVisitor. + * @param targetClass the class in whose hierarchy to look for + * the visited class members. + * @param visitThisMember specifies whether to visit the class + * members in the target class itself. + * @param visitSuperMembers specifies whether to visit the class + * members in the super classes of the target + * class. + * @param visitInterfaceMembers specifies whether to visit the class + * members in the interface classes of the + * target class. + * @param visitOverridingMembers specifies whether to visit the class + * members in the subclasses of the target + * class. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public SimilarMemberVisitor(Clazz targetClass, + boolean visitThisMember, + boolean visitSuperMembers, + boolean visitInterfaceMembers, + boolean visitOverridingMembers, + MemberVisitor memberVisitor) + { + this.targetClass = targetClass; + this.visitThisMember = visitThisMember; + this.visitSuperMembers = visitSuperMembers; + this.visitInterfaceMembers = visitInterfaceMembers; + this.visitOverridingMembers = visitOverridingMembers; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedFieldVisitor(programField.getName(programClass), + programField.getDescriptor(programClass), + memberVisitor)); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedFieldVisitor(libraryField.getName(libraryClass), + libraryField.getDescriptor(libraryClass), + memberVisitor)); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedMethodVisitor(programMethod.getName(programClass), + programMethod.getDescriptor(programClass), + memberVisitor)); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedMethodVisitor(libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass), + memberVisitor)); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/SimpleClassPrinter.java b/src/proguard/classfile/visitor/SimpleClassPrinter.java new file mode 100644 index 0000000..a661110 --- /dev/null +++ b/src/proguard/classfile/visitor/SimpleClassPrinter.java @@ -0,0 +1,167 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; +import proguard.classfile.util.ClassUtil; + +import java.io.PrintStream; + + +/** + * This <code>ClassVisitor</code> and <code>MemberVisitor</code> + * prints out the class names of the classes it visits, and the full class + * member descriptions of the class members it visits. The names are printed + * in a readable, Java-like format. The access modifiers can be included or not. + * + * @author Eric Lafortune + */ +public class SimpleClassPrinter +implements ClassVisitor, + MemberVisitor +{ + private final boolean printAccessModifiers; + private final PrintStream ps; + + + /** + * Creates a new SimpleClassPrinter that prints to + * <code>System.out</code>, including the access modifiers. + */ + public SimpleClassPrinter() + { + this(true); + } + + /** + * Creates a new SimpleClassPrinter that prints to + * <code>System.out</code>, with or without the access modifiers. + */ + public SimpleClassPrinter(boolean printAccessModifiers) + { + this(printAccessModifiers, System.out); + } + + /** + * Creates a new SimpleClassPrinter that prints to the given + * <code>PrintStream</code>, with or without the access modifiers. + */ + public SimpleClassPrinter(boolean printAccessModifiers, + PrintStream printStream) + { + this.printAccessModifiers = printAccessModifiers; + this.ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + programClass.getAccessFlags() : + 0, + programClass.getName())); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + libraryClass.getAccessFlags() : + 0, + libraryClass.getName())); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + programClass.getAccessFlags() : + 0, + programClass.getName()) + + ": " + + ClassUtil.externalFullFieldDescription( + printAccessModifiers ? + programField.getAccessFlags() : + 0, + programField.getName(programClass), + programField.getDescriptor(programClass))); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + programClass.getAccessFlags() : + 0, + programClass.getName()) + + ": " + + ClassUtil.externalFullMethodDescription( + programClass.getName(), + printAccessModifiers ? + programMethod.getAccessFlags() : + 0, + programMethod.getName(programClass), + programMethod.getDescriptor(programClass))); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + libraryClass.getAccessFlags() : + 0, + libraryClass.getName()) + + ": " + + ClassUtil.externalFullFieldDescription( + printAccessModifiers ? + libraryField.getAccessFlags() : + 0, + libraryField.getName(libraryClass), + libraryField.getDescriptor(libraryClass))); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + ps.println(ClassUtil.externalFullClassDescription( + printAccessModifiers ? + libraryClass.getAccessFlags() : + 0, + libraryClass.getName()) + + ": " + + ClassUtil.externalFullMethodDescription( + libraryClass.getName(), + printAccessModifiers ? + libraryMethod.getAccessFlags() : + 0, + libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass))); + } +} diff --git a/src/proguard/classfile/visitor/SubclassFilter.java b/src/proguard/classfile/visitor/SubclassFilter.java new file mode 100644 index 0000000..69ea1a1 --- /dev/null +++ b/src/proguard/classfile/visitor/SubclassFilter.java @@ -0,0 +1,91 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + +/** + * This <code>ClassVisitor</code> delegates its visits to another given + * <code>ClassVisitor</code>, except for classes that have a given class as + * direct subclass. + * + * @author Eric Lafortune + */ +public class SubclassFilter implements ClassVisitor +{ + private final Clazz subclass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new SubclassFilter. + * @param subclass the class whose superclasses will not be visited. + * @param classVisitor the <code>ClassVisitor</code> to which visits will + * be delegated. + */ + public SubclassFilter(Clazz subclass, + ClassVisitor classVisitor) + { + this.subclass = subclass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!present(programClass.subClasses)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!present(libraryClass.subClasses)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean present(Clazz[] subclasses) + { + if (subclasses == null) + { + return false; + } + + for (int index = 0; index < subclasses.length; index++) + { + if (subclasses[index].equals(subclass)) + { + return true; + } + } + + return false; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/SubclassTraveler.java b/src/proguard/classfile/visitor/SubclassTraveler.java new file mode 100644 index 0000000..4170341 --- /dev/null +++ b/src/proguard/classfile/visitor/SubclassTraveler.java @@ -0,0 +1,60 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This <code>ClassVisitor</code> lets a given <code>ClassVisitor</code> + * travel to direct subclasses of the visited class. + * + * @author Eric Lafortune + */ +public class SubclassTraveler implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param classVisitor the <code>ClassVisitor</code> to + * which visits will be delegated. + */ + public SubclassTraveler(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.subclassesAccept(classVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.subclassesAccept(classVisitor); + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/visitor/VariableClassVisitor.java b/src/proguard/classfile/visitor/VariableClassVisitor.java new file mode 100644 index 0000000..2f575c4 --- /dev/null +++ b/src/proguard/classfile/visitor/VariableClassVisitor.java @@ -0,0 +1,78 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This ClassVisitor delegates all method calls to a ClassVisitor + * that can be changed at any time. + * + * @author Eric Lafortune + */ +public class VariableClassVisitor implements ClassVisitor +{ + private ClassVisitor classVisitor; + + + public VariableClassVisitor() + { + this(null); + } + + + public VariableClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + public void setClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + public ClassVisitor getClassVisitor() + { + return classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (classVisitor != null) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (classVisitor != null) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} diff --git a/src/proguard/classfile/visitor/VariableMemberVisitor.java b/src/proguard/classfile/visitor/VariableMemberVisitor.java new file mode 100644 index 0000000..c58cff3 --- /dev/null +++ b/src/proguard/classfile/visitor/VariableMemberVisitor.java @@ -0,0 +1,96 @@ +/* + * 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.classfile.visitor; + +import proguard.classfile.*; + + +/** + * This MemberVisitor delegates all method calls to a MemberVisitor + * that can be changed at any time. + * + * @author Eric Lafortune + */ +public class VariableMemberVisitor implements MemberVisitor +{ + private MemberVisitor memberVisitor; + + + public VariableMemberVisitor() + { + this(null); + } + + + public VariableMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + public void setMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + public MemberVisitor getMemberVisitor() + { + return memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (memberVisitor != null) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (memberVisitor != null) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (memberVisitor != null) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (memberVisitor != null) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +} diff --git a/src/proguard/classfile/visitor/package.html b/src/proguard/classfile/visitor/package.html new file mode 100644 index 0000000..d3be40c --- /dev/null +++ b/src/proguard/classfile/visitor/package.html @@ -0,0 +1,40 @@ +<body> +This package contains interfaces and classes for processing class files from +the <code>{@link proguard.classfile proguard.classfile}</code> package using +the <i>visitor pattern</i>. Cfr., for instance, "Design Patterns, Elements of +Reusable OO Software", by Gamma, Helm, Johnson, and Vlissider. +<p> +Why the visitor pattern? Class files frequently contain lists of elements of +various mixed types: class items, constant pool entries, attributes,... +These lists and types are largely fixed; they won't change much in future +releases of the Java class file specifications. On the other hand, the kinds +of operations that we may wish to perform on the class files may change and +expand. We want to separate the objects and the operations performed upon them. +This is a good place to use the visitor pattern. +<p> +Visitor interfaces avoid having to do series of <code>instanceof</code> tests +on the elements of a list, followed by type casts and the proper operations. +Every list element is a visitor accepter. When its <code>accept</code> method +is called by a visitor, it calls its corresponding <code>visitX</code> method +in the visitor, passing itself as an argument. This technique is called +double-dispatch. +<p> +As already mentioned, the main advantage is avoiding lots of +<code>instanceof</code> tests and type casts. Also, implementing a visitor +interface ensures you're handling all possible visitor accepter types. Each +type has its own method, which you simply have to implement. +<p> +A disadvantage is that the visitor methods always get the same names, specified +by the visitor interface. These names aren't descriptive at all, making code +harder to read. It's the visitor classes that describe the operations now. +<p> +Also, the visitor methods always have the same parameters and return values, as +specified by the visitor interfaces. Passing additional parameters is done by +means of extra fields in the visitor, which is somewhat of a kludge. +<p> +Because objects (the visitor accepters) and the operations performed upon them +(the visitors) are now separated, it becomes harder to associate some state +with the objects. For convenience, we always provide an extra <i>visitor +info</i> field in visitor accepters, in which visitors can put any temporary +information they want. +</body> |