1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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 */
21package proguard.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.annotation.*;
26import proguard.classfile.attribute.annotation.visitor.*;
27import proguard.classfile.attribute.preverification.*;
28import proguard.classfile.attribute.preverification.visitor.*;
29import proguard.classfile.attribute.visitor.*;
30import proguard.classfile.constant.*;
31import proguard.classfile.constant.visitor.ConstantVisitor;
32import proguard.classfile.instruction.*;
33import proguard.classfile.instruction.visitor.InstructionVisitor;
34import proguard.classfile.util.SimplifiedVisitor;
35import proguard.classfile.visitor.*;
36
37import java.util.Arrays;
38
39/**
40 * This ClassVisitor removes all unused entries from the constant pool.
41 *
42 * @author Eric Lafortune
43 */
44public class ConstantPoolShrinker
45extends      SimplifiedVisitor
46implements   ClassVisitor,
47             MemberVisitor,
48             ConstantVisitor,
49             AttributeVisitor,
50             BootstrapMethodInfoVisitor,
51             InnerClassesInfoVisitor,
52             ExceptionInfoVisitor,
53             StackMapFrameVisitor,
54             VerificationTypeVisitor,
55             LocalVariableInfoVisitor,
56             LocalVariableTypeInfoVisitor,
57             AnnotationVisitor,
58             ElementValueVisitor,
59             InstructionVisitor
60{
61    // A visitor info flag to indicate the constant is being used.
62    private static final Object USED = new Object();
63
64    private       int[]                constantIndexMap     = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
65    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
66
67
68    // Implementations for ClassVisitor.
69
70    public void visitProgramClass(ProgramClass programClass)
71    {
72        // Mark this class's name.
73        markConstant(programClass, programClass.u2thisClass);
74
75        // Mark the superclass class constant.
76        programClass.superClassConstantAccept(this);
77
78        // Mark the interface class constants.
79        programClass.interfaceConstantsAccept(this);
80
81        // Mark the constants referenced by the class members.
82        programClass.fieldsAccept(this);
83        programClass.methodsAccept(this);
84
85        // Mark the attributes.
86        programClass.attributesAccept(this);
87
88        // Shift the used constant pool entries together, filling out the
89        // index map.
90        int newConstantPoolCount =
91            shrinkConstantPool(programClass.constantPool,
92                               programClass.u2constantPoolCount);
93
94        // Remap the references to the constant pool if it has shrunk.
95        if (newConstantPoolCount < programClass.u2constantPoolCount)
96        {
97            programClass.u2constantPoolCount = newConstantPoolCount;
98
99            // Remap all constant pool references.
100            constantPoolRemapper.setConstantIndexMap(constantIndexMap);
101            constantPoolRemapper.visitProgramClass(programClass);
102        }
103    }
104
105
106    // Implementations for MemberVisitor.
107
108    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
109    {
110        // Mark the name and descriptor.
111        markConstant(programClass, programMember.u2nameIndex);
112        markConstant(programClass, programMember.u2descriptorIndex);
113
114        // Mark the attributes.
115        programMember.attributesAccept(programClass, this);
116    }
117
118
119    // Implementations for ConstantVisitor.
120
121    public void visitAnyConstant(Clazz clazz, Constant constant)
122    {
123        markAsUsed(constant);
124    }
125
126
127    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
128    {
129        markAsUsed(stringConstant);
130
131        markConstant(clazz, stringConstant.u2stringIndex);
132    }
133
134
135    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
136    {
137        markAsUsed(invokeDynamicConstant);
138
139        markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex);
140
141        // Mark the bootstrap methods attribute.
142        clazz.attributesAccept(this);
143    }
144
145
146    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
147    {
148        markAsUsed(methodHandleConstant);
149
150        markConstant(clazz, methodHandleConstant.u2referenceIndex);
151    }
152
153
154    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
155    {
156        markAsUsed(refConstant);
157
158        markConstant(clazz, refConstant.u2classIndex);
159        markConstant(clazz, refConstant.u2nameAndTypeIndex);
160    }
161
162
163    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
164    {
165        markAsUsed(classConstant);
166
167        markConstant(clazz, classConstant.u2nameIndex);
168    }
169
170
171    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
172    {
173        markAsUsed(methodTypeConstant);
174
175        markConstant(clazz, methodTypeConstant.u2descriptorIndex);
176    }
177
178
179    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
180    {
181        markAsUsed(nameAndTypeConstant);
182
183        markConstant(clazz, nameAndTypeConstant.u2nameIndex);
184        markConstant(clazz, nameAndTypeConstant.u2descriptorIndex);
185    }
186
187
188    // Implementations for AttributeVisitor.
189
190    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
191    {
192        markConstant(clazz, attribute.u2attributeNameIndex);
193    }
194
195
196    public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute)
197    {
198        markConstant(clazz, bootstrapMethodsAttribute.u2attributeNameIndex);
199
200        // Mark the bootstrap method entries.
201        bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this);
202    }
203
204
205    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
206    {
207        markConstant(clazz, sourceFileAttribute.u2attributeNameIndex);
208        markConstant(clazz, sourceFileAttribute.u2sourceFileIndex);
209    }
210
211
212    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
213    {
214        markConstant(clazz, sourceDirAttribute.u2attributeNameIndex);
215        markConstant(clazz, sourceDirAttribute.u2sourceDirIndex);
216    }
217
218
219    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
220    {
221        markConstant(clazz, innerClassesAttribute.u2attributeNameIndex);
222
223        // Mark the outer class entries.
224        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
225    }
226
227
228    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
229    {
230        markConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex);
231        markConstant(clazz, enclosingMethodAttribute.u2classIndex);
232
233        if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
234        {
235            markConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex);
236        }
237    }
238
239
240    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
241    {
242        markConstant(clazz, signatureAttribute.u2attributeNameIndex);
243        markConstant(clazz, signatureAttribute.u2signatureIndex);
244    }
245
246
247    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
248    {
249        markConstant(clazz, constantValueAttribute.u2attributeNameIndex);
250        markConstant(clazz, constantValueAttribute.u2constantValueIndex);
251    }
252
253
254    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
255    {
256        markConstant(clazz, exceptionsAttribute.u2attributeNameIndex);
257
258        // Mark the constant pool entries referenced by the exceptions.
259        exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this);
260    }
261
262
263    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
264    {
265        markConstant(clazz, codeAttribute.u2attributeNameIndex);
266
267        // Mark the constant pool entries referenced by the instructions,
268        // by the exceptions, and by the attributes.
269        codeAttribute.instructionsAccept(clazz, method, this);
270        codeAttribute.exceptionsAccept(clazz, method, this);
271        codeAttribute.attributesAccept(clazz, method, this);
272    }
273
274
275    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
276    {
277        markConstant(clazz, stackMapAttribute.u2attributeNameIndex);
278
279        // Mark the constant pool entries referenced by the stack map frames.
280        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
281    }
282
283
284    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
285    {
286        markConstant(clazz, stackMapTableAttribute.u2attributeNameIndex);
287
288        // Mark the constant pool entries referenced by the stack map frames.
289        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
290    }
291
292
293    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
294    {
295        markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex);
296
297        // Mark the constant pool entries referenced by the local variables.
298        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
299    }
300
301
302    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
303    {
304        markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex);
305
306        // Mark the constant pool entries referenced by the local variable types.
307        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
308    }
309
310
311    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
312    {
313        markConstant(clazz, annotationsAttribute.u2attributeNameIndex);
314
315        // Mark the constant pool entries referenced by the annotations.
316        annotationsAttribute.annotationsAccept(clazz, this);
317    }
318
319
320    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
321    {
322        markConstant(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
323
324        // Mark the constant pool entries referenced by the annotations.
325        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
326    }
327
328
329    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
330    {
331        markConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex);
332
333        // Mark the constant pool entries referenced by the element value.
334        annotationDefaultAttribute.defaultValueAccept(clazz, this);
335    }
336
337
338    // Implementations for BootstrapMethodInfoVisitor.
339
340    public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo)
341    {
342        markConstant(clazz, bootstrapMethodInfo.u2methodHandleIndex);
343
344        // Mark the constant pool entries referenced by the arguments.
345        bootstrapMethodInfo.methodArgumentsAccept(clazz, this);
346    }
347
348
349    // Implementations for InnerClassesInfoVisitor.
350
351    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
352    {
353        innerClassesInfo.innerClassConstantAccept(clazz, this);
354        innerClassesInfo.outerClassConstantAccept(clazz, this);
355        innerClassesInfo.innerNameConstantAccept(clazz, this);
356    }
357
358
359    // Implementations for ExceptionInfoVisitor.
360
361    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
362    {
363        if (exceptionInfo.u2catchType != 0)
364        {
365            markConstant(clazz, exceptionInfo.u2catchType);
366        }
367    }
368
369
370    // Implementations for StackMapFrameVisitor.
371
372    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) {}
373
374
375    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
376    {
377        // Mark the constant pool entries referenced by the verification types.
378        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
379    }
380
381
382    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
383    {
384        // Mark the constant pool entries referenced by the verification types.
385        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
386    }
387
388
389    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
390    {
391        // Mark the constant pool entries referenced by the verification types.
392        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
393        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
394    }
395
396
397    // Implementations for VerificationTypeVisitor.
398
399    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
400
401
402    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
403    {
404        markConstant(clazz, objectType.u2classIndex);
405    }
406
407
408    // Implementations for LocalVariableInfoVisitor.
409
410    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
411    {
412        markConstant(clazz, localVariableInfo.u2nameIndex);
413        markConstant(clazz, localVariableInfo.u2descriptorIndex);
414    }
415
416
417    // Implementations for LocalVariableTypeInfoVisitor.
418
419    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
420    {
421        markConstant(clazz, localVariableTypeInfo.u2nameIndex);
422        markConstant(clazz, localVariableTypeInfo.u2signatureIndex);
423    }
424
425
426    // Implementations for AnnotationVisitor.
427
428    public void visitAnnotation(Clazz clazz, Annotation annotation)
429    {
430        markConstant(clazz, annotation.u2typeIndex);
431
432        // Mark the constant pool entries referenced by the element values.
433        annotation.elementValuesAccept(clazz, this);
434    }
435
436
437    // Implementations for ElementValueVisitor.
438
439    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
440    {
441        if (constantElementValue.u2elementNameIndex != 0)
442        {
443            markConstant(clazz, constantElementValue.u2elementNameIndex);
444        }
445
446        markConstant(clazz, constantElementValue.u2constantValueIndex);
447    }
448
449
450    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
451    {
452        if (enumConstantElementValue.u2elementNameIndex != 0)
453        {
454            markConstant(clazz, enumConstantElementValue.u2elementNameIndex);
455        }
456
457        markConstant(clazz, enumConstantElementValue.u2typeNameIndex);
458        markConstant(clazz, enumConstantElementValue.u2constantNameIndex);
459    }
460
461
462    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
463    {
464        if (classElementValue.u2elementNameIndex != 0)
465        {
466            markConstant(clazz, classElementValue.u2elementNameIndex);
467        }
468
469        markConstant(clazz, classElementValue.u2classInfoIndex);
470    }
471
472
473    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
474    {
475        if (annotationElementValue.u2elementNameIndex != 0)
476        {
477            markConstant(clazz, annotationElementValue.u2elementNameIndex);
478        }
479
480        // Mark the constant pool entries referenced by the annotation.
481        annotationElementValue.annotationAccept(clazz, this);
482    }
483
484
485    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
486    {
487        if (arrayElementValue.u2elementNameIndex != 0)
488        {
489            markConstant(clazz, arrayElementValue.u2elementNameIndex);
490        }
491
492        // Mark the constant pool entries referenced by the element values.
493        arrayElementValue.elementValuesAccept(clazz, annotation, this);
494    }
495
496
497    // Implementations for InstructionVisitor.
498
499    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
500
501
502    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
503    {
504        markConstant(clazz, constantInstruction.constantIndex);
505    }
506
507
508    // Small utility methods.
509
510    /**
511     * Marks the given constant pool entry of the given class. This includes
512     * visiting any referenced objects.
513     */
514    private void markConstant(Clazz clazz, int index)
515    {
516        clazz.constantPoolEntryAccept(index, this);
517    }
518
519
520    /**
521     * Marks the given visitor accepter as being used.
522     */
523    private void markAsUsed(Constant constant)
524    {
525        constant.setVisitorInfo(USED);
526    }
527
528
529    /**
530     * Returns whether the given visitor accepter has been marked as being used.
531     */
532    private boolean isUsed(VisitorAccepter visitorAccepter)
533    {
534        return visitorAccepter.getVisitorInfo() == USED;
535    }
536
537
538    /**
539     * Removes all constants that are not marked as being used from the given
540     * constant pool.
541     * @return the new number of entries.
542     */
543    private int shrinkConstantPool(Constant[] constantPool, int length)
544    {
545        // Create a new index map, if necessary.
546        if (constantIndexMap.length < length)
547        {
548            constantIndexMap = new int[length];
549        }
550
551        int     counter = 1;
552        boolean isUsed  = false;
553
554        // Shift the used constant pool entries together.
555        for (int index = 1; index < length; index++)
556        {
557            constantIndexMap[index] = counter;
558
559            Constant constant = constantPool[index];
560
561            // Don't update the flag if this is the second half of a long entry.
562            if (constant != null)
563            {
564                isUsed = isUsed(constant);
565            }
566
567            if (isUsed)
568            {
569                constantPool[counter++] = constant;
570            }
571        }
572
573        // Clear the remaining constant pool elements.
574        Arrays.fill(constantPool, counter, length, null);
575
576        return counter;
577    }
578}
579