diff options
Diffstat (limited to 'src/proguard/obfuscate')
29 files changed, 4234 insertions, 0 deletions
diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/src/proguard/obfuscate/AttributeShrinker.java new file mode 100644 index 0000000..a8bc36b --- /dev/null +++ b/src/proguard/obfuscate/AttributeShrinker.java @@ -0,0 +1,121 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor removes attributes that are not marked as being used or + * required. + * + * @see AttributeUsageMarker + * + * @author Eric Lafortune + */ +public class AttributeShrinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Compact the array for class attributes. + programClass.u2attributesCount = + shrinkArray(programClass.attributes, + programClass.u2attributesCount); + + // Compact the attributes in fields, methods, and class attributes, + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Library classes are left unchanged. + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Compact the attributes array. + programMember.u2attributesCount = + shrinkArray(programMember.attributes, + programMember.u2attributesCount); + + // Compact any attributes of the remaining attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Compact the attributes array. + codeAttribute.u2attributesCount = + shrinkArray(codeAttribute.attributes, + codeAttribute.u2attributesCount); + } + + + // Small utility methods. + + /** + * Removes all VisitorAccepter objects that are not marked as being used + * from the given array. + * @return the new number of VisitorAccepter objects. + */ + private static int shrinkArray(VisitorAccepter[] array, int length) + { + int counter = 0; + + // Shift the used objects together. + for (int index = 0; index < length; index++) + { + if (AttributeUsageMarker.isUsed(array[index])) + { + array[counter++] = array[index]; + } + } + + // Clear the remaining array elements. + for (int index = counter; index < length; index++) + { + array[index] = null; + } + + return counter; + } +} diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/src/proguard/obfuscate/AttributeUsageMarker.java new file mode 100644 index 0000000..e772324 --- /dev/null +++ b/src/proguard/obfuscate/AttributeUsageMarker.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ClassVisitor marks all attributes that it visits. + * + * @see AttributeShrinker + * + * @author Eric Lafortune + */ +public class AttributeUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor +{ + // A visitor info flag to indicate the attribute is being used. + private static final Object USED = new Object(); + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + markAsUsed(attribute); + } + + + // Small utility methods. + + /** + * Marks the given VisitorAccepter as being used (or useful). + * In this context, the VisitorAccepter will be an Attribute object. + */ + private static void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be an Attribute object. + */ + static boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } +} diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/src/proguard/obfuscate/ClassObfuscator.java new file mode 100644 index 0000000..9af0c82 --- /dev/null +++ b/src/proguard/obfuscate/ClassObfuscator.java @@ -0,0 +1,521 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.ClassVisitor; +import proguard.util.*; + +import java.util.*; + +/** + * This <code>ClassVisitor</code> comes up with obfuscated names for the + * classes it visits, and for their class members. The actual renaming is + * done afterward. + * + * @see ClassRenamer + * + * @author Eric Lafortune + */ +public class ClassObfuscator +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + ConstantVisitor +{ + private final DictionaryNameFactory classNameFactory; + private final DictionaryNameFactory packageNameFactory; + private final boolean useMixedCaseClassNames; + private final StringMatcher keepPackageNamesMatcher; + private final String flattenPackageHierarchy; + private final String repackageClasses; + private final boolean allowAccessModification; + + private final Set classNamesToAvoid = new HashSet(); + + // Map: [package prefix - new package prefix] + private final Map packagePrefixMap = new HashMap(); + + // Map: [package prefix - package name factory] + private final Map packagePrefixPackageNameFactoryMap = new HashMap(); + + // Map: [package prefix - numeric class name factory] + private final Map packagePrefixClassNameFactoryMap = new HashMap(); + + // Map: [package prefix - numeric class name factory] + private final Map packagePrefixNumericClassNameFactoryMap = new HashMap(); + + // Field acting as temporary variables and as return values for names + // of outer classes and types of inner classes. + private String newClassName; + private boolean numericClassName; + + + /** + * Creates a new ClassObfuscator. + * @param programClassPool the class pool in which class names + * have to be unique. + * @param classNameFactory the optional class obfuscation dictionary. + * @param packageNameFactory the optional package obfuscation + * dictionary. + * @param useMixedCaseClassNames specifies whether obfuscated packages and + * classes can get mixed-case names. + * @param keepPackageNames the optional filter for which matching + * package names are kept. + * @param flattenPackageHierarchy the base package if the obfuscated package + * hierarchy is to be flattened. + * @param repackageClasses the base package if the obfuscated classes + * are to be repackaged. + * @param allowAccessModification specifies whether obfuscated classes can + * be freely moved between packages. + */ + public ClassObfuscator(ClassPool programClassPool, + DictionaryNameFactory classNameFactory, + DictionaryNameFactory packageNameFactory, + boolean useMixedCaseClassNames, + List keepPackageNames, + String flattenPackageHierarchy, + String repackageClasses, + boolean allowAccessModification) + { + this.classNameFactory = classNameFactory; + this.packageNameFactory = packageNameFactory; + + // First append the package separator if necessary. + if (flattenPackageHierarchy != null && + flattenPackageHierarchy.length() > 0) + { + flattenPackageHierarchy += ClassConstants.INTERNAL_PACKAGE_SEPARATOR; + } + + // First append the package separator if necessary. + if (repackageClasses != null && + repackageClasses.length() > 0) + { + repackageClasses += ClassConstants.INTERNAL_PACKAGE_SEPARATOR; + } + + this.useMixedCaseClassNames = useMixedCaseClassNames; + this.keepPackageNamesMatcher = keepPackageNames == null ? null : + new ListParser(new FileNameParser()).parse(keepPackageNames); + this.flattenPackageHierarchy = flattenPackageHierarchy; + this.repackageClasses = repackageClasses; + this.allowAccessModification = allowAccessModification; + + // Map the root package onto the root package. + packagePrefixMap.put("", ""); + + // Collect all names that have been taken already. + programClassPool.classesAccept(new MyKeepCollector()); + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Does this class still need a new name? + newClassName = newClassName(programClass); + if (newClassName == null) + { + // Make sure the outer class has a name, if it exists. The name will + // be stored as the new class name, as a side effect, so we'll be + // able to use it as a prefix. + programClass.attributesAccept(this); + + // Figure out a package prefix. The package prefix may actually be + // the an outer class prefix, if any, or it may be the fixed base + // package, if classes are to be repackaged. + String newPackagePrefix = newClassName != null ? + newClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR : + newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName())); + + // Come up with a new class name, numeric or ordinary. + newClassName = newClassName != null && numericClassName ? + generateUniqueNumericClassName(newPackagePrefix) : + generateUniqueClassName(newPackagePrefix); + + setNewClassName(programClass, newClassName); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Make sure the outer classes have a name, if they exist. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + // Make sure the enclosing class has a name. + enclosingMethodAttribute.referencedClassAccept(this); + + String innerClassName = clazz.getName(); + String outerClassName = clazz.getClassName(enclosingMethodAttribute.u2classIndex); + + numericClassName = isNumericClassName(innerClassName, + outerClassName); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // Make sure the outer class has a name, if it exists. + int innerClassIndex = innerClassesInfo.u2innerClassIndex; + int outerClassIndex = innerClassesInfo.u2outerClassIndex; + if (innerClassIndex != 0 && + outerClassIndex != 0) + { + String innerClassName = clazz.getClassName(innerClassIndex); + if (innerClassName.equals(clazz.getName())) + { + clazz.constantPoolEntryAccept(outerClassIndex, this); + + String outerClassName = clazz.getClassName(outerClassIndex); + + numericClassName = isNumericClassName(innerClassName, + outerClassName); + } + } + } + + + /** + * Returns whether the given inner class name is a numeric name. + */ + private boolean isNumericClassName(String innerClassName, + String outerClassName) + { + int innerClassNameStart = outerClassName.length() + 1; + int innerClassNameLength = innerClassName.length(); + + if (innerClassNameStart >= innerClassNameLength) + { + return false; + } + + for (int index = innerClassNameStart; index < innerClassNameLength; index++) + { + if (!Character.isDigit(innerClassName.charAt(index))) + { + return false; + } + } + + return true; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Make sure the outer class has a name. + classConstant.referencedClassAccept(this); + } + + + /** + * This ClassVisitor collects package names and class names that have to + * be kept. + */ + private class MyKeepCollector implements ClassVisitor + { + public void visitProgramClass(ProgramClass programClass) + { + // Does the class already have a new name? + String newClassName = newClassName(programClass); + if (newClassName != null) + { + // Remember not to use this name. + classNamesToAvoid.add(mixedCaseClassName(newClassName)); + + // Are we not aggressively repackaging all obfuscated classes? + if (repackageClasses == null || + !allowAccessModification) + { + String className = programClass.getName(); + + // Keep the package name for all other classes in the same + // package. Do this recursively if we're not doing any + // repackaging. + mapPackageName(className, + newClassName, + repackageClasses == null && + flattenPackageHierarchy == null); + } + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + /** + * Makes sure the package name of the given class will always be mapped + * consistently with its new name. + */ + private void mapPackageName(String className, + String newClassName, + boolean recursively) + { + String packagePrefix = ClassUtil.internalPackagePrefix(className); + String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName); + + // Put the mapping of this package prefix, and possibly of its + // entire hierarchy, into the package prefix map. + do + { + packagePrefixMap.put(packagePrefix, newPackagePrefix); + + if (!recursively) + { + break; + } + + packagePrefix = ClassUtil.internalPackagePrefix(packagePrefix); + newPackagePrefix = ClassUtil.internalPackagePrefix(newPackagePrefix); + } + while (packagePrefix.length() > 0 && + newPackagePrefix.length() > 0); + } + } + + + // Small utility methods. + + /** + * Finds or creates the new package prefix for the given package. + */ + private String newPackagePrefix(String packagePrefix) + { + // Doesn't the package prefix have a new package prefix yet? + String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix); + if (newPackagePrefix == null) + { + // Are we keeping the package name? + if (keepPackageNamesMatcher != null && + keepPackageNamesMatcher.matches(packagePrefix.length() > 0 ? + packagePrefix.substring(0, packagePrefix.length()-1) : + packagePrefix)) + { + return packagePrefix; + } + + // Are we forcing a new package prefix? + if (repackageClasses != null) + { + return repackageClasses; + } + + // Are we forcing a new superpackage prefix? + // Otherwise figure out the new superpackage prefix, recursively. + String newSuperPackagePrefix = flattenPackageHierarchy != null ? + flattenPackageHierarchy : + newPackagePrefix(ClassUtil.internalPackagePrefix(packagePrefix)); + + // Come up with a new package prefix. + newPackagePrefix = generateUniquePackagePrefix(newSuperPackagePrefix); + + // Remember to use this mapping in the future. + packagePrefixMap.put(packagePrefix, newPackagePrefix); + } + + return newPackagePrefix; + } + + + /** + * Creates a new package prefix in the given new superpackage. + */ + private String generateUniquePackagePrefix(String newSuperPackagePrefix) + { + // Find the right name factory for this package. + NameFactory packageNameFactory = + (NameFactory)packagePrefixPackageNameFactoryMap.get(newSuperPackagePrefix); + if (packageNameFactory == null) + { + // We haven't seen packages in this superpackage before. Create + // a new name factory for them. + packageNameFactory = new SimpleNameFactory(useMixedCaseClassNames); + if (this.packageNameFactory != null) + { + packageNameFactory = + new DictionaryNameFactory(this.packageNameFactory, + packageNameFactory); + } + + packagePrefixPackageNameFactoryMap.put(newSuperPackagePrefix, + packageNameFactory); + } + + return generateUniquePackagePrefix(newSuperPackagePrefix, packageNameFactory); + } + + + /** + * Creates a new package prefix in the given new superpackage, with the + * given package name factory. + */ + private String generateUniquePackagePrefix(String newSuperPackagePrefix, + NameFactory packageNameFactory) + { + // Come up with package names until we get an original one. + String newPackagePrefix; + do + { + // Let the factory produce a package name. + newPackagePrefix = newSuperPackagePrefix + + packageNameFactory.nextName() + + ClassConstants.INTERNAL_PACKAGE_SEPARATOR; + } + while (packagePrefixMap.containsValue(newPackagePrefix)); + + return newPackagePrefix; + } + + + /** + * Creates a new class name in the given new package. + */ + private String generateUniqueClassName(String newPackagePrefix) + { + // Find the right name factory for this package. + NameFactory classNameFactory = + (NameFactory)packagePrefixClassNameFactoryMap.get(newPackagePrefix); + if (classNameFactory == null) + { + // We haven't seen classes in this package before. + // Create a new name factory for them. + classNameFactory = new SimpleNameFactory(useMixedCaseClassNames); + if (this.classNameFactory != null) + { + classNameFactory = + new DictionaryNameFactory(this.classNameFactory, + classNameFactory); + } + + packagePrefixClassNameFactoryMap.put(newPackagePrefix, + classNameFactory); + } + + return generateUniqueClassName(newPackagePrefix, classNameFactory); + } + + + /** + * Creates a new class name in the given new package. + */ + private String generateUniqueNumericClassName(String newPackagePrefix) + { + // Find the right name factory for this package. + NameFactory classNameFactory = + (NameFactory)packagePrefixNumericClassNameFactoryMap.get(newPackagePrefix); + if (classNameFactory == null) + { + // We haven't seen classes in this package before. + // Create a new name factory for them. + classNameFactory = new NumericNameFactory(); + + packagePrefixNumericClassNameFactoryMap.put(newPackagePrefix, + classNameFactory); + } + + return generateUniqueClassName(newPackagePrefix, classNameFactory); + } + + + /** + * Creates a new class name in the given new package, with the given + * class name factory. + */ + private String generateUniqueClassName(String newPackagePrefix, + NameFactory classNameFactory) + { + // Come up with class names until we get an original one. + String newClassName; + do + { + // Let the factory produce a class name. + newClassName = newPackagePrefix + + classNameFactory.nextName(); + } + while (classNamesToAvoid.contains(mixedCaseClassName(newClassName))); + + return newClassName; + } + + + /** + * Returns the given class name, unchanged if mixed-case class names are + * allowed, or the lower-case version otherwise. + */ + private String mixedCaseClassName(String className) + { + return useMixedCaseClassNames ? + className : + className.toLowerCase(); + } + + + /** + * Assigns a new name to the given class. + * @param clazz the given class. + * @param name the new name. + */ + static void setNewClassName(Clazz clazz, String name) + { + clazz.setVisitorInfo(name); + } + + + /** + * Retrieves the new name of the given class. + * @param clazz the given class. + * @return the class's new name, or <code>null</code> if it doesn't + * have one yet. + */ + static String newClassName(Clazz clazz) + { + Object visitorInfo = clazz.getVisitorInfo(); + + return visitorInfo instanceof String ? + (String)visitorInfo : + null; + } +} diff --git a/src/proguard/obfuscate/ClassRenamer.java b/src/proguard/obfuscate/ClassRenamer.java new file mode 100644 index 0000000..143e3fb --- /dev/null +++ b/src/proguard/obfuscate/ClassRenamer.java @@ -0,0 +1,109 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This <code>ClassVisitor</code> renames the class names and class member + * names of the classes it visits, using names previously determined by the + * obfuscator. + * + * @see ClassObfuscator + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class ClassRenamer +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Rename this class. + programClass.thisClassConstantAccept(this); + + // Rename the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.thisClassName = ClassObfuscator.newClassName(libraryClass); + + // Rename the class members. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, + ProgramMember programMember) + { + // Has the class member name changed? + String name = programMember.getName(programClass); + String newName = MemberObfuscator.newMemberName(programMember); + if (newName != null && + !newName.equals(name)) + { + programMember.u2nameIndex = + new ConstantPoolEditor(programClass).addUtf8Constant(newName); + } + } + + public void visitLibraryMember(LibraryClass libraryClass, + LibraryMember libraryMember) + { + String newName = MemberObfuscator.newMemberName(libraryMember); + if (newName != null) + { + libraryMember.name = newName; + } + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Update the Class entry if required. + String newName = ClassObfuscator.newClassName(clazz); + if (newName != null) + { + // Refer to a new Utf8 entry. + classConstant.u2nameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); + } + } +} diff --git a/src/proguard/obfuscate/DictionaryNameFactory.java b/src/proguard/obfuscate/DictionaryNameFactory.java new file mode 100644 index 0000000..f262664 --- /dev/null +++ b/src/proguard/obfuscate/DictionaryNameFactory.java @@ -0,0 +1,189 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import java.io.*; +import java.util.*; + +/** + * This <code>NameFactory</code> generates names that are read from a + * specified input file. + * Comments (everything starting with '#' on a single line) are ignored. + * + * @author Eric Lafortune + */ +public class DictionaryNameFactory implements NameFactory +{ + private static final char COMMENT_CHARACTER = '#'; + + + private final List names; + private final NameFactory nameFactory; + + private int index = 0; + + + /** + * Creates a new <code>DictionaryNameFactory</code>. + * @param file the file from which the names can be read. + * @param nameFactory the name factory from which names will be retrieved + * if the list of read names has been exhausted. + */ + public DictionaryNameFactory(File file, + NameFactory nameFactory) throws IOException + { + this.names = new ArrayList(); + this.nameFactory = nameFactory; + + Reader reader = new FileReader(file); + + try + { + StringBuffer buffer = new StringBuffer(); + + while (true) + { + // Read the next character. + int c = reader.read(); + + // Is it a valid identifier character? + if (c != -1 && + (buffer.length() == 0 ? + Character.isJavaIdentifierStart((char)c) : + Character.isJavaIdentifierPart((char)c))) + { + // Append it to the current identifier. + buffer.append((char)c); + } + else + { + // Did we collect a new identifier? + if (buffer.length() > 0) + { + // Add the completed name to the list of names, if it's + // not in it yet. + String name = buffer.toString(); + if (!names.contains(name)) + { + names.add(name); + } + + // Clear the buffer. + buffer.setLength(0); + } + + // Is this the beginning of a comment line? + if (c == COMMENT_CHARACTER) + { + // Skip all characters till the end of the line. + do + { + c = reader.read(); + } + while (c != -1 && + c != '\n' && + c != '\r'); + } + + // Is this the end of the file? + if (c == -1) + { + // Just return. + return; + } + } + } + } + finally + { + reader.close(); + } + } + + + /** + * Creates a new <code>DictionaryNameFactory</code>. + * @param dictionaryNameFactory the dictionary name factory whose dictionary + * will be used. + * @param nameFactory the name factory from which names will be + * retrieved if the list of read names has been + * exhausted. + */ + public DictionaryNameFactory(DictionaryNameFactory dictionaryNameFactory, + NameFactory nameFactory) + { + this.names = dictionaryNameFactory.names; + this.nameFactory = nameFactory; + } + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + + nameFactory.reset(); + } + + + public String nextName() + { + String name; + + // Do we still have names? + if (index < names.size()) + { + // Return the next name. + name = (String)names.get(index++); + } + else + { + // Return the next different name from the other name factory. + do + { + name = nameFactory.nextName(); + } + while (names.contains(name)); + } + + return name; + } + + + public static void main(String[] args) + { + try + { + DictionaryNameFactory factory = + new DictionaryNameFactory(new File(args[0]), new SimpleNameFactory()); + + for (int counter = 0; counter < 50; counter++) + { + System.out.println("["+factory.nextName()+"]"); + } + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/obfuscate/MapCleaner.java b/src/proguard/obfuscate/MapCleaner.java new file mode 100644 index 0000000..fdefec5 --- /dev/null +++ b/src/proguard/obfuscate/MapCleaner.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Map; + +/** + * This ClassVisitor clears a given map whenever it visits a class. + * + * @author Eric Lafortune + */ +public class MapCleaner +extends SimplifiedVisitor +implements ClassVisitor +{ + private final Map map; + + + /** + * Creates a new MapCleaner. + * @param map the map to be cleared. + */ + public MapCleaner(Map map) + { + this.map = map; + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + map.clear(); + } +} diff --git a/src/proguard/obfuscate/MappingKeeper.java b/src/proguard/obfuscate/MappingKeeper.java new file mode 100644 index 0000000..c9d6aa6 --- /dev/null +++ b/src/proguard/obfuscate/MappingKeeper.java @@ -0,0 +1,177 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.util.ListUtil; + + +/** + * This MappingKeeper applies the mappings that it receives to its class pool, + * so these mappings are ensured in a subsequent obfuscation step. + * + * @author Eric Lafortune + */ +public class MappingKeeper implements MappingProcessor +{ + private final ClassPool classPool; + private final WarningPrinter warningPrinter; + + // A field acting as a parameter. + private Clazz clazz; + + + /** + * Creates a new MappingKeeper. + * @param classPool the class pool in which class names and class + * member names have to be mapped. + * @param warningPrinter the optional warning printer to which warnings + * can be printed. + */ + public MappingKeeper(ClassPool classPool, + WarningPrinter warningPrinter) + { + this.classPool = classPool; + this.warningPrinter = warningPrinter; + } + + + // Implementations for MappingProcessor. + + public boolean processClassMapping(String className, + String newClassName) + { + // Find the class. + String name = ClassUtil.internalClassName(className); + + clazz = classPool.getClass(name); + if (clazz != null) + { + String newName = ClassUtil.internalClassName(newClassName); + + // Print out a warning if the mapping conflicts with a name that + // was set before. + if (warningPrinter != null) + { + String currentNewName = ClassObfuscator.newClassName(clazz); + if (currentNewName != null && + !currentNewName.equals(newName)) + { + warningPrinter.print(name, + currentNewName, + "Warning: " + + className + + " is not being kept as '" + + ClassUtil.externalClassName(currentNewName) + + "', but remapped to '" + + newClassName + "'"); + } + } + + ClassObfuscator.setNewClassName(clazz, newName); + + // The class members have to be kept as well. + return true; + } + + return false; + } + + + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName) + { + if (clazz != null) + { + // Find the field. + String name = fieldName; + String descriptor = ClassUtil.internalType(fieldType); + + Field field = clazz.findField(name, descriptor); + if (field != null) + { + // Print out a warning if the mapping conflicts with a name that + // was set before. + if (warningPrinter != null) + { + String currentNewName = MemberObfuscator.newMemberName(field); + if (currentNewName != null && + !currentNewName.equals(newFieldName)) + { + warningPrinter.print(ClassUtil.internalClassName(className), + "Warning: " + + className + + ": field '" + fieldType + " " + fieldName + + "' is not being kept as '" + currentNewName + + "', but remapped to '" + newFieldName + "'"); + } + } + + // Make sure the mapping name will be kept. + MemberObfuscator.setFixedNewMemberName(field, newFieldName); + } + } + } + + + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName) + { + if (clazz != null) + { + // Find the method. + String descriptor = ClassUtil.internalMethodDescriptor(methodReturnType, + ListUtil.commaSeparatedList(methodArguments)); + + Method method = clazz.findMethod(methodName, descriptor); + if (method != null) + { + // Print out a warning if the mapping conflicts with a name that + // was set before. + if (warningPrinter != null) + { + String currentNewName = MemberObfuscator.newMemberName(method); + if (currentNewName != null && + !currentNewName.equals(newMethodName)) + { + warningPrinter.print(ClassUtil.internalClassName(className), + "Warning: " + + className + + ": method '" + methodReturnType + " " + methodName + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + methodArguments + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE + + "' is not being kept as '" + currentNewName + + "', but remapped to '" + newMethodName + "'"); + } + } + + // Make sure the mapping name will be kept. + MemberObfuscator.setFixedNewMemberName(method, newMethodName); + } + } + } +} diff --git a/src/proguard/obfuscate/MappingPrinter.java b/src/proguard/obfuscate/MappingPrinter.java new file mode 100644 index 0000000..aa8b13e --- /dev/null +++ b/src/proguard/obfuscate/MappingPrinter.java @@ -0,0 +1,147 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.PrintStream; + + +/** + * This ClassVisitor prints out the renamed classes and class members with + * their old names and new names. + * + * @see ClassRenamer + * + * @author Eric Lafortune + */ +public class MappingPrinter +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + private final PrintStream ps; + + + /** + * Creates a new MappingPrinter that prints to <code>System.out</code>. + */ + public MappingPrinter() + { + this(System.out); + } + + + /** + * Creates a new MappingPrinter that prints to the given stream. + * @param printStream the stream to which to print + */ + public MappingPrinter(PrintStream printStream) + { + this.ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + String name = programClass.getName(); + String newName = ClassObfuscator.newClassName(programClass); + + ps.println(ClassUtil.externalClassName(name) + + " -> " + + ClassUtil.externalClassName(newName) + + ":"); + + // Print out the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + String newName = MemberObfuscator.newMemberName(programField); + if (newName != null) + { + ps.println(" " + + //lineNumberRange(programClass, programField) + + ClassUtil.externalFullFieldDescription( + 0, + programField.getName(programClass), + programField.getDescriptor(programClass)) + + " -> " + + newName); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + String name = programMethod.getName(programClass); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + String newName = MemberObfuscator.newMemberName(programMethod); + if (newName != null) + { + ps.println(" " + + lineNumberRange(programClass, programMethod) + + ClassUtil.externalFullMethodDescription( + programClass.getName(), + 0, + programMethod.getName(programClass), + programMethod.getDescriptor(programClass)) + + " -> " + + newName); + } + } + + + // Small utility methods. + + /** + * Returns the line number range of the given class member, followed by a + * colon, or just an empty String if no range is available. + */ + private static String lineNumberRange(ProgramClass programClass, ProgramMember programMember) + { + String range = programMember.getLineNumberRange(programClass); + return range != null ? + (range + ":") : + ""; + } +} diff --git a/src/proguard/obfuscate/MappingProcessor.java b/src/proguard/obfuscate/MappingProcessor.java new file mode 100644 index 0000000..01c1809 --- /dev/null +++ b/src/proguard/obfuscate/MappingProcessor.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + + +/** + * This interface specifies methods to process name mappings between original + * classes and their obfuscated versions. The mappings are typically read + * from a mapping file. + * + * @see MappingReader + * + * @author Eric Lafortune + */ +public interface MappingProcessor +{ + /** + * Processes the given class name mapping. + * + * @param className the original class name. + * @param newClassName the new class name. + * @return whether the processor is interested in receiving mappings of the + * class members of this class. + */ + public boolean processClassMapping(String className, + String newClassName); + + /** + * Processes the given field name mapping. + * + * @param className the original class name. + * @param fieldType the original external field type. + * @param fieldName the original field name. + * @param newFieldName the new field name. + */ + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName); + + /** + * Processes the given method name mapping. + * + * @param className the original class name. + * @param firstLineNumber the first line number of the method, or 0 if it + * is not known. + * @param lastLineNumber the last line number of the method, or 0 if it + * is not known. + * @param methodReturnType the original external method return type. + * @param methodName the original external method name. + * @param methodArguments the original external method arguments. + * @param newMethodName the new method name. + */ + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName); +} diff --git a/src/proguard/obfuscate/MappingReader.java b/src/proguard/obfuscate/MappingReader.java new file mode 100644 index 0000000..24fd26c --- /dev/null +++ b/src/proguard/obfuscate/MappingReader.java @@ -0,0 +1,199 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import java.io.*; + + +/** + * This class can parse mapping files and invoke a processor for each of the + * mapping entries. + * + * @author Eric Lafortune + */ +public class MappingReader +{ + private final File mappingFile; + + + public MappingReader(File mappingFile) + { + this.mappingFile = mappingFile; + } + + + /** + * Reads the mapping file, presenting all of the encountered mapping entries + * to the given processor. + */ + public void pump(MappingProcessor mappingProcessor) throws IOException + { + LineNumberReader reader = new LineNumberReader( + new BufferedReader( + new FileReader(mappingFile))); + try + { + String className = null; + + // Read the subsequent class mappings and class member mappings. + while (true) + { + String line = reader.readLine(); + + if (line == null) + { + break; + } + + line = line.trim(); + + // The distinction between a class mapping and a class + // member mapping is the initial whitespace. + if (line.endsWith(":")) + { + // Process the class mapping and remember the class's + // old name. + className = processClassMapping(line, mappingProcessor); + } + else if (className != null) + { + // Process the class member mapping, in the context of the + // current old class name. + processClassMemberMapping(className, line, mappingProcessor); + } + } + } + catch (IOException ex) + { + throw new IOException("Can't process mapping file (" + ex.getMessage() + ")"); + } + finally + { + try + { + reader.close(); + } + catch (IOException ex) + { + // This shouldn't happen. + } + } + } + + + /** + * Parses the given line with a class mapping and processes the + * results with the given mapping processor. Returns the old class name, + * or null if any subsequent class member lines can be ignored. + */ + private String processClassMapping(String line, + MappingProcessor mappingProcessor) + { + // See if we can parse "___ -> ___:", containing the original + // class name and the new class name. + + int arrowIndex = line.indexOf("->"); + if (arrowIndex < 0) + { + return null; + } + + int colonIndex = line.indexOf(':', arrowIndex + 2); + if (colonIndex < 0) + { + return null; + } + + // Extract the elements. + String className = line.substring(0, arrowIndex).trim(); + String newClassName = line.substring(arrowIndex + 2, colonIndex).trim(); + + // Process this class name mapping. + boolean interested = mappingProcessor.processClassMapping(className, newClassName); + + return interested ? className : null; + } + + + /** + * Parses the given line with a class member mapping and processes the + * results with the given mapping processor. + */ + private void processClassMemberMapping(String className, + String line, + MappingProcessor mappingProcessor) + { + // See if we can parse "___:___:___ ___(___) -> ___", + // containing the optional line numbers, the return type, the original + // field/method name, optional arguments, and the new field/method name. + + int colonIndex1 = line.indexOf(':'); + int colonIndex2 = colonIndex1 < 0 ? -1 : line.indexOf(':', colonIndex1 + 1); + int spaceIndex = line.indexOf(' ', colonIndex2 + 2); + int argumentIndex1 = line.indexOf('(', spaceIndex + 1); + int argumentIndex2 = argumentIndex1 < 0 ? -1 : line.indexOf(')', argumentIndex1 + 1); + int arrowIndex = line.indexOf("->", Math.max(spaceIndex, argumentIndex2) + 1); + + if (spaceIndex < 0 || + arrowIndex < 0) + { + return; + } + + // Extract the elements. + String type = line.substring(colonIndex2 + 1, spaceIndex).trim(); + String name = line.substring(spaceIndex + 1, argumentIndex1 >= 0 ? argumentIndex1 : arrowIndex).trim(); + String newName = line.substring(arrowIndex + 2).trim(); + + // Process this class member mapping. + if (type.length() > 0 && + name.length() > 0 && + newName.length() > 0) + { + // Is it a field or a method? + if (argumentIndex2 < 0) + { + mappingProcessor.processFieldMapping(className, type, name, newName); + } + else + { + int firstLineNumber = 0; + int lastLineNumber = 0; + + if (colonIndex2 > 0) + { + firstLineNumber = Integer.parseInt(line.substring(0, colonIndex1).trim()); + lastLineNumber = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim()); + } + + String arguments = line.substring(argumentIndex1 + 1, argumentIndex2).trim(); + + mappingProcessor.processMethodMapping(className, + firstLineNumber, + lastLineNumber, + type, + name, + arguments, + newName); + } + } + } +} diff --git a/src/proguard/obfuscate/MemberNameCleaner.java b/src/proguard/obfuscate/MemberNameCleaner.java new file mode 100644 index 0000000..c41c59d --- /dev/null +++ b/src/proguard/obfuscate/MemberNameCleaner.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This <code>MemberVisitor</code> clears the new names of the class members + * that it visits. + * + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class MemberNameCleaner implements MemberVisitor +{ + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + MemberObfuscator.setNewMemberName(programField, null); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + MemberObfuscator.setNewMemberName(programMethod, null); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + MemberObfuscator.setNewMemberName(libraryField, null); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + MemberObfuscator.setNewMemberName(libraryMethod, null); + } +} diff --git a/src/proguard/obfuscate/MemberNameCollector.java b/src/proguard/obfuscate/MemberNameCollector.java new file mode 100644 index 0000000..c248820 --- /dev/null +++ b/src/proguard/obfuscate/MemberNameCollector.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.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; + +import java.util.Map; + +/** + * This MemberVisitor collects all new (obfuscation) names of the members + * that it visits. + * + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class MemberNameCollector +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean allowAggressiveOverloading; + private final Map descriptorMap; + + + /** + * Creates a new MemberNameCollector. + * @param allowAggressiveOverloading a flag that specifies whether class + * members can be overloaded aggressively. + * @param descriptorMap the map of descriptors to + * [new name - old name] maps. + */ + public MemberNameCollector(boolean allowAggressiveOverloading, + Map descriptorMap) + { + this.allowAggressiveOverloading = allowAggressiveOverloading; + this.descriptorMap = descriptorMap; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + String name = member.getName(clazz); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // Get the member's new name. + String newName = MemberObfuscator.newMemberName(member); + + // Remember it, if it has already been set. + if (newName != null) + { + // Get the member's descriptor. + String descriptor = member.getDescriptor(clazz); + + // Check whether we're allowed to do aggressive overloading + if (!allowAggressiveOverloading) + { + // Trim the return argument from the descriptor if not. + // Works for fields and methods alike. + descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); + } + + // Put the [descriptor - new name] in the map, + // creating a new [new name - old name] map if necessary. + Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor); + + // Isn't there another original name for this new name, or should + // this original name get priority? + String otherName = (String)nameMap.get(newName); + if (otherName == null || + MemberObfuscator.hasFixedNewMemberName(member) || + name.compareTo(otherName) < 0) + { + // Remember not to use the new name again in this name space. + nameMap.put(newName, name); + } + } + } +} diff --git a/src/proguard/obfuscate/MemberNameConflictFixer.java b/src/proguard/obfuscate/MemberNameConflictFixer.java new file mode 100644 index 0000000..b9093a6 --- /dev/null +++ b/src/proguard/obfuscate/MemberNameConflictFixer.java @@ -0,0 +1,159 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +import java.util.Map; + +/** + * This MemberInfoVisitor solves obfuscation naming conflicts in all class + * members that it visits. It avoids names from the given descriptor map, + * delegating to the given obfuscator in order to get a new name if necessary. + * + * @author Eric Lafortune + */ +public class MemberNameConflictFixer implements MemberVisitor +{ + private final boolean allowAggressiveOverloading; + private final Map descriptorMap; + private final WarningPrinter warningPrinter; + private final MemberObfuscator memberObfuscator; + + + /** + * Creates a new MemberNameConflictFixer. + * @param allowAggressiveOverloading a flag that specifies whether class + * members can be overloaded aggressively. + * @param descriptorMap the map of descriptors to + * [new name - old name] maps. + * @param warningPrinter an optional warning printer to which + * warnings about conflicting name + * mappings can be printed. + * @param memberObfuscator the obfuscator that can assign new + * names to members with conflicting + * names. + */ + public MemberNameConflictFixer(boolean allowAggressiveOverloading, + Map descriptorMap, + WarningPrinter warningPrinter, + MemberObfuscator memberObfuscator) + { + this.allowAggressiveOverloading = allowAggressiveOverloading; + this.descriptorMap = descriptorMap; + this.warningPrinter = warningPrinter; + this.memberObfuscator = memberObfuscator; + } + + + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + visitMember(programClass, programField, true); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + String name = programMethod.getName(programClass); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + visitMember(programClass, programMethod, false); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + + /** + * Obfuscates the given class member. + * @param clazz the class of the given member. + * @param member the class member to be obfuscated. + * @param isField specifies whether the class member is a field. + */ + private void visitMember(Clazz clazz, + Member member, + boolean isField) + { + // Get the member's name and descriptor. + String name = member.getName(clazz); + String descriptor = member.getDescriptor(clazz); + + // Check whether we're allowed to overload aggressively. + if (!allowAggressiveOverloading) + { + // Trim the return argument from the descriptor if not. + // Works for fields and methods alike. + descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); + } + + // Get the name map. + Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor); + + // Get the member's new name. + String newName = MemberObfuscator.newMemberName(member); + + // Get the expected old name for this new name. + String previousName = (String)nameMap.get(newName); + if (previousName != null && + !name.equals(previousName)) + { + // There's a conflict! A member (with a given old name) in a + // first namespace has received the same new name as this + // member (with a different old name) in a second name space, + // and now these two have to live together in this name space. + if (MemberObfuscator.hasFixedNewMemberName(member) && + warningPrinter != null) + { + descriptor = member.getDescriptor(clazz); + warningPrinter.print(clazz.getName(), + "Warning: " + ClassUtil.externalClassName(clazz.getName()) + + (isField ? + ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) : + ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) + + "' can't be mapped to '" + newName + + "' because it would conflict with " + + (isField ? + "field '" : + "method '" ) + previousName + + "', which is already being mapped to '" + newName + "'"); + } + + // Clear the conflicting name. + MemberObfuscator.setNewMemberName(member, null); + + // Assign a new name. + member.accept(clazz, memberObfuscator); + } + } +} diff --git a/src/proguard/obfuscate/MemberObfuscator.java b/src/proguard/obfuscate/MemberObfuscator.java new file mode 100644 index 0000000..332b849 --- /dev/null +++ b/src/proguard/obfuscate/MemberObfuscator.java @@ -0,0 +1,230 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +import java.util.*; + +/** + * This MemberVisitor obfuscates all class members that it visits. + * It uses names from the given name factory. At the same time, it avoids names + * from the given descriptor map. + * <p> + * The class members must have been linked before applying this visitor. + * + * @see MethodLinker + * + * @author Eric Lafortune + */ +public class MemberObfuscator +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean allowAggressiveOverloading; + private final NameFactory nameFactory; + private final Map descriptorMap; + + + /** + * Creates a new MemberObfuscator. + * @param allowAggressiveOverloading a flag that specifies whether class + * members can be overloaded aggressively. + * @param nameFactory the factory that can produce + * obfuscated member names. + * @param descriptorMap the map of descriptors to + * [new name - old name] maps. + */ + public MemberObfuscator(boolean allowAggressiveOverloading, + NameFactory nameFactory, + Map descriptorMap) + { + this.allowAggressiveOverloading = allowAggressiveOverloading; + this.nameFactory = nameFactory; + this.descriptorMap = descriptorMap; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + String name = member.getName(clazz); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // Get the member's descriptor. + String descriptor = member.getDescriptor(clazz); + + // Check whether we're allowed to do aggressive overloading + if (!allowAggressiveOverloading) + { + // Trim the return argument from the descriptor if not. + // Works for fields and methods alike. + descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); + } + + // Get the name map, creating a new one if necessary. + Map nameMap = retrieveNameMap(descriptorMap, descriptor); + + // Get the member's new name. + String newName = newMemberName(member); + + // Assign a new one, if necessary. + if (newName == null) + { + // Find an acceptable new name. + nameFactory.reset(); + + do + { + newName = nameFactory.nextName(); + } + while (nameMap.containsKey(newName)); + + // Remember not to use the new name again in this name space. + nameMap.put(newName, name); + + // Assign the new name. + setNewMemberName(member, newName); + } + } + + + // Small utility methods. + + /** + * Gets the name map, based on the given map and a given descriptor. + * A new empty map is created if necessary. + * @param descriptorMap the map of descriptors to [new name - old name] maps. + * @param descriptor the class member descriptor. + * @return the corresponding name map. + */ + static Map retrieveNameMap(Map descriptorMap, String descriptor) + { + // See if we can find the nested map with this descriptor key. + Map nameMap = (Map)descriptorMap.get(descriptor); + + // Create a new one if not. + if (nameMap == null) + { + nameMap = new HashMap(); + descriptorMap.put(descriptor, nameMap); + } + + return nameMap; + } + + + /** + * Assigns a fixed new name to the given class member. + * @param member the class member. + * @param name the new name. + */ + static void setFixedNewMemberName(Member member, String name) + { + VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member); + + if (!(lastVisitorAccepter instanceof LibraryMember) && + !(lastVisitorAccepter instanceof MyFixedName)) + { + lastVisitorAccepter.setVisitorInfo(new MyFixedName(name)); + } + else + { + lastVisitorAccepter.setVisitorInfo(name); + } + } + + + /** + * Assigns a new name to the given class member. + * @param member the class member. + * @param name the new name. + */ + static void setNewMemberName(Member member, String name) + { + MethodLinker.lastVisitorAccepter(member).setVisitorInfo(name); + } + + + /** + * Returns whether the new name of the given class member is fixed. + * @param member the class member. + * @return whether its new name is fixed. + */ + static boolean hasFixedNewMemberName(Member member) + { + VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member); + + return lastVisitorAccepter instanceof LibraryMember || + lastVisitorAccepter instanceof MyFixedName; + } + + + /** + * Retrieves the new name of the given class member. + * @param member the class member. + * @return the class member's new name, or <code>null</code> if it doesn't + * have one yet. + */ + static String newMemberName(Member member) + { + return (String)MethodLinker.lastVisitorAccepter(member).getVisitorInfo(); + } + + + /** + * This VisitorAccepter can be used to wrap a name string, to indicate that + * the name is fixed. + */ + private static class MyFixedName implements VisitorAccepter + { + private String newName; + + + public MyFixedName(String newName) + { + this.newName = newName; + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return newName; + } + + + public void setVisitorInfo(Object visitorInfo) + { + newName = (String)visitorInfo; + } + } +} diff --git a/src/proguard/obfuscate/MemberSpecialNameFilter.java b/src/proguard/obfuscate/MemberSpecialNameFilter.java new file mode 100644 index 0000000..f83374b --- /dev/null +++ b/src/proguard/obfuscate/MemberSpecialNameFilter.java @@ -0,0 +1,101 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This <code>MemberVisitor</code> delegates its visits to another given + * <code>MemberVisitor</code>, but only when the visited member has a + * special new name. A special name is a name that might have been produced by + * a <code>SpecialNameFactory</code>. + * + * @see MemberObfuscator + * @see SpecialNameFactory + * + * @author Eric Lafortune + */ +public class MemberSpecialNameFilter implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberSpecialNameFilter. + * @param memberVisitor the <code>MemberVisitor</code> to which + * visits will be delegated. + */ + public MemberSpecialNameFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (isSpecialName(programField)) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (isSpecialName(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (isSpecialName(libraryField)) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (isSpecialName(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + /** + * Returns whether the given class member has a special new name. + * @param member the class member. + */ + private static boolean isSpecialName(Member member) + { + return SpecialNameFactory.isSpecialName(MemberObfuscator.newMemberName(member)); + } +} diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/src/proguard/obfuscate/MultiMappingProcessor.java new file mode 100644 index 0000000..4074ff8 --- /dev/null +++ b/src/proguard/obfuscate/MultiMappingProcessor.java @@ -0,0 +1,96 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +/** + * This MappingKeeper delegates all method calls to each MappingProcessor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiMappingProcessor implements MappingProcessor +{ + private final MappingProcessor[] mappingProcessors; + + + /** + * Creates a new MultiMappingProcessor. + * @param mappingProcessors the mapping processors to which method calls + * will be delegated. + */ + public MultiMappingProcessor(MappingProcessor[] mappingProcessors) + { + this.mappingProcessors = mappingProcessors; + } + + + // Implementations for MappingProcessor. + + public boolean processClassMapping(String className, + String newClassName) + { + boolean result = false; + + for (int index = 0; index < mappingProcessors.length; index++) + { + result |= mappingProcessors[index].processClassMapping(className, + newClassName); + } + + return result; + } + + + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName) + { + for (int index = 0; index < mappingProcessors.length; index++) + { + mappingProcessors[index].processFieldMapping(className, + fieldType, + fieldName, + newFieldName); + } + } + + + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName) + { + for (int index = 0; index < mappingProcessors.length; index++) + { + mappingProcessors[index].processMethodMapping(className, + firstLineNumber, + lastLineNumber, + methodReturnType, + methodName, + methodArguments, + newMethodName); + } + } +} diff --git a/src/proguard/obfuscate/NameAndTypeShrinker.java b/src/proguard/obfuscate/NameAndTypeShrinker.java new file mode 100644 index 0000000..1284c82 --- /dev/null +++ b/src/proguard/obfuscate/NameAndTypeShrinker.java @@ -0,0 +1,112 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.ConstantPoolRemapper; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This ClassVisitor removes NameAndType constant pool entries + * that are not marked as being used. + * + * @see NameAndTypeUsageMarker + * + * @author Eric Lafortune + */ +public class NameAndTypeShrinker implements ClassVisitor +{ + private int[] constantIndexMap; + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Shift the used constant pool entries together, filling out the + // index map. + programClass.u2constantPoolCount = + shrinkConstantPool(programClass.constantPool, + programClass.u2constantPoolCount); + + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Small utility methods. + + /** + * Removes all NameAndType entries that are not marked as being used + * from the given constant pool. + * @return the new number of entries. + */ + private int shrinkConstantPool(Constant[] constantPool, int length) + { + // Create a new index map, if necessary. + if (constantIndexMap == null || + constantIndexMap.length < length) + { + constantIndexMap = new int[length]; + } + + int counter = 1; + boolean isUsed = false; + + // Shift the used constant pool entries together. + for (int index = 1; index < length; index++) + { + constantIndexMap[index] = counter; + + Constant constant = constantPool[index]; + + // Don't update the flag if this is the second half of a long entry. + if (constant != null) + { + isUsed = constant.getTag() != ClassConstants.CONSTANT_NameAndType || + NameAndTypeUsageMarker.isUsed(constant); + } + + if (isUsed) + { + constantPool[counter++] = constant; + } + } + + // Clear the remaining constant pool elements. + for (int index = counter; index < length; index++) + { + constantPool[index] = null; + } + + return counter; + } +} diff --git a/src/proguard/obfuscate/NameAndTypeUsageMarker.java b/src/proguard/obfuscate/NameAndTypeUsageMarker.java new file mode 100644 index 0000000..cc779f0 --- /dev/null +++ b/src/proguard/obfuscate/NameAndTypeUsageMarker.java @@ -0,0 +1,135 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor marks all NameAndType constant pool entries that are + * being used in the program classes it visits. + * + * @see NameAndTypeShrinker + * + * @author Eric Lafortune + */ +public class NameAndTypeUsageMarker +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + AttributeVisitor +{ + // A visitor info flag to indicate the NameAndType constant pool entry is being used. + private static final Object USED = new Object(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Mark the NameAndType entries referenced by all other constant pool + // entries. + programClass.constantPoolEntriesAccept(this); + + // Mark the NameAndType entries referenced by all EnclosingMethod + // attributes. + programClass.attributesAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + visitRefConstant(clazz, fieldrefConstant); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + visitRefConstant(clazz, interfaceMethodrefConstant); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + visitRefConstant(clazz, methodrefConstant); + } + + + private void visitRefConstant(Clazz clazz, RefConstant refConstant) + { + markNameAndTypeConstant(clazz, refConstant.u2nameAndTypeIndex); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + markNameAndTypeConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex); + } + } + + + // Small utility methods. + + /** + * Marks the given UTF-8 constant pool entry of the given class. + */ + private void markNameAndTypeConstant(Clazz clazz, int index) + { + markAsUsed((NameAndTypeConstant)((ProgramClass)clazz).getConstant(index)); + } + + + /** + * Marks the given VisitorAccepter as being used. + * In this context, the VisitorAccepter will be a NameAndTypeConstant object. + */ + private static void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be a NameAndTypeConstant object. + */ + static boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } +} diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NameFactory.java new file mode 100644 index 0000000..c64d1ad --- /dev/null +++ b/src/proguard/obfuscate/NameFactory.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +/** + * This interfaces provides methods to generate unique sequences of names. + * The names must be valid Java identifiers. + * + * @author Eric Lafortune + */ +public interface NameFactory +{ + public void reset(); + + public String nextName(); +} diff --git a/src/proguard/obfuscate/NameFactoryResetter.java b/src/proguard/obfuscate/NameFactoryResetter.java new file mode 100644 index 0000000..b6ba6ad --- /dev/null +++ b/src/proguard/obfuscate/NameFactoryResetter.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor resets a given name factory whenever it visits a class + * file. + * + * @author Eric Lafortune + */ +public class NameFactoryResetter implements ClassVisitor +{ + private final NameFactory nameFactory; + + + /** + * Creates a new NameFactoryResetter. + * @param nameFactory the name factory to be reset. + */ + public NameFactoryResetter(NameFactory nameFactory) + { + this.nameFactory = nameFactory; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + nameFactory.reset(); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + nameFactory.reset(); + } +} diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java new file mode 100644 index 0000000..2ce0ee9 --- /dev/null +++ b/src/proguard/obfuscate/NameMarker.java @@ -0,0 +1,166 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.visitor.*; + + +/** + * This <code>ClassVisitor</code> and <code>MemberVisitor</code> + * marks names of the classes and class members it visits. The marked names + * will remain unchanged in the obfuscation step. + * + * @see ClassObfuscator + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +class NameMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + ConstantVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + keepClassName(programClass); + + // Make sure any outer class names are kept as well. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + keepClassName(libraryClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + keepFieldName(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + keepMethodName(programClass, programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + keepFieldName(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + keepMethodName(libraryClass, libraryMethod); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Make sure the outer class names are kept as well. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // Make sure the outer class name is kept as well. + int innerClassIndex = innerClassesInfo.u2innerClassIndex; + int outerClassIndex = innerClassesInfo.u2outerClassIndex; + if (innerClassIndex != 0 && + outerClassIndex != 0 && + clazz.getClassName(innerClassIndex).equals(clazz.getName())) + { + clazz.constantPoolEntryAccept(outerClassIndex, this); + } + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Make sure the outer class name is kept as well. + classConstant.referencedClassAccept(this); + } + + + // Small utility method. + + /** + * Ensures the name of the given class name will be kept. + */ + public void keepClassName(Clazz clazz) + { + ClassObfuscator.setNewClassName(clazz, + clazz.getName()); + } + + + /** + * Ensures the name of the given field name will be kept. + */ + private void keepFieldName(Clazz clazz, Field field) + { + MemberObfuscator.setFixedNewMemberName(field, + field.getName(clazz)); + } + + + /** + * Ensures the name of the given method name will be kept. + */ + private void keepMethodName(Clazz clazz, Method method) + { + String name = method.getName(clazz); + + if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) && + !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + MemberObfuscator.setFixedNewMemberName(method, + method.getName(clazz)); + } + } +} diff --git a/src/proguard/obfuscate/NumericNameFactory.java b/src/proguard/obfuscate/NumericNameFactory.java new file mode 100644 index 0000000..cc21c4b --- /dev/null +++ b/src/proguard/obfuscate/NumericNameFactory.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import java.util.*; + + +/** + * This <code>NameFactory</code> generates unique numeric names, starting at + * "1". + * + * @author Eric Lafortune + */ +public class NumericNameFactory implements NameFactory +{ + private int index; + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + } + + + public String nextName() + { + return Integer.toString(++index); + } +}
\ No newline at end of file diff --git a/src/proguard/obfuscate/Obfuscator.java b/src/proguard/obfuscate/Obfuscator.java new file mode 100644 index 0000000..dce563a --- /dev/null +++ b/src/proguard/obfuscate/Obfuscator.java @@ -0,0 +1,429 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.visitor.AllConstantVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.util.*; + +import java.io.*; +import java.util.*; + +/** + * This class can perform obfuscation of class pools according to a given + * specification. + * + * @author Eric Lafortune + */ +public class Obfuscator +{ + private final Configuration configuration; + + + /** + * Creates a new Obfuscator. + */ + public Obfuscator(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Performs obfuscation of the given program class pool. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + // Check if we have at least some keep commands. + if (configuration.keep == null && + configuration.applyMapping == null && + configuration.printMapping == null) + { + throw new IOException("You have to specify '-keep' options for the obfuscation step."); + } + + // Clean up any old visitor info. + programClassPool.classesAccept(new ClassCleaner()); + libraryClassPool.classesAccept(new ClassCleaner()); + + // If the class member names have to correspond globally, + // link all class members in all classes, otherwise + // link all non-private methods in all class hierarchies. + ClassVisitor memberInfoLinker = + configuration.useUniqueClassMemberNames ? + (ClassVisitor)new AllMemberVisitor(new MethodLinker()) : + (ClassVisitor)new BottomClassFilter(new MethodLinker()); + + programClassPool.classesAccept(memberInfoLinker); + libraryClassPool.classesAccept(memberInfoLinker); + + // Create a visitor for marking the seeds. + NameMarker nameMarker = new NameMarker(); + ClassPoolVisitor classPoolvisitor = + ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, + nameMarker, + nameMarker, + false, + false, + true); + // Mark the seeds. + programClassPool.accept(classPoolvisitor); + libraryClassPool.accept(classPoolvisitor); + + // All library classes and library class members keep their names. + libraryClassPool.classesAccept(nameMarker); + libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker)); + + // Mark attributes that have to be kept. + AttributeUsageMarker requiredAttributeUsageMarker = + new AttributeUsageMarker(); + + AttributeVisitor optionalAttributeUsageMarker = + configuration.keepAttributes == null ? null : + new AttributeNameFilter(new ListParser(new NameParser()).parse(configuration.keepAttributes), + requiredAttributeUsageMarker); + + programClassPool.classesAccept( + new AllAttributeVisitor(true, + new RequiredAttributeFilter(requiredAttributeUsageMarker, + optionalAttributeUsageMarker))); + + // Remove the attributes that can be discarded. Note that the attributes + // may only be discarded after the seeds have been marked, since the + // configuration may rely on annotations. + programClassPool.classesAccept(new AttributeShrinker()); + + // Apply the mapping, if one has been specified. The mapping can + // override the names of library classes and of library class members. + if (configuration.applyMapping != null) + { + WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); + + MappingReader reader = new MappingReader(configuration.applyMapping); + + MappingProcessor keeper = + new MultiMappingProcessor(new MappingProcessor[] + { + new MappingKeeper(programClassPool, warningPrinter), + new MappingKeeper(libraryClassPool, null), + }); + + reader.pump(keeper); + + // Print out a summary of the warnings if necessary. + int mappingWarningCount = warningPrinter.getWarningCount(); + if (mappingWarningCount > 0) + { + System.err.println("Warning: there were " + mappingWarningCount + + " kept classes and class members that were remapped anyway."); + System.err.println(" You should adapt your configuration or edit the mapping file."); + + if (!configuration.ignoreWarnings) + { + System.err.println(" If you are sure this remapping won't hurt,"); + System.err.println(" you could try your luck using the '-ignorewarnings' option."); + throw new IOException("Please correct the above warnings first."); + } + } + } + + // Come up with new names for all classes. + DictionaryNameFactory classNameFactory = configuration.classObfuscationDictionary != null ? + new DictionaryNameFactory(configuration.classObfuscationDictionary, null) : + null; + + DictionaryNameFactory packageNameFactory = configuration.packageObfuscationDictionary != null ? + new DictionaryNameFactory(configuration.packageObfuscationDictionary, null) : + null; + + programClassPool.classesAccept( + new ClassObfuscator(programClassPool, + classNameFactory, + packageNameFactory, + configuration.useMixedCaseClassNames, + configuration.keepPackageNames, + configuration.flattenPackageHierarchy, + configuration.repackageClasses, + configuration.allowAccessModification)); + + // Come up with new names for all class members. + NameFactory nameFactory = new SimpleNameFactory(); + + if (configuration.obfuscationDictionary != null) + { + nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, + nameFactory); + } + + WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); + + // Maintain a map of names to avoid [descriptor - new name - old name]. + Map descriptorMap = new HashMap(); + + // Do the class member names have to be globally unique? + if (configuration.useUniqueClassMemberNames) + { + // Collect all member names in all classes. + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap))); + + // Assign new names to all members in all classes. + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberObfuscator(configuration.overloadAggressively, + nameFactory, + descriptorMap))); + } + else + { + // Come up with new names for all non-private class members. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all private member names in this class and down + // the hierarchy. + new ClassHierarchyTraveler(true, false, false, true, + new AllMemberVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Collect all non-private member names anywhere in the hierarchy. + new ClassHierarchyTraveler(true, true, true, true, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Assign new names to all non-private members in this class. + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberObfuscator(configuration.overloadAggressively, + nameFactory, + descriptorMap))), + + // Clear the collected names. + new MapCleaner(descriptorMap) + })); + + // Come up with new names for all private class members. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all member names in this class. + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)), + + // Collect all non-private member names higher up the hierarchy. + new ClassHierarchyTraveler(false, true, true, false, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Assign new names to all private members in this class. + new AllMemberVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberObfuscator(configuration.overloadAggressively, + nameFactory, + descriptorMap))), + + // Clear the collected names. + new MapCleaner(descriptorMap) + })); + } + + // Some class members may have ended up with conflicting names. + // Come up with new, globally unique names for them. + NameFactory specialNameFactory = + new SpecialNameFactory(new SimpleNameFactory()); + + // Collect a map of special names to avoid + // [descriptor - new name - old name]. + Map specialDescriptorMap = new HashMap(); + + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberSpecialNameFilter( + new MemberNameCollector(configuration.overloadAggressively, + specialDescriptorMap)))); + + libraryClassPool.classesAccept( + new AllMemberVisitor( + new MemberSpecialNameFilter( + new MemberNameCollector(configuration.overloadAggressively, + specialDescriptorMap)))); + + // Replace conflicting non-private member names with special names. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all private member names in this class and down + // the hierarchy. + new ClassHierarchyTraveler(true, false, false, true, + new AllMemberVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Collect all non-private member names in this class and + // higher up the hierarchy. + new ClassHierarchyTraveler(true, true, true, false, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Assign new names to all conflicting non-private members + // in this class and higher up the hierarchy. + new ClassHierarchyTraveler(true, true, true, false, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameConflictFixer(configuration.overloadAggressively, + descriptorMap, + warningPrinter, + new MemberObfuscator(configuration.overloadAggressively, + specialNameFactory, + specialDescriptorMap))))), + + // Clear the collected names. + new MapCleaner(descriptorMap) + })); + + // Replace conflicting private member names with special names. + // This is only possible if those names were kept or mapped. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all member names in this class. + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)), + + // Collect all non-private member names higher up the hierarchy. + new ClassHierarchyTraveler(false, true, true, false, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Assign new names to all conflicting private members in this + // class. + new AllMemberVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberNameConflictFixer(configuration.overloadAggressively, + descriptorMap, + warningPrinter, + new MemberObfuscator(configuration.overloadAggressively, + specialNameFactory, + specialDescriptorMap)))), + + // Clear the collected names. + new MapCleaner(descriptorMap) + })); + + // Print out any warnings about member name conflicts. + int warningCount = warningPrinter.getWarningCount(); + if (warningCount > 0) + { + System.err.println("Warning: there were " + warningCount + + " conflicting class member name mappings."); + System.err.println(" Your configuration may be inconsistent."); + + if (!configuration.ignoreWarnings) + { + System.err.println(" If you are sure the conflicts are harmless,"); + System.err.println(" you could try your luck using the '-ignorewarnings' option."); + throw new IOException("Please correct the above warnings first."); + } + } + + // Print out the mapping, if requested. + if (configuration.printMapping != null) + { + PrintStream ps = isFile(configuration.printMapping) ? + new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printMapping))) : + System.out; + + // Print out items that will be removed. + programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps)); + + if (ps != System.out) + { + ps.close(); + } + } + + // Actually apply the new names. + programClassPool.classesAccept(new ClassRenamer()); + libraryClassPool.classesAccept(new ClassRenamer()); + + // Update all references to these new names. + programClassPool.classesAccept(new ClassReferenceFixer(false)); + libraryClassPool.classesAccept(new ClassReferenceFixer(false)); + programClassPool.classesAccept(new MemberReferenceFixer()); + + // Make package visible elements public or protected, if obfuscated + // classes are being repackaged aggressively. + if (configuration.repackageClasses != null && + configuration.allowAccessModification) + { + programClassPool.classesAccept( + new AllConstantVisitor( + new AccessFixer())); + } + + // Rename the source file attributes, if requested. + if (configuration.newSourceFileAttribute != null) + { + programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute)); + } + + // Mark NameAndType constant pool entries that have to be kept + // and remove the other ones. + programClassPool.classesAccept(new NameAndTypeUsageMarker()); + programClassPool.classesAccept(new NameAndTypeShrinker()); + + // Mark Utf8 constant pool entries that have to be kept + // and remove the other ones. + programClassPool.classesAccept(new Utf8UsageMarker()); + programClassPool.classesAccept(new Utf8Shrinker()); + } + + + /** + * Returns whether the given file is actually a file, or just a placeholder + * for the standard output. + */ + private boolean isFile(File file) + { + return file.getPath().length() > 0; + } +} diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/src/proguard/obfuscate/SimpleNameFactory.java new file mode 100644 index 0000000..bce22de --- /dev/null +++ b/src/proguard/obfuscate/SimpleNameFactory.java @@ -0,0 +1,156 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import java.util.*; + + +/** + * This <code>NameFactory</code> generates unique short names, using mixed-case + * characters or lower-case characters only. + * + * @author Eric Lafortune + */ +public class SimpleNameFactory implements NameFactory +{ + private static final int CHARACTER_COUNT = 26; + + private static final List cachedMixedCaseNames = new ArrayList(); + private static final List cachedLowerCaseNames = new ArrayList(); + + private final boolean generateMixedCaseNames; + private int index = 0; + + + /** + * Creates a new <code>SimpleNameFactory</code> that generates mixed-case names. + */ + public SimpleNameFactory() + { + this(true); + } + + + /** + * Creates a new <code>SimpleNameFactory</code>. + * @param generateMixedCaseNames a flag to indicate whether the generated + * names will be mixed-case, or lower-case only. + */ + public SimpleNameFactory(boolean generateMixedCaseNames) + { + this.generateMixedCaseNames = generateMixedCaseNames; + } + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + } + + + public String nextName() + { + return name(index++); + } + + + /** + * Returns the name at the given index. + */ + private String name(int index) + { + // Which cache do we need? + List cachedNames = generateMixedCaseNames ? + cachedMixedCaseNames : + cachedLowerCaseNames; + + // Do we have the name in the cache? + if (index < cachedNames.size()) + { + return (String)cachedNames.get(index); + } + + // Create a new name and cache it. + String name = newName(index); + cachedNames.add(index, name); + + return name; + } + + + /** + * Creates and returns the name at the given index. + */ + private String newName(int index) + { + // If we're allowed to generate mixed-case names, we can use twice as + // many characters. + int totalCharacterCount = generateMixedCaseNames ? + 2 * CHARACTER_COUNT : + CHARACTER_COUNT; + + int baseIndex = index / totalCharacterCount; + int offset = index % totalCharacterCount; + + char newChar = charAt(offset); + + String newName = baseIndex == 0 ? + new String(new char[] { newChar }) : + (name(baseIndex-1) + newChar); + + return newName; + } + + + /** + * Returns the character with the given index, between 0 and the number of + * acceptable characters. + */ + private char charAt(int index) + { + return (char)((index < CHARACTER_COUNT ? 'a' - 0 : + 'A' - CHARACTER_COUNT) + index); + } + + + public static void main(String[] args) + { + System.out.println("Some mixed-case names:"); + printNameSamples(new SimpleNameFactory(true), 60); + System.out.println("Some lower-case names:"); + printNameSamples(new SimpleNameFactory(false), 60); + System.out.println("Some more mixed-case names:"); + printNameSamples(new SimpleNameFactory(true), 80); + System.out.println("Some more lower-case names:"); + printNameSamples(new SimpleNameFactory(false), 80); + } + + + private static void printNameSamples(SimpleNameFactory factory, int count) + { + for (int counter = 0; counter < count; counter++) + { + System.out.println(" ["+factory.nextName()+"]"); + } + } +} diff --git a/src/proguard/obfuscate/SourceFileRenamer.java b/src/proguard/obfuscate/SourceFileRenamer.java new file mode 100644 index 0000000..cbf1b63 --- /dev/null +++ b/src/proguard/obfuscate/SourceFileRenamer.java @@ -0,0 +1,84 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor changes the name stored in the source file attributes + * and source dir attributes of the classes that it visits, if the + * attributes are present. + * + * @author Eric Lafortune + */ +public class SourceFileRenamer +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor +{ + private final String newSourceFileAttribute; + + + /** + * Creates a new SourceFileRenamer. + * @param newSourceFileAttribute the new string to be put in the source file + * attributes. + */ + public SourceFileRenamer(String newSourceFileAttribute) + { + this.newSourceFileAttribute = newSourceFileAttribute; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Only visit the class attributes. + programClass.attributesAccept(this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + // Fix the source file attribute. + sourceFileAttribute.u2sourceFileIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + // Fix the source file attribute. + sourceDirAttribute.u2sourceDirIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSourceFileAttribute); + } +} diff --git a/src/proguard/obfuscate/SpecialNameFactory.java b/src/proguard/obfuscate/SpecialNameFactory.java new file mode 100644 index 0000000..a5431ca --- /dev/null +++ b/src/proguard/obfuscate/SpecialNameFactory.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +/** + * This <code>NameFactory</code> generates names that are special, by appending + * a suffix. + * + * @author Eric Lafortune + */ +public class SpecialNameFactory implements NameFactory +{ + private static final char SPECIAL_SUFFIX = '_'; + + + private final NameFactory nameFactory; + + + /** + * Creates a new <code>SpecialNameFactory</code>. + * @param nameFactory the name factory from which original names will be + * retrieved. + */ + public SpecialNameFactory(NameFactory nameFactory) + { + this.nameFactory = nameFactory; + } + + + // Implementations for NameFactory. + + public void reset() + { + nameFactory.reset(); + } + + + public String nextName() + { + return nameFactory.nextName() + SPECIAL_SUFFIX; + } + + + // Small utility methods. + + /** + * Returns whether the given name is special. + */ + static boolean isSpecialName(String name) + { + return name != null && + name.charAt(name.length()-1) == SPECIAL_SUFFIX; + } + + + public static void main(String[] args) + { + SpecialNameFactory factory = new SpecialNameFactory(new SimpleNameFactory()); + + for (int counter = 0; counter < 50; counter++) + { + System.out.println("["+factory.nextName()+"]"); + } + } +} diff --git a/src/proguard/obfuscate/Utf8Shrinker.java b/src/proguard/obfuscate/Utf8Shrinker.java new file mode 100644 index 0000000..87ada80 --- /dev/null +++ b/src/proguard/obfuscate/Utf8Shrinker.java @@ -0,0 +1,110 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.ConstantPoolRemapper; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This ClassVisitor removes UTF-8 constant pool entries that are not marked + * as being used. + * + * @see Utf8UsageMarker + * + * @author Eric Lafortune + */ +public class Utf8Shrinker implements ClassVisitor +{ + private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Shift the used constant pool entries together, filling out the + // index map. + programClass.u2constantPoolCount = + shrinkConstantPool(programClass.constantPool, + programClass.u2constantPoolCount); + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Small utility methods. + + /** + * Removes all UTF-8 entries that are not marked as being used + * from the given constant pool. + * @return the new number of entries. + */ + private int shrinkConstantPool(Constant[] constantPool, int length) + { + // Create a new index map, if necessary. + if (constantIndexMap.length < length) + { + constantIndexMap = new int[length]; + } + + int counter = 1; + boolean isUsed = false; + + // Shift the used constant pool entries together. + for (int index = 1; index < length; index++) + { + constantIndexMap[index] = counter; + + Constant constant = constantPool[index]; + + // Don't update the flag if this is the second half of a long entry. + if (constant != null) + { + isUsed = constant.getTag() != ClassConstants.CONSTANT_Utf8 || + Utf8UsageMarker.isUsed(constant); + } + + if (isUsed) + { + constantPool[counter++] = constant; + } + } + + // Clear the remaining constant pool elements. + for (int index = counter; index < length; index++) + { + constantPool[index] = null; + } + + return counter; + } +} diff --git a/src/proguard/obfuscate/Utf8UsageMarker.java b/src/proguard/obfuscate/Utf8UsageMarker.java new file mode 100644 index 0000000..c59ebb8 --- /dev/null +++ b/src/proguard/obfuscate/Utf8UsageMarker.java @@ -0,0 +1,392 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor marks all UTF-8 constant pool entries that are + * being used in the program classes it visits. + * + * @see Utf8Shrinker + * + * @author Eric Lafortune + */ +public class Utf8UsageMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + // A visitor info flag to indicate the UTF-8 constant pool entry is being used. + private static final Object USED = new Object(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Mark the UTF-8 entries referenced by the other constant pool entries. + programClass.constantPoolEntriesAccept(this); + + // Mark the UTF-8 entries referenced by the fields and methods. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Mark the UTF-8 entries referenced by the attributes. + programClass.attributesAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Mark the name and descriptor UTF-8 entries. + markCpUtf8Entry(programClass, programMember.u2nameIndex); + markCpUtf8Entry(programClass, programMember.u2descriptorIndex); + + // Mark the UTF-8 entries referenced by the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + markCpUtf8Entry(clazz, stringConstant.u2stringIndex); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + markCpUtf8Entry(clazz, classConstant.u2nameIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + markCpUtf8Entry(clazz, nameAndTypeConstant.u2nameIndex); + markCpUtf8Entry(clazz, nameAndTypeConstant.u2descriptorIndex); + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // This is the best we can do for unknown attributes. + markCpUtf8Entry(clazz, unknownAttribute.u2attributeNameIndex); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + markCpUtf8Entry(clazz, sourceFileAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + markCpUtf8Entry(clazz, sourceDirAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + markCpUtf8Entry(clazz, innerClassesAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the inner classes. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + markCpUtf8Entry(clazz, enclosingMethodAttribute.u2attributeNameIndex); + + // These entries have already been marked in the constant pool. + //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2classIndex); + //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2nameAndTypeIndex); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + markCpUtf8Entry(clazz, syntheticAttribute.u2attributeNameIndex); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + markCpUtf8Entry(clazz, signatureAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + markCpUtf8Entry(clazz, constantValueAttribute.u2attributeNameIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the element value. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + if (innerClassesInfo.u2innerNameIndex != 0) + { + markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex); + } + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex); + markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex); + markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + markCpUtf8Entry(clazz, annotation.u2typeIndex); + + // Mark the UTF-8 entries referenced by the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + if (constantElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, constantElementValue.u2elementNameIndex); + } + + // Only the string constant element value refers to a UTF-8 entry. + if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT) + { + markCpUtf8Entry(clazz, constantElementValue.u2constantValueIndex); + } + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + if (enumConstantElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex); + } + + markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex); + markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + if (classElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex); + } + + markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + if (annotationElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex); + } + + // Mark the UTF-8 entries referenced by the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + if (arrayElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex); + } + + // Mark the UTF-8 entries referenced by the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Marks the given UTF-8 constant pool entry of the given class. + */ + private void markCpUtf8Entry(Clazz clazz, int index) + { + markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index)); + } + + + /** + * Marks the given VisitorAccepter as being used. + * In this context, the VisitorAccepter will be a Utf8Constant object. + */ + private static void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be a Utf8Constant object. + */ + static boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } +} diff --git a/src/proguard/obfuscate/package.html b/src/proguard/obfuscate/package.html new file mode 100644 index 0000000..a82d266 --- /dev/null +++ b/src/proguard/obfuscate/package.html @@ -0,0 +1,3 @@ +<body> +This package contains classes to perform obfuscation of class files. +</body> |