VariableCleaner.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
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.util.SimplifiedVisitor;
27
28import java.util.Arrays;
29
30/**
31 * This AttributeVisitor cleans up variable tables in all code attributes that
32 * it visits. It trims overlapping local variables. It removes empty local
33 * variables and empty local variable tables.
34 *
35 * @author Eric Lafortune
36 */
37public class VariableCleaner
38extends      SimplifiedVisitor
39implements   AttributeVisitor
40{
41    private boolean deleteLocalVariableTableAttribute;
42    private boolean deleteLocalVariableTypeTableAttribute;
43
44
45    // Implementations for AttributeVisitor.
46
47    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
48
49
50    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
51    {
52        deleteLocalVariableTableAttribute     = false;
53        deleteLocalVariableTypeTableAttribute = false;
54
55        // Trim the local variable table and the local variable type table.
56        codeAttribute.attributesAccept(clazz, method, this);
57
58        // Delete the local variable table if it ended up empty.
59        if (deleteLocalVariableTableAttribute)
60        {
61            AttributesEditor editor =
62                new AttributesEditor((ProgramClass)clazz,
63                                     (ProgramMember)method,
64                                     codeAttribute,
65                                     true);
66
67            editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTable);
68        }
69
70        // Delete the local variable type table if it ended up empty.
71        if (deleteLocalVariableTypeTableAttribute)
72        {
73            AttributesEditor editor =
74                new AttributesEditor((ProgramClass)clazz,
75                                     (ProgramMember)method,
76                                     codeAttribute,
77                                     true);
78
79            editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTypeTable);
80        }
81    }
82
83
84    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
85    {
86        // Clean up local variables that aren't used.
87        localVariableTableAttribute.u2localVariableTableLength =
88            removeUnusedLocalVariables(localVariableTableAttribute.localVariableTable,
89                                       localVariableTableAttribute.u2localVariableTableLength,
90                                       codeAttribute.u2maxLocals);
91
92        // Trim the code blocks of the local variables.
93        trimLocalVariables(localVariableTableAttribute.localVariableTable,
94                           localVariableTableAttribute.u2localVariableTableLength,
95                           codeAttribute.u2maxLocals);
96
97        // Delete the attribute in a moment, if it is empty.
98        if (localVariableTableAttribute.u2localVariableTableLength == 0)
99        {
100            deleteLocalVariableTableAttribute = true;
101        }
102    }
103
104
105    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
106    {
107        // Clean up local variables that aren't used.
108        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
109            removeUnusedLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
110                                           localVariableTypeTableAttribute.u2localVariableTypeTableLength,
111                                           codeAttribute.u2maxLocals);
112
113        // Trim the code blocks of the local variables.
114        trimLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
115                               localVariableTypeTableAttribute.u2localVariableTypeTableLength,
116                               codeAttribute.u2maxLocals);
117
118        // Delete the attribute in a moment, if it is empty.
119        if (localVariableTypeTableAttribute.u2localVariableTypeTableLength == 0)
120        {
121            deleteLocalVariableTypeTableAttribute = true;
122        }
123    }
124
125
126    // Small utility methods.
127
128    /**
129     * Returns the given list of local variables, without the ones that aren't
130     * used.
131     */
132    private int removeUnusedLocalVariables(LocalVariableInfo[] localVariableInfos,
133                                           int                 localVariableInfoCount,
134                                           int                 maxLocals)
135    {
136        // Overwrite all empty local variable entries.
137        // Do keep parameter entries.
138        int newIndex = 0;
139        for (int index = 0; index < localVariableInfoCount; index++)
140        {
141            LocalVariableInfo localVariableInfo = localVariableInfos[index];
142
143            if (localVariableInfo.u2index >= 0        &&
144                localVariableInfo.u2index < maxLocals &&
145                (localVariableInfo.u2startPC == 0 ||
146                 localVariableInfo.u2length > 0))
147            {
148                localVariableInfos[newIndex++] = localVariableInfos[index];
149            }
150        }
151
152        // Clean up any remaining array elements.
153        Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null);
154
155        return newIndex;
156    }
157
158
159    /**
160     * Returns the given list of local variable types, without the ones that
161     * aren't used.
162     */
163    private int removeUnusedLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
164                                               int                     localVariableTypeInfoCount,
165                                               int                     maxLocals)
166    {
167        // Overwrite all empty local variable type entries.
168        // Do keep parameter entries.
169        int newIndex = 0;
170        for (int index = 0; index < localVariableTypeInfoCount; index++)
171        {
172            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
173
174            if (localVariableTypeInfo.u2index >= 0        &&
175                localVariableTypeInfo.u2index < maxLocals &&
176                (localVariableTypeInfo.u2startPC == 0 ||
177                 localVariableTypeInfo.u2length > 0))
178            {
179                localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index];
180            }
181        }
182
183        // Clean up any remaining array elements.
184        Arrays.fill(localVariableTypeInfos,  newIndex, localVariableTypeInfoCount, null);
185
186        return newIndex;
187    }
188
189
190    /**
191     * Sorts the given list of local variables and trims their code blocks
192     * when necessary. The block is trimmed at the end, which is a bit
193     * arbitrary.
194     */
195    private void trimLocalVariables(LocalVariableInfo[] localVariableInfos,
196                                    int                 localVariableInfoCount,
197                                    int                 maxLocals)
198    {
199        // Sort the local variable entries.
200        Arrays.sort(localVariableInfos, 0, localVariableInfoCount);
201
202        int[] startPCs = createMaxArray(maxLocals);
203
204        // Trim the local variable entries, starting at the last one.
205        for (int index = localVariableInfoCount-1; index >= 0; index--)
206        {
207            LocalVariableInfo localVariableInfo = localVariableInfos[index];
208
209            // Make sure the variable's code block doesn't overlap with the
210            // next one for the same variable.
211            int maxLength = startPCs[localVariableInfo.u2index] -
212                            localVariableInfo.u2startPC;
213
214            if (localVariableInfo.u2length > maxLength)
215            {
216                localVariableInfo.u2length = maxLength;
217            }
218
219            startPCs[localVariableInfo.u2index] = localVariableInfo.u2startPC;
220        }
221    }
222
223
224    /**
225     * Sorts the given list of local variable types and trims their code blocks
226     * when necessary. The block is trimmed at the end, which is a bit
227     * arbitrary.
228     */
229    private void trimLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
230                                        int                     localVariableTypeInfoCount,
231                                        int                     maxLocals)
232    {
233        // Sort the local variable entries.
234        Arrays.sort(localVariableTypeInfos, 0, localVariableTypeInfoCount);
235
236        int[] startPCs = createMaxArray(maxLocals);
237
238        // Trim the local variable entries, starting at the last one.
239        for (int index = localVariableTypeInfoCount-1; index >= 0; index--)
240        {
241            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
242
243            // Make sure the variable's code block doesn't overlap with the
244            // next one for the same variable.
245            int maxLength = startPCs[localVariableTypeInfo.u2index] -
246                            localVariableTypeInfo.u2startPC;
247
248            if (localVariableTypeInfo.u2length > maxLength)
249            {
250                localVariableTypeInfo.u2length = maxLength;
251            }
252
253            startPCs[localVariableTypeInfo.u2index] = localVariableTypeInfo.u2startPC;
254        }
255    }
256
257
258    /**
259     * Creates an integer array of the given length, initialized with
260     * Integer.MAX_VALUE.
261     */
262    private int[] createMaxArray(int length)
263    {
264        int[] startPCs = new int[length];
265        for (int index = 0; index < length; index++)
266        {
267            startPCs[index] = Integer.MAX_VALUE;
268        }
269        return startPCs;
270    }
271}