KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > shrink > ClassShrinker


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.shrink;
22
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.annotation.*;
26 import proguard.classfile.attribute.annotation.visitor.*;
27 import proguard.classfile.attribute.visitor.AttributeVisitor;
28 import proguard.classfile.constant.Constant;
29 import proguard.classfile.editor.ConstantPoolRemapper;
30 import proguard.classfile.util.SimplifiedVisitor;
31 import proguard.classfile.visitor.*;
32
33 /**
34  * This ClassVisitor removes constant pool entries and class members that
35  * are not marked as being used.
36  *
37  * @see UsageMarker
38  *
39  * @author Eric Lafortune
40  */

41 public class ClassShrinker
42 extends SimplifiedVisitor
43 implements ClassVisitor,
44              MemberVisitor,
45              AttributeVisitor,
46              AnnotationVisitor,
47              ElementValueVisitor
48 {
49     private UsageMarker usageMarker;
50
51     private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
52
53     private ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
54
55
56     /**
57      * Creates a new ClassShrinker.
58      * @param usageMarker the usage marker that is used to mark the classes
59      * and class members.
60      */

61     public ClassShrinker(UsageMarker usageMarker)
62     {
63         this.usageMarker = usageMarker;
64     }
65
66
67     // Implementations for ClassVisitor.
68

69     public void visitProgramClass(ProgramClass programClass)
70     {
71         // Shrink the arrays for constant pool, interfaces, fields, methods,
72
// and class attributes.
73
programClass.u2interfacesCount =
74             shrinkConstantIndexArray(programClass.constantPool,
75                                      programClass.u2interfaces,
76                                      programClass.u2interfacesCount);
77
78         // Shrinking the constant pool also sets up an index map.
79
programClass.u2constantPoolCount =
80             shrinkConstantPool(programClass.constantPool,
81                                programClass.u2constantPoolCount);
82
83         programClass.u2fieldsCount =
84             shrinkArray(programClass.fields,
85                         programClass.u2fieldsCount);
86
87         programClass.u2methodsCount =
88             shrinkArray(programClass.methods,
89                         programClass.u2methodsCount);
90
91         programClass.u2attributesCount =
92             shrinkArray(programClass.attributes,
93                         programClass.u2attributesCount);
94
95         // Compact the remaining fields, methods, and attributes,
96
// and remap their references to the constant pool.
97
programClass.fieldsAccept(this);
98         programClass.methodsAccept(this);
99         programClass.attributesAccept(this);
100
101         // Remap all constant pool references.
102
constantPoolRemapper.setConstantIndexMap(constantIndexMap);
103         constantPoolRemapper.visitProgramClass(programClass);
104
105         // Compact the extra field pointing to the subclasses of this class.
106
programClass.subClasses =
107             shrinkToNewArray(programClass.subClasses);
108     }
109
110
111     public void visitLibraryClass(LibraryClass libraryClass)
112     {
113         // Library classes are left unchanged.
114

115         // Compact the extra field pointing to the subclasses of this class.
116
libraryClass.subClasses =
117             shrinkToNewArray(libraryClass.subClasses);
118     }
119
120
121     // Implementations for MemberVisitor.
122

123     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
124     {
125         // Shrink the attributes array.
126
programMember.u2attributesCount =
127             shrinkArray(programMember.attributes,
128                         programMember.u2attributesCount);
129
130         // Shrink any attributes.
131
programMember.attributesAccept(programClass, this);
132     }
133
134
135     // Implementations for AttributeVisitor.
136

137     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
138
139
140     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
141     {
142         // Shrink the array of InnerClassesInfo objects.
143
innerClassesAttribute.u2classesCount =
144             shrinkArray(innerClassesAttribute.classes,
145                         innerClassesAttribute.u2classesCount);
146     }
147
148
149     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
150     {
151         // Sometimes, a class is still referenced (apparently as a dummy class),
152
// but its enclosing method is not. Then remove the reference to
153
// the enclosing method.
154
// E.g. the anonymous inner class javax.swing.JList$1 is defined inside
155
// a constructor of javax.swing.JList, but it is also referenced as a
156
// dummy argument in a constructor of javax.swing.JList$ListSelectionHandler.
157
if (enclosingMethodAttribute.referencedMethod != null &&
158             !usageMarker.isUsed(enclosingMethodAttribute.referencedMethod))
159         {
160             enclosingMethodAttribute.u2nameAndTypeIndex = 0;
161
162             enclosingMethodAttribute.referencedMethod = null;
163         }
164     }
165
166
167     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
168     {
169         // Shrink the attributes array.
170
codeAttribute.u2attributesCount =
171             shrinkArray(codeAttribute.attributes,
172                         codeAttribute.u2attributesCount);
173     }
174
175
176     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
177     {
178         // Shrink the annotations array.
179
annotationsAttribute.u2annotationsCount =
180             shrinkArray(annotationsAttribute.annotations,
181                         annotationsAttribute.u2annotationsCount);
182
183         // Shrink the annotations themselves.
184
annotationsAttribute.annotationsAccept(clazz, this);
185     }
186
187
188     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
189     {
190         // Loop over all parameters.
191
for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++)
192         {
193             // Shrink the parameter annotations array.
194
parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] =
195                 shrinkArray(parameterAnnotationsAttribute.parameterAnnotations[parameterIndex],
196                             parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]);
197         }
198
199         // Shrink the annotations themselves.
200
parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
201     }
202
203
204     // Implementations for AnnotationVisitor.
205

206     public void visitAnnotation(Clazz clazz, Annotation annotation)
207     {
208         // Shrink the element values array.
209
annotation.u2elementValuesCount =
210             shrinkArray(annotation.elementValues,
211                         annotation.u2elementValuesCount);
212
213         // Shrink the element values themselves.
214
annotation.elementValuesAccept(clazz, this);
215     }
216
217
218     // Implementations for ElementValueVisitor.
219

220     public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {}
221
222
223     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
224     {
225         // Shrink the contained annotation.
226
annotationElementValue.annotationAccept(clazz, this);
227     }
228
229
230     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
231     {
232         // Shrink the element values array.
233
arrayElementValue.u2elementValuesCount =
234             shrinkArray(arrayElementValue.elementValues,
235                         arrayElementValue.u2elementValuesCount);
236
237         // Shrink the element values themselves.
238
arrayElementValue.elementValuesAccept(clazz, annotation, this);
239     }
240
241
242     // Small utility methods.
243

244     /**
245      * Removes all entries that are not marked as being used from the given
246      * constant pool.
247      * @return the new number of entries.
248      */

249     private int shrinkConstantPool(Constant[] constantPool, int length)
250     {
251         if (constantIndexMap.length < length)
252         {
253             constantIndexMap = new int[length];
254         }
255
256         int counter = 1;
257         boolean isUsed = false;
258
259         // Shift the used constant pool entries together.
260
for (int index = 1; index < length; index++)
261         {
262             constantIndexMap[index] = counter;
263
264             Constant constant = constantPool[index];
265
266             // Don't update the flag if this is the second half of a long entry.
267
if (constant != null)
268             {
269                 isUsed = usageMarker.isUsed(constant);
270             }
271
272             if (isUsed)
273             {
274                 constantPool[counter++] = constant;
275             }
276         }
277
278         // Clear the remaining constant pool elements.
279
for (int index = counter; index < length; index++)
280         {
281             constantPool[index] = null;
282         }
283
284         return counter;
285     }
286
287
288     /**
289      * Removes all indices that point to unused constant pool entries
290      * from the given array.
291      * @return the new number of indices.
292      */

293     private int shrinkConstantIndexArray(Constant[] constantPool, int[] array, int length)
294     {
295         int counter = 0;
296
297         // Shift the used objects together.
298
for (int index = 0; index < length; index++)
299         {
300             if (usageMarker.isUsed(constantPool[array[index]]))
301             {
302                 array[counter++] = array[index];
303             }
304         }
305
306         // Clear the remaining array elements.
307
for (int index = counter; index < length; index++)
308         {
309             array[index] = 0;
310         }
311
312         return counter;
313     }
314
315
316     /**
317      * Removes all Clazz objects that are not marked as being used
318      * from the given array and returns the remaining objects in a an array
319      * of the right size.
320      * @return the new array.
321      */

322     private Clazz[] shrinkToNewArray(Clazz[] array)
323     {
324         if (array == null)
325         {
326             return null;
327         }
328
329         // Shrink the given array in-place.
330
int length = shrinkArray(array, array.length);
331         if (length == 0)
332         {
333             return null;
334         }
335
336         // Return immediately if the array is of right size already.
337
if (length == array.length)
338         {
339             return array;
340         }
341
342         // Copy the remaining elements into a new array of the right size.
343
Clazz[] newArray = new Clazz[length];
344         System.arraycopy(array, 0, newArray, 0, length);
345         return newArray;
346     }
347
348
349     /**
350      * Removes all VisitorAccepter objects that are not marked as being used
351      * from the given array.
352      * @return the new number of VisitorAccepter objects.
353      */

354     private int shrinkArray(VisitorAccepter[] array, int length)
355     {
356         int counter = 0;
357
358         // Shift the used objects together.
359
for (int index = 0; index < length; index++)
360         {
361             if (usageMarker.isUsed(array[index]))
362             {
363                 array[counter++] = array[index];
364             }
365         }
366
367         // Clear the remaining array elements.
368
for (int index = counter; index < length; index++)
369         {
370             array[index] = null;
371         }
372
373         return counter;
374     }
375 }
376
Popular Tags