MethodDescriptorShrinker.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.optimize;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.annotation.*;
26import proguard.classfile.attribute.visitor.*;
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("  ["+programClass.getName()+"."+
99                                   name+descriptor+"] -> ["+
100                                   newName+newDescriptor+"]");
101            }
102
103            ConstantPoolEditor constantPoolEditor =
104                new ConstantPoolEditor(programClass);
105
106            // Update the name, if necessary.
107            if (!newName.equals(name))
108            {
109                programMethod.u2nameIndex =
110                    constantPoolEditor.addUtf8Constant(newName);
111            }
112
113            // Update the referenced classes.
114            programMethod.referencedClasses =
115                shrinkReferencedClasses(programMethod,
116                                        descriptor,
117                                        programMethod.referencedClasses);
118
119            // Finally, update the descriptor itself.
120            programMethod.u2descriptorIndex =
121                constantPoolEditor.addUtf8Constant(newDescriptor);
122
123            // Visit the method, if required.
124            if (extraMemberVisitor != null)
125            {
126                extraMemberVisitor.visitProgramMethod(programClass, programMethod);
127            }
128        }
129    }
130
131
132    // Implementations for AttributeVisitor.
133
134    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
135
136
137    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
138    {
139        // Compute the new signature.
140        String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
141        String newSignature = shrinkDescriptor(method, signature);
142
143        // Update the signature.
144        signatureAttribute.u2signatureIndex =
145            new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
146
147        // Update the referenced classes.
148        signatureAttribute.referencedClasses =
149            shrinkReferencedClasses(method,
150                                    signature,
151                                    signatureAttribute.referencedClasses);
152    }
153
154
155    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
156    {
157        int[]          annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
158        Annotation[][] annotations       = parameterAnnotationsAttribute.parameterAnnotations;
159
160        // All parameters of non-static methods are shifted by one in the local
161        // variable frame.
162        int parameterIndex =
163            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
164                0 : 1;
165
166        int annotationIndex    = 0;
167        int newAnnotationIndex = 0;
168
169        // Go over the parameters.
170        String descriptor = method.getDescriptor(clazz);
171        InternalTypeEnumeration internalTypeEnumeration =
172            new InternalTypeEnumeration(descriptor);
173
174        while (internalTypeEnumeration.hasMoreTypes())
175        {
176            String type = internalTypeEnumeration.nextType();
177            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
178            {
179                annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
180                annotations[newAnnotationIndex++]     = annotations[annotationIndex];
181            }
182
183            annotationIndex++;
184
185            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
186        }
187
188        // Update the number of parameters.
189        parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;
190
191        // Clear the unused entries.
192        while (newAnnotationIndex < annotationIndex)
193        {
194            annotationsCounts[newAnnotationIndex] = 0;
195            annotations[newAnnotationIndex++]     = null;
196        }
197    }
198
199
200    // Small utility methods.
201
202    /**
203     * Returns a shrunk descriptor or signature of the given method.
204     */
205    private String shrinkDescriptor(Method method, String descriptor)
206    {
207        // All parameters of non-static methods are shifted by one in the local
208        // variable frame.
209        int parameterIndex =
210            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
211                0 : 1;
212
213        // Go over the parameters.
214        InternalTypeEnumeration internalTypeEnumeration =
215            new InternalTypeEnumeration(descriptor);
216
217        StringBuffer newDescriptorBuffer = new StringBuffer();
218
219        newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters());
220        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
221
222        while (internalTypeEnumeration.hasMoreTypes())
223        {
224            String type = internalTypeEnumeration.nextType();
225            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
226            {
227                newDescriptorBuffer.append(type);
228            }
229            else if (DEBUG)
230            {
231                System.out.println("  Deleting parameter #"+parameterIndex+" ["+type+"]");
232            }
233
234            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
235        }
236
237        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
238        newDescriptorBuffer.append(internalTypeEnumeration.returnType());
239
240        return newDescriptorBuffer.toString();
241    }
242
243
244    /**
245     * Shrinks the array of referenced classes of the given method.
246     */
247    private Clazz[] shrinkReferencedClasses(Method  method,
248                                            String  descriptor,
249                                            Clazz[] referencedClasses)
250    {
251        if (referencedClasses != null)
252        {
253            // All parameters of non-static methods are shifted by one in the local
254            // variable frame.
255            int parameterIndex =
256                (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
257                    0 : 1;
258
259            int referencedClassIndex    = 0;
260            int newReferencedClassIndex = 0;
261
262            // Go over the parameters.
263            InternalTypeEnumeration internalTypeEnumeration =
264                new InternalTypeEnumeration(descriptor);
265
266            // Also look at the formal type parameters.
267            String type  = internalTypeEnumeration.formalTypeParameters();
268            int    count = new DescriptorClassEnumeration(type).classCount();
269            for (int counter = 0; counter < count; counter++)
270            {
271                referencedClasses[newReferencedClassIndex++] =
272                    referencedClasses[referencedClassIndex++];
273            }
274
275            while (internalTypeEnumeration.hasMoreTypes())
276            {
277                // Consider the classes referenced by this parameter type.
278                type  = internalTypeEnumeration.nextType();
279                count = new DescriptorClassEnumeration(type).classCount();
280
281                if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
282                {
283                    // Copy the referenced classes.
284                    for (int counter = 0; counter < count; counter++)
285                    {
286                        referencedClasses[newReferencedClassIndex++] =
287                            referencedClasses[referencedClassIndex++];
288                    }
289                }
290                else
291                {
292                    // Skip the referenced classes.
293                    referencedClassIndex += count;
294                }
295
296                parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
297            }
298
299            // Also look at the return value.
300            type  = internalTypeEnumeration.returnType();
301            count = new DescriptorClassEnumeration(type).classCount();
302            for (int counter = 0; counter < count; counter++)
303            {
304                referencedClasses[newReferencedClassIndex++] =
305                    referencedClasses[referencedClassIndex++];
306            }
307
308            // Clear the unused entries.
309            while (newReferencedClassIndex < referencedClassIndex)
310            {
311                referencedClasses[newReferencedClassIndex++] = null;
312            }
313        }
314
315        return referencedClasses;
316    }
317}
318