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.visitor.AttributeVisitor;
26import proguard.classfile.constant.*;
27import proguard.classfile.constant.visitor.ConstantVisitor;
28import proguard.classfile.editor.ConstantPoolRemapper;
29import proguard.classfile.util.SimplifiedVisitor;
30import proguard.classfile.visitor.ClassVisitor;
31
32import java.util.Arrays;
33
34
35/**
36 * This ClassVisitor removes NameAndType constant pool entries that are not
37 * used.
38 *
39 * @author Eric Lafortune
40 */
41public class NameAndTypeShrinker
42extends      SimplifiedVisitor
43implements   ClassVisitor,
44             ConstantVisitor,
45             AttributeVisitor
46{
47    // A visitor info flag to indicate the NameAndType constant pool entry is being used.
48    private static final Object USED = new Object();
49
50    private       int[]                constantIndexMap;
51    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
52
53
54    // Implementations for ClassVisitor.
55
56    public void visitProgramClass(ProgramClass programClass)
57    {
58        // Mark the NameAndType entries referenced by all other constant pool
59        // entries.
60        programClass.constantPoolEntriesAccept(this);
61
62        // Mark the NameAndType entries referenced by all EnclosingMethod
63        // attributes.
64        programClass.attributesAccept(this);
65
66        // Shift the used constant pool entries together, filling out the
67        // index map.
68        int newConstantPoolCount =
69            shrinkConstantPool(programClass.constantPool,
70                               programClass.u2constantPoolCount);
71
72        // Remap the references to the constant pool if it has shrunk.
73        if (newConstantPoolCount < programClass.u2constantPoolCount)
74        {
75            programClass.u2constantPoolCount = newConstantPoolCount;
76
77            // Remap all constant pool references.
78            constantPoolRemapper.setConstantIndexMap(constantIndexMap);
79            constantPoolRemapper.visitProgramClass(programClass);
80        }
81    }
82
83
84    // Implementations for ConstantVisitor.
85
86    public void visitAnyConstant(Clazz clazz, Constant constant) {}
87
88
89    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
90    {
91        markNameAndTypeConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex);
92    }
93
94
95    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
96    {
97        markNameAndTypeConstant(clazz, refConstant.u2nameAndTypeIndex);
98    }
99
100
101    // Implementations for AttributeVisitor.
102
103    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
104
105
106    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
107    {
108        if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
109        {
110            markNameAndTypeConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex);
111        }
112    }
113
114
115    // Small utility methods.
116
117    /**
118     * Marks the given UTF-8 constant pool entry of the given class.
119     */
120    private void markNameAndTypeConstant(Clazz clazz, int index)
121    {
122         markAsUsed((NameAndTypeConstant)((ProgramClass)clazz).getConstant(index));
123    }
124
125
126    /**
127     * Marks the given VisitorAccepter as being used.
128     * In this context, the VisitorAccepter will be a NameAndTypeConstant object.
129     */
130    private void markAsUsed(VisitorAccepter visitorAccepter)
131    {
132        visitorAccepter.setVisitorInfo(USED);
133    }
134
135
136    /**
137     * Returns whether the given VisitorAccepter has been marked as being used.
138     * In this context, the VisitorAccepter will be a NameAndTypeConstant object.
139     */
140    private boolean isUsed(VisitorAccepter visitorAccepter)
141    {
142        return visitorAccepter.getVisitorInfo() == USED;
143    }
144
145
146    /**
147     * Removes all NameAndType entries that are not marked as being used
148     * from the given constant pool.
149     * @return the new number of entries.
150     */
151    private int shrinkConstantPool(Constant[] constantPool, int length)
152    {
153        // Create a new index map, if necessary.
154        if (constantIndexMap == null ||
155            constantIndexMap.length < length)
156        {
157            constantIndexMap = new int[length];
158        }
159
160        int     counter = 1;
161        boolean isUsed  = false;
162
163        // Shift the used constant pool entries together.
164        for (int index = 1; index < length; index++)
165        {
166            constantIndexMap[index] = counter;
167
168            Constant constant = constantPool[index];
169
170            // Don't update the flag if this is the second half of a long entry.
171            if (constant != null)
172            {
173                isUsed = constant.getTag() != ClassConstants.CONSTANT_NameAndType ||
174                         isUsed(constant);
175            }
176
177            if (isUsed)
178            {
179                constantPool[counter++] = constant;
180            }
181        }
182
183        // Clear the remaining constant pool elements.
184        Arrays.fill(constantPool, counter, length, null);
185
186        return counter;
187    }
188}
189