summaryrefslogtreecommitdiff
path: root/src/proguard/obfuscate
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/obfuscate')
-rw-r--r--src/proguard/obfuscate/AttributeShrinker.java121
-rw-r--r--src/proguard/obfuscate/AttributeUsageMarker.java71
-rw-r--r--src/proguard/obfuscate/ClassObfuscator.java521
-rw-r--r--src/proguard/obfuscate/ClassRenamer.java109
-rw-r--r--src/proguard/obfuscate/DictionaryNameFactory.java189
-rw-r--r--src/proguard/obfuscate/MapCleaner.java57
-rw-r--r--src/proguard/obfuscate/MappingKeeper.java177
-rw-r--r--src/proguard/obfuscate/MappingPrinter.java147
-rw-r--r--src/proguard/obfuscate/MappingProcessor.java79
-rw-r--r--src/proguard/obfuscate/MappingReader.java199
-rw-r--r--src/proguard/obfuscate/MemberNameCleaner.java60
-rw-r--r--src/proguard/obfuscate/MemberNameCollector.java106
-rw-r--r--src/proguard/obfuscate/MemberNameConflictFixer.java159
-rw-r--r--src/proguard/obfuscate/MemberObfuscator.java230
-rw-r--r--src/proguard/obfuscate/MemberSpecialNameFilter.java101
-rw-r--r--src/proguard/obfuscate/MultiMappingProcessor.java96
-rw-r--r--src/proguard/obfuscate/NameAndTypeShrinker.java112
-rw-r--r--src/proguard/obfuscate/NameAndTypeUsageMarker.java135
-rw-r--r--src/proguard/obfuscate/NameFactory.java34
-rw-r--r--src/proguard/obfuscate/NameFactoryResetter.java59
-rw-r--r--src/proguard/obfuscate/NameMarker.java166
-rw-r--r--src/proguard/obfuscate/NumericNameFactory.java49
-rw-r--r--src/proguard/obfuscate/Obfuscator.java429
-rw-r--r--src/proguard/obfuscate/SimpleNameFactory.java156
-rw-r--r--src/proguard/obfuscate/SourceFileRenamer.java84
-rw-r--r--src/proguard/obfuscate/SpecialNameFactory.java83
-rw-r--r--src/proguard/obfuscate/Utf8Shrinker.java110
-rw-r--r--src/proguard/obfuscate/Utf8UsageMarker.java392
-rw-r--r--src/proguard/obfuscate/package.html3
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>