KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > obfuscate > ClassObfuscator


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */

21 package proguard.obfuscate;
22
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.*;
26 import proguard.classfile.constant.*;
27 import proguard.classfile.constant.visitor.ConstantVisitor;
28 import proguard.classfile.util.*;
29 import proguard.classfile.visitor.*;
30
31 import java.util.*;
32
33 /**
34  * This <code>ClassVisitor</code> comes up with obfuscated names for the
35  * classes it visits, and for their class members. The actual renaming is
36  * done afterward.
37  *
38  * @see ClassRenamer
39  *
40  * @author Eric Lafortune
41  */

42 public class ClassObfuscator
43 extends SimplifiedVisitor
44 implements ClassVisitor,
45              AttributeVisitor,
46              InnerClassesInfoVisitor,
47              ConstantVisitor
48 {
49     private boolean useMixedCaseClassNames;
50     private String JavaDoc flattenPackageHierarchy;
51     private String JavaDoc repackageClasses;
52     private boolean allowAccessModification;
53
54     private final Set classNamesToAvoid = new HashSet();
55
56     // Map: [package prefix - new package prefix]
57
private final Map packagePrefixMap = new HashMap();
58
59     // Map: [package prefix - package name factory]
60
private final Map packagePrefixPackageNameFactoryMap = new HashMap();
61
62     // Map: [package prefix - class name factory]
63
private final Map packagePrefixClassNameFactoryMap = new HashMap();
64
65     // A field acting as a temporary variable and as a return value for names
66
// of outer classes.
67
private String JavaDoc newClassName;
68
69
70     /**
71      * Creates a new ClassObfuscator.
72      * @param programClassPool the class pool in which class names
73      * have to be unique.
74      * @param useMixedCaseClassNames specifies whether obfuscated packages
75      * and classes can get mixed-case names.
76      * @param flattenPackageHierarchy the base package if the obfuscated
77      * package hierarchy is to be flattened.
78      * @param repackageClasses the base package if the obfuscated
79      * classes are to be repackaged.
80      * @param allowAccessModification specifies whether obfuscated classes
81      * can be freely moved between packages.
82      */

83     public ClassObfuscator(ClassPool programClassPool,
84                            boolean useMixedCaseClassNames,
85                            String JavaDoc flattenPackageHierarchy,
86                            String JavaDoc repackageClasses,
87                            boolean allowAccessModification)
88     {
89         // First append the package separator if necessary.
90
if (flattenPackageHierarchy != null &&
91             flattenPackageHierarchy.length() > 0)
92         {
93             flattenPackageHierarchy += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
94         }
95
96         // First append the package separator if necessary.
97
if (repackageClasses != null &&
98             repackageClasses.length() > 0)
99         {
100             repackageClasses += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
101         }
102
103         this.useMixedCaseClassNames = useMixedCaseClassNames;
104         this.flattenPackageHierarchy = flattenPackageHierarchy;
105         this.repackageClasses = repackageClasses;
106         this.allowAccessModification = allowAccessModification;
107
108         // Map the root package onto the root package.
109
packagePrefixMap.put("", "");
110
111         // Collect all names that have been taken already.
112
programClassPool.classesAccept(new MyKeepCollector());
113     }
114
115
116     // Implementations for ClassVisitor.
117

118     public void visitProgramClass(ProgramClass programClass)
119     {
120         // Does this class still need a new name?
121
newClassName = newClassName(programClass);
122         if (newClassName == null)
123         {
124             // Make sure the outer class has a name, if it exists. The name will
125
// be stored as the new class name, as a side effect, so we'll be
126
// able to use it as a prefix.
127
programClass.attributesAccept(this);
128
129             // Figure out a package prefix. The package prefix may actually be
130
// the outer class prefix, if any, or it may be the fixed base
131
// package, if classes are to be repackaged.
132
String JavaDoc newPackagePrefix = newClassName != null ?
133                 newClassName + ClassConstants.INNER_CLASS_SEPARATOR :
134                 newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName()));
135
136             // Come up with a new class name.
137
newClassName = generateUniqueClassName(newPackagePrefix);
138
139             setNewClassName(programClass, newClassName);
140         }
141     }
142
143
144     // Implementations for AttributeVisitor.
145

146     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
147
148
149     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
150     {
151         // Make sure the outer classes have a name, if they exist.
152
innerClassesAttribute.innerClassEntriesAccept(clazz, this);
153     }
154
155
156     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
157     {
158         // Make sure the enclosing class has a name.
159
enclosingMethodAttribute.referencedClassAccept(this);
160     }
161
162
163     // Implementations for InnerClassesInfoVisitor.
164

165     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
166     {
167         // Make sure the outer class has a name, if it exists.
168
int innerClassIndex = innerClassesInfo.u2innerClassIndex;
169         int outerClassIndex = innerClassesInfo.u2outerClassIndex;
170         if (innerClassIndex != 0 &&
171             outerClassIndex != 0 &&
172             clazz.getClassName(innerClassIndex).equals(clazz.getName()))
173         {
174             clazz.constantPoolEntryAccept(outerClassIndex, this);
175         }
176     }
177
178
179     // Implementations for ConstantVisitor.
180

181     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
182     {
183         // Make sure the outer class has a name.
184
classConstant.referencedClassAccept(this);
185     }
186
187
188     /**
189      * This ClassVisitor collects package names and class names that have to
190      * be kept.
191      */

192     private class MyKeepCollector implements ClassVisitor
193     {
194         public void visitProgramClass(ProgramClass programClass)
195         {
196             // Does the class already have a new name?
197
String JavaDoc newClassName = newClassName(programClass);
198             if (newClassName != null)
199             {
200                 // Remember not to use this name.
201
classNamesToAvoid.add(newClassName);
202
203                 // Are we not aggressively repackaging all obfuscated classes?
204
if (repackageClasses == null ||
205                     !allowAccessModification)
206                 {
207                     String JavaDoc className = programClass.getName();
208
209                     // Keep the package name for all other classes in the same
210
// package. Do this resursively if we're not doing any
211
// repackaging.
212
mapPackageName(className,
213                                    newClassName,
214                                    repackageClasses == null &&
215                                    flattenPackageHierarchy == null);
216                 }
217             }
218         }
219
220
221         public void visitLibraryClass(LibraryClass libraryClass)
222         {
223         }
224
225
226         /**
227          * Makes sure the package name of the given class will always be mapped
228          * consistently with its new name.
229          */

230         private void mapPackageName(String JavaDoc className,
231                                     String JavaDoc newClassName,
232                                     boolean recursively)
233         {
234             String JavaDoc packagePrefix = ClassUtil.internalPackagePrefix(className);
235             String JavaDoc newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName);
236
237             // Put the mapping of this package prefix, and possibly of its
238
// entire hierarchy, into the package prefix map.
239
do
240             {
241                 packagePrefixMap.put(packagePrefix, newPackagePrefix);
242
243                 if (!recursively)
244                 {
245                     break;
246                 }
247
248                 packagePrefix = ClassUtil.internalPackagePrefix(packagePrefix);
249                 newPackagePrefix = ClassUtil.internalPackagePrefix(newPackagePrefix);
250             }
251             while (packagePrefix.length() > 0 &&
252                    newPackagePrefix.length() > 0);
253         }
254     }
255
256
257     // Small utility methods.
258

259     /**
260      * Finds or creates the new package prefix for the given package.
261      */

262     private String JavaDoc newPackagePrefix(String JavaDoc packagePrefix)
263     {
264         // Doesn't the package prefix have a new package prefix yet?
265
String JavaDoc newPackagePrefix = (String JavaDoc)packagePrefixMap.get(packagePrefix);
266         if (newPackagePrefix == null)
267         {
268             // Are we forcing a new package prefix?
269
if (repackageClasses != null)
270             {
271                 return repackageClasses;
272             }
273
274             // Are we forcing a new superpackage prefix?
275
// Othewrise figure out the new superpackage prefix, recursively.
276
String JavaDoc newSuperPackagePrefix = flattenPackageHierarchy != null ?
277                 flattenPackageHierarchy :
278                 newPackagePrefix(ClassUtil.internalPackagePrefix(packagePrefix));
279
280             // Come up with a new package prefix.
281
newPackagePrefix = generateUniquePackagePrefix(newSuperPackagePrefix);
282
283             // Remember to use this mapping in the future.
284
packagePrefixMap.put(packagePrefix, newPackagePrefix);
285         }
286
287         return newPackagePrefix;
288     }
289
290
291     /**
292      * Creates a new package prefix in the given new superpackage.
293      */

294     private String JavaDoc generateUniquePackagePrefix(String JavaDoc newSuperPackagePrefix)
295     {
296         // Find the right name factory for this package.
297
NameFactory packageNameFactory =
298             (NameFactory)packagePrefixPackageNameFactoryMap.get(newSuperPackagePrefix);
299         if (packageNameFactory == null)
300         {
301             // We haven't seen packages in this superpackage before. Create
302
// a new name factory for them.
303
packageNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
304             packagePrefixPackageNameFactoryMap.put(newSuperPackagePrefix,
305                                                    packageNameFactory);
306         }
307
308         // Come up with package names until we get an original one.
309
String JavaDoc newPackagePrefix;
310         do
311         {
312             // Let the factory produce a package name.
313
newPackagePrefix = newSuperPackagePrefix +
314                                packageNameFactory.nextName() +
315                                ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
316         }
317         while (packagePrefixMap.containsValue(newPackagePrefix));
318
319         return newPackagePrefix;
320     }
321
322
323     /**
324      * Creates a new class name in the given new package.
325      */

326     private String JavaDoc generateUniqueClassName(String JavaDoc newPackagePrefix)
327     {
328         // Find the right name factory for this package.
329
NameFactory classNameFactory =
330             (NameFactory)packagePrefixClassNameFactoryMap.get(newPackagePrefix);
331         if (classNameFactory == null)
332         {
333             // We haven't seen classes in this package before. Create a new name
334
// factory for them.
335
classNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
336             packagePrefixClassNameFactoryMap.put(newPackagePrefix,
337                                                  classNameFactory);
338         }
339
340         // Come up with class names until we get an original one.
341
String JavaDoc newClassName;
342         do
343         {
344             // Let the factory produce a class name.
345
newClassName = newPackagePrefix +
346                            classNameFactory.nextName();
347         }
348         while (classNamesToAvoid.contains(newClassName));
349
350         return newClassName;
351     }
352
353
354     /**
355      * Assigns a new name to the given class.
356      * @param clazz the given class.
357      * @param name the new name.
358      */

359     static void setNewClassName(Clazz clazz, String JavaDoc name)
360     {
361         clazz.setVisitorInfo(name);
362     }
363
364
365     /**
366      * Retrieves the new name of the given class.
367      * @param clazz the given class.
368      * @return the class's new name, or <code>null</code> if it doesn't
369      * have one yet.
370      */

371     static String JavaDoc newClassName(Clazz clazz)
372     {
373         Object JavaDoc visitorInfo = clazz.getVisitorInfo();
374
375         return visitorInfo instanceof String JavaDoc ?
376             (String JavaDoc)visitorInfo :
377             null;
378     }
379 }
380
Popular Tags