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