001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.optimize;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.*;
025: import proguard.classfile.attribute.annotation.*;
026: import proguard.classfile.attribute.visitor.AttributeVisitor;
027: import proguard.classfile.editor.ConstantPoolEditor;
028: import proguard.classfile.util.*;
029: import proguard.classfile.visitor.MemberVisitor;
030: import proguard.optimize.info.*;
031: import proguard.optimize.peephole.VariableShrinker;
032:
033: /**
034: * This MemberVisitor removes unused parameters in the descriptors of the
035: * methods that it visits.
036: *
037: * @see ParameterUsageMarker
038: * @see VariableUsageMarker
039: * @see VariableShrinker
040: * @author Eric Lafortune
041: */
042: public class MethodDescriptorShrinker extends SimplifiedVisitor
043: implements MemberVisitor, AttributeVisitor {
044: private static final boolean DEBUG = false;
045:
046: private final MemberVisitor extraParameterMemberVisitor;
047:
048: private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
049:
050: /**
051: * Creates a new MethodDescriptorShrinker.
052: */
053: public MethodDescriptorShrinker() {
054: this (null);
055: }
056:
057: /**
058: * Creates a new MethodDescriptorShrinker with an extra visitor.
059: * @param extraParameterMemberVisitor an optional extra visitor for all
060: * methods whose parameters have been
061: * simplified.
062: */
063: public MethodDescriptorShrinker(
064: MemberVisitor extraParameterMemberVisitor) {
065: this .extraParameterMemberVisitor = extraParameterMemberVisitor;
066: }
067:
068: // Implementations for MemberVisitor.
069:
070: public void visitProgramMethod(ProgramClass programClass,
071: ProgramMethod programMethod) {
072: // Update the descriptor if it has any unused parameters.
073: String descriptor = programMethod.getDescriptor(programClass);
074: String newDescriptor = shrinkDescriptor(programMethod,
075: descriptor);
076:
077: if (!descriptor.equals(newDescriptor)) {
078: // Shrink the signature and parameter annotations.
079: programMethod.attributesAccept(programClass, this );
080:
081: String name = programMethod.getName(programClass);
082: String newName = name;
083:
084: // Append a code, if the method isn't a class instance initializer.
085: if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) {
086: newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR
087: + Long.toHexString(Math.abs((descriptor)
088: .hashCode()));
089: }
090:
091: if (DEBUG) {
092: System.out.println("MethodDescriptorShrinker:");
093: System.out.println(" Class file = "
094: + programClass.getName());
095: System.out.println(" Method name = " + name);
096: System.out.println(" -> " + newName);
097: System.out.println(" Method descriptor = "
098: + descriptor);
099: System.out.println(" -> "
100: + newDescriptor);
101: }
102:
103: // Update the name, if necessary.
104: if (!newName.equals(name)) {
105: programMethod.u2nameIndex = constantPoolEditor
106: .addUtf8Constant(programClass, newName);
107: }
108:
109: // Update the referenced classes.
110: programMethod.referencedClasses = shrinkReferencedClasses(
111: programMethod, descriptor,
112: programMethod.referencedClasses);
113:
114: // Finally, update the descriptor itself.
115: programMethod.u2descriptorIndex = constantPoolEditor
116: .addUtf8Constant(programClass, newDescriptor);
117:
118: // Visit the method, if required.
119: if (extraParameterMemberVisitor != null) {
120: extraParameterMemberVisitor.visitProgramMethod(
121: programClass, programMethod);
122: }
123: }
124: }
125:
126: // Implementations for AttributeVisitor.
127:
128: public void visitAnyAttribute(Clazz clazz, Attribute attribute) {
129: }
130:
131: public void visitSignatureAttribute(Clazz clazz, Method method,
132: SignatureAttribute signatureAttribute) {
133: // Compute the new signature.
134: String signature = clazz
135: .getString(signatureAttribute.u2signatureIndex);
136: String newSignature = shrinkDescriptor(method, signature);
137:
138: // Update the signature.
139: signatureAttribute.u2signatureIndex = constantPoolEditor
140: .addUtf8Constant((ProgramClass) clazz, newSignature);
141:
142: // Update the referenced classes.
143: signatureAttribute.referencedClasses = shrinkReferencedClasses(
144: method, signature, signatureAttribute.referencedClasses);
145: }
146:
147: public void visitAnyParameterAnnotationsAttribute(Clazz clazz,
148: Method method,
149: ParameterAnnotationsAttribute parameterAnnotationsAttribute) {
150: int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
151: Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations;
152:
153: // All parameters of non-static methods are shifted by one in the local
154: // variable frame.
155: int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0
156: : 1;
157:
158: int annotationIndex = 0;
159: int newAnnotationIndex = 0;
160:
161: // Go over the parameters.
162: String descriptor = method.getDescriptor(clazz);
163: InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(
164: descriptor);
165:
166: while (internalTypeEnumeration.hasMoreTypes()) {
167: String type = internalTypeEnumeration.nextType();
168: if (ParameterUsageMarker.isParameterUsed(method,
169: parameterIndex)) {
170: annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
171: annotations[newAnnotationIndex++] = annotations[annotationIndex];
172: }
173:
174: annotationIndex++;
175:
176: parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2
177: : 1;
178: }
179:
180: // Update the number of parameters.
181: parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;
182:
183: // Clear the unused entries.
184: while (newAnnotationIndex < annotationIndex) {
185: annotationsCounts[newAnnotationIndex] = 0;
186: annotations[newAnnotationIndex++] = null;
187: }
188: }
189:
190: // Small utility methods.
191:
192: /**
193: * Returns a shrunk descriptor or signature of the given method.
194: */
195: private String shrinkDescriptor(Method method, String descriptor) {
196: // All parameters of non-static methods are shifted by one in the local
197: // variable frame.
198: int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0
199: : 1;
200:
201: // Go over the parameters.
202: InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(
203: descriptor);
204:
205: StringBuffer newDescriptorBuffer = new StringBuffer();
206: newDescriptorBuffer
207: .append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
208:
209: while (internalTypeEnumeration.hasMoreTypes()) {
210: String type = internalTypeEnumeration.nextType();
211: if (ParameterUsageMarker.isParameterUsed(method,
212: parameterIndex)) {
213: newDescriptorBuffer.append(type);
214: } else if (DEBUG) {
215: System.out.println(" Deleting parameter #"
216: + parameterIndex + " [" + type + "]");
217: }
218:
219: parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2
220: : 1;
221: }
222:
223: newDescriptorBuffer
224: .append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
225: newDescriptorBuffer
226: .append(internalTypeEnumeration.returnType());
227:
228: return newDescriptorBuffer.toString();
229: }
230:
231: /**
232: * Shrinks the array of referenced classes of the given method.
233: */
234: private Clazz[] shrinkReferencedClasses(Method method,
235: String descriptor, Clazz[] referencedClasses) {
236: if (referencedClasses != null) {
237: // All parameters of non-static methods are shifted by one in the local
238: // variable frame.
239: int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0
240: : 1;
241:
242: int referencedClassIndex = 0;
243: int newReferencedClassIndex = 0;
244:
245: // Go over the parameters.
246: InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(
247: descriptor);
248:
249: while (internalTypeEnumeration.hasMoreTypes()) {
250: // Consider the classes referenced by this parameter type.
251: String type = internalTypeEnumeration.nextType();
252: int classCount = new DescriptorClassEnumeration(type)
253: .classCount();
254:
255: if (ParameterUsageMarker.isParameterUsed(method,
256: parameterIndex)) {
257: // Copy the referenced classes.
258: for (int counter = 0; counter < classCount; counter++) {
259: referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
260: }
261: } else {
262: // Skip the referenced classes.
263: referencedClassIndex += classCount;
264: }
265:
266: parameterIndex += ClassUtil
267: .isInternalCategory2Type(type) ? 2 : 1;
268: }
269:
270: // Also look at the return value.
271: String type = internalTypeEnumeration.returnType();
272: int count = new DescriptorClassEnumeration(type)
273: .classCount();
274: for (int counter = 0; counter < count; counter++) {
275: referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
276: }
277:
278: // Clear the unused entries.
279: while (newReferencedClassIndex < referencedClassIndex) {
280: referencedClasses[newReferencedClassIndex++] = null;
281: }
282: }
283:
284: return referencedClasses;
285: }
286: }
|