aboutsummaryrefslogtreecommitdiffstats
path: root/src/proguard/classfile/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/classfile/util')
-rw-r--r--src/proguard/classfile/util/AccessUtil.java105
-rw-r--r--src/proguard/classfile/util/ClassReferenceInitializer.java545
-rw-r--r--src/proguard/classfile/util/ClassSubHierarchyInitializer.java77
-rw-r--r--src/proguard/classfile/util/ClassSuperHierarchyInitializer.java168
-rw-r--r--src/proguard/classfile/util/ClassUtil.java1154
-rw-r--r--src/proguard/classfile/util/DescriptorClassEnumeration.java236
-rw-r--r--src/proguard/classfile/util/DynamicClassReferenceInitializer.java478
-rw-r--r--src/proguard/classfile/util/DynamicMemberReferenceInitializer.java604
-rw-r--r--src/proguard/classfile/util/ExternalTypeEnumeration.java106
-rw-r--r--src/proguard/classfile/util/InstructionSequenceMatcher.java634
-rw-r--r--src/proguard/classfile/util/InternalTypeEnumeration.java204
-rw-r--r--src/proguard/classfile/util/MemberFinder.java197
-rw-r--r--src/proguard/classfile/util/MethodLinker.java165
-rw-r--r--src/proguard/classfile/util/SimplifiedVisitor.java810
-rw-r--r--src/proguard/classfile/util/StringReferenceInitializer.java89
-rw-r--r--src/proguard/classfile/util/StringSharer.java155
-rw-r--r--src/proguard/classfile/util/WarningPrinter.java136
-rw-r--r--src/proguard/classfile/util/package.html3
18 files changed, 5866 insertions, 0 deletions
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>&lt;init&gt;</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>&lt;init&gt;</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>