MethodDescriptorShrinker.java revision 9f606f95f03a75961498803e24bee6799a7c0885
1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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.optimize;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.annotation.*;
26import proguard.classfile.attribute.visitor.AttributeVisitor;
27import proguard.classfile.editor.ConstantPoolEditor;
28import proguard.classfile.util.*;
29import proguard.classfile.visitor.MemberVisitor;
30import proguard.optimize.info.*;
31import proguard.optimize.peephole.VariableShrinker;
32
33/**
34 * This MemberVisitor removes unused parameters in the descriptors of the
35 * methods that it visits.
36 *
37 * @see ParameterUsageMarker
38 * @see VariableUsageMarker
39 * @see VariableShrinker
40 * @author Eric Lafortune
41 */
42public class MethodDescriptorShrinker
43extends      SimplifiedVisitor
44implements   MemberVisitor,
45             AttributeVisitor
46{
47    private static final boolean DEBUG = false;
48
49
50    private final MemberVisitor extraMemberVisitor;
51
52
53    /**
54     * Creates a new MethodDescriptorShrinker.
55     */
56    public MethodDescriptorShrinker()
57    {
58        this(null);
59    }
60
61
62    /**
63     * Creates a new MethodDescriptorShrinker with an extra visitor.
64     * @param extraMemberVisitor an optional extra visitor for all methods whose
65     *                           parameters have been simplified.
66     */
67    public MethodDescriptorShrinker(MemberVisitor extraMemberVisitor)
68    {
69        this.extraMemberVisitor = extraMemberVisitor;
70    }
71
72
73    // Implementations for MemberVisitor.
74
75    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
76    {
77        // Update the descriptor if it has any unused parameters.
78        String descriptor    = programMethod.getDescriptor(programClass);
79        String newDescriptor = shrinkDescriptor(programMethod, descriptor);
80
81        if (!descriptor.equals(newDescriptor))
82        {
83            // Shrink the signature and parameter annotations.
84            programMethod.attributesAccept(programClass, this);
85
86            String name    = programMethod.getName(programClass);
87            String newName = name;
88
89            // Append a code, if the method isn't a class instance initializer.
90            if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
91            {
92                newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
93            }
94
95            if (DEBUG)
96            {
97                System.out.println("MethodDescriptorShrinker:");
98                System.out.println("  Class file        = "+programClass.getName());
99                System.out.println("  Method name       = "+name);
100                System.out.println("                   -> "+newName);
101                System.out.println("  Method descriptor = "+descriptor);
102                System.out.println("                   -> "+newDescriptor);
103            }
104
105            ConstantPoolEditor constantPoolEditor =
106                new ConstantPoolEditor(programClass);
107
108            // Update the name, if necessary.
109            if (!newName.equals(name))
110            {
111                programMethod.u2nameIndex =
112                    constantPoolEditor.addUtf8Constant(newName);
113            }
114
115            // Update the referenced classes.
116            programMethod.referencedClasses =
117                shrinkReferencedClasses(programMethod,
118                                        descriptor,
119                                        programMethod.referencedClasses);
120
121            // Finally, update the descriptor itself.
122            programMethod.u2descriptorIndex =
123                constantPoolEditor.addUtf8Constant(newDescriptor);
124
125            // Visit the method, if required.
126            if (extraMemberVisitor != null)
127            {
128                extraMemberVisitor.visitProgramMethod(programClass, programMethod);
129            }
130        }
131    }
132
133
134    // Implementations for AttributeVisitor.
135
136    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
137
138
139    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
140    {
141        // Compute the new signature.
142        String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
143        String newSignature = shrinkDescriptor(method, signature);
144
145        // Update the signature.
146        signatureAttribute.u2signatureIndex =
147            new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
148
149        // Update the referenced classes.
150        signatureAttribute.referencedClasses =
151            shrinkReferencedClasses(method,
152                                    signature,
153                                    signatureAttribute.referencedClasses);
154    }
155
156
157    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
158    {
159        int[]          annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
160        Annotation[][] annotations       = parameterAnnotationsAttribute.parameterAnnotations;
161
162        // All parameters of non-static methods are shifted by one in the local
163        // variable frame.
164        int parameterIndex =
165            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
166                0 : 1;
167
168        int annotationIndex    = 0;
169        int newAnnotationIndex = 0;
170
171        // Go over the parameters.
172        String descriptor = method.getDescriptor(clazz);
173        InternalTypeEnumeration internalTypeEnumeration =
174            new InternalTypeEnumeration(descriptor);
175
176        while (internalTypeEnumeration.hasMoreTypes())
177        {
178            String type = internalTypeEnumeration.nextType();
179            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
180            {
181                annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
182                annotations[newAnnotationIndex++]     = annotations[annotationIndex];
183            }
184
185            annotationIndex++;
186
187            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
188        }
189
190        // Update the number of parameters.
191        parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;
192
193        // Clear the unused entries.
194        while (newAnnotationIndex < annotationIndex)
195        {
196            annotationsCounts[newAnnotationIndex] = 0;
197            annotations[newAnnotationIndex++]     = null;
198        }
199    }
200
201
202    // Small utility methods.
203
204    /**
205     * Returns a shrunk descriptor or signature of the given method.
206     */
207    private String shrinkDescriptor(Method method, String descriptor)
208    {
209        // All parameters of non-static methods are shifted by one in the local
210        // variable frame.
211        int parameterIndex =
212            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
213                0 : 1;
214
215        // Go over the parameters.
216        InternalTypeEnumeration internalTypeEnumeration =
217            new InternalTypeEnumeration(descriptor);
218
219        StringBuffer newDescriptorBuffer = new StringBuffer();
220
221        newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters());
222        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
223
224        while (internalTypeEnumeration.hasMoreTypes())
225        {
226            String type = internalTypeEnumeration.nextType();
227            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
228            {
229                newDescriptorBuffer.append(type);
230            }
231            else if (DEBUG)
232            {
233                System.out.println("  Deleting parameter #"+parameterIndex+" ["+type+"]");
234            }
235
236            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
237        }
238
239        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
240        newDescriptorBuffer.append(internalTypeEnumeration.returnType());
241
242        return newDescriptorBuffer.toString();
243    }
244
245
246    /**
247     * Shrinks the array of referenced classes of the given method.
248     */
249    private Clazz[] shrinkReferencedClasses(Method  method,
250                                            String  descriptor,
251                                            Clazz[] referencedClasses)
252    {
253        if (referencedClasses != null)
254        {
255            // All parameters of non-static methods are shifted by one in the local
256            // variable frame.
257            int parameterIndex =
258                (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
259                    0 : 1;
260
261            int referencedClassIndex    = 0;
262            int newReferencedClassIndex = 0;
263
264            // Go over the parameters.
265            InternalTypeEnumeration internalTypeEnumeration =
266                new InternalTypeEnumeration(descriptor);
267
268            // Also look at the formal type parameters.
269            String type  = internalTypeEnumeration.formalTypeParameters();
270            int    count = new DescriptorClassEnumeration(type).classCount();
271            for (int counter = 0; counter < count; counter++)
272            {
273                referencedClasses[newReferencedClassIndex++] =
274                    referencedClasses[referencedClassIndex++];
275            }
276
277            while (internalTypeEnumeration.hasMoreTypes())
278            {
279                // Consider the classes referenced by this parameter type.
280                type  = internalTypeEnumeration.nextType();
281                count = new DescriptorClassEnumeration(type).classCount();
282
283                if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
284                {
285                    // Copy the referenced classes.
286                    for (int counter = 0; counter < count; counter++)
287                    {
288                        referencedClasses[newReferencedClassIndex++] =
289                            referencedClasses[referencedClassIndex++];
290                    }
291                }
292                else
293                {
294                    // Skip the referenced classes.
295                    referencedClassIndex += count;
296                }
297
298                parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
299            }
300
301            // Also look at the return value.
302            type  = internalTypeEnumeration.returnType();
303            count = new DescriptorClassEnumeration(type).classCount();
304            for (int counter = 0; counter < count; counter++)
305            {
306                referencedClasses[newReferencedClassIndex++] =
307                    referencedClasses[referencedClassIndex++];
308            }
309
310            // Clear the unused entries.
311            while (newReferencedClassIndex < referencedClassIndex)
312            {
313                referencedClasses[newReferencedClassIndex++] = null;
314            }
315        }
316
317        return referencedClasses;
318    }
319}
320