1
/* $Id: MemberReferenceFixer.java,v 1.4 2005/06/25 22:07:51 eric Exp $
3
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
5
* Copyright (c) 2002-2005 Eric Lafortune (eric@graphics.cornell.edu)
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)
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
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
21
package proguard.classfile.editor;
23
import proguard.classfile.*;
24
import proguard.classfile.attribute.*;
25
import proguard.classfile.attribute.annotation.*;
26
import proguard.classfile.visitor.*;
29
* This ClassFileVisitor fixes constant pool field and method references to
30
* fields and methods whose names or descriptors have changed.
32
* @author Eric Lafortune
34
public class MemberReferenceFixer
35
implements ClassFileVisitor,
42
private static final boolean DEBUG = false;
45
private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
46
private StackSizeUpdater stackSizeUpdater;
48
// Parameter for the visitor methods.
51
// Return values for the visitor methods.
52
private boolean isInterfaceMethod;
53
private boolean stackSizesMayHaveChanged;
57
* Creates a new MemberReferenceFixer.
58
* @param codeLength an estimate of the maximum length of all the code that
61
public MemberReferenceFixer(int codeLength)
63
stackSizeUpdater = new StackSizeUpdater(codeLength);
67
// Implementations for ClassFileVisitor.
69
public void visitProgramClassFile(ProgramClassFile programClassFile)
71
stackSizesMayHaveChanged = false;
73
// Fix the constant pool entries.
74
for (int index = 1; index < programClassFile.u2constantPoolCount; index++)
76
CpInfo cpInfo = programClassFile.constantPool[index];
79
// Fix the entry, replacing it entirely if needed.
82
cpInfo.accept(programClassFile, this);
87
programClassFile.fieldsAccept(this);
88
programClassFile.methodsAccept(this);
90
// Fix the attributes.
91
programClassFile.attributesAccept(this);
95
public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
100
// Implementations for CpInfoVisitor.
102
public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
103
public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
104
public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
105
public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
106
public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
107
public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
108
public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
111
public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
113
// Do we know the referenced field?
114
MemberInfo referencedMemberInfo = fieldrefCpInfo.referencedMemberInfo;
115
if (referencedMemberInfo != null)
117
ClassFile referencedClassFile = fieldrefCpInfo.referencedClassFile;
119
// Does it have a new name or type?
120
String newName = referencedMemberInfo.getName(referencedClassFile);
121
String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
123
if (!fieldrefCpInfo.getName(classFile).equals(newName) ||
124
!fieldrefCpInfo.getType(classFile).equals(newType))
128
debug(classFile, fieldrefCpInfo, referencedClassFile, referencedMemberInfo);
131
// Update the name and type index.
132
fieldrefCpInfo.u2nameAndTypeIndex =
133
constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
141
public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
143
// Do we know the referenced interface method?
144
MemberInfo referencedMemberInfo = interfaceMethodrefCpInfo.referencedMemberInfo;
145
if (referencedMemberInfo != null)
147
ClassFile referencedClassFile = interfaceMethodrefCpInfo.referencedClassFile;
149
// Does it have a new name or type?
150
String newName = referencedMemberInfo.getName(referencedClassFile);
151
String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
153
if (!interfaceMethodrefCpInfo.getName(classFile).equals(newName) ||
154
!interfaceMethodrefCpInfo.getType(classFile).equals(newType))
158
debug(classFile, interfaceMethodrefCpInfo, referencedClassFile, referencedMemberInfo);
161
// Update the name and type index.
162
interfaceMethodrefCpInfo.u2nameAndTypeIndex =
163
constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
167
// Remember that the stack sizes of the methods in this class
169
stackSizesMayHaveChanged = true;
172
// Check if this is an interface method.
173
isInterfaceMethod = true;
174
classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.u2classIndex, this);
176
// Has the method become a non-interface method?
177
if (!isInterfaceMethod)
181
System.out.println("MemberReferenceFixer:");
182
System.out.println(" Class file = "+classFile.getName());
183
System.out.println(" Ref class file = "+referencedClassFile.getName());
184
System.out.println(" Ref method = "+interfaceMethodrefCpInfo.getName(classFile)+interfaceMethodrefCpInfo.getType(classFile));
185
System.out.println(" -> ordinary method");
188
// Replace the interface method reference by a method reference.
189
((ProgramClassFile)classFile).constantPool[this.cpIndex] =
190
new MethodrefCpInfo(interfaceMethodrefCpInfo.u2classIndex,
191
interfaceMethodrefCpInfo.u2nameAndTypeIndex,
193
referencedMemberInfo);
199
public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
201
// Do we know the referenced method?
202
MemberInfo referencedMemberInfo = methodrefCpInfo.referencedMemberInfo;
203
if (referencedMemberInfo != null)
205
ClassFile referencedClassFile = methodrefCpInfo.referencedClassFile;
207
// Does it have a new name or type?
208
String newName = referencedMemberInfo.getName(referencedClassFile);
209
String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
211
if (!methodrefCpInfo.getName(classFile).equals(newName) ||
212
!methodrefCpInfo.getType(classFile).equals(newType))
216
debug(classFile, methodrefCpInfo, referencedClassFile, referencedMemberInfo);
219
// Update the name and type index.
220
methodrefCpInfo.u2nameAndTypeIndex =
221
constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
225
// Remember that the stack sizes of the methods in this class
227
stackSizesMayHaveChanged = true;
230
// Check if this is an interface method.
231
isInterfaceMethod = false;
232
classFile.constantPoolEntryAccept(methodrefCpInfo.u2classIndex, this);
234
// Has the method become an interface method?
235
if (isInterfaceMethod)
239
System.out.println("MemberReferenceFixer:");
240
System.out.println(" Class file = "+classFile.getName());
241
System.out.println(" Ref class file = "+referencedClassFile.getName());
242
System.out.println(" Ref method = "+methodrefCpInfo.getName(classFile)+methodrefCpInfo.getType(classFile));
243
System.out.println(" -> interface method");
246
// Replace the method reference by an interface method reference.
247
((ProgramClassFile)classFile).constantPool[this.cpIndex] =
248
new InterfaceMethodrefCpInfo(methodrefCpInfo.u2classIndex,
249
methodrefCpInfo.u2nameAndTypeIndex,
251
referencedMemberInfo);
257
public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
259
// Check if this class entry refers to an interface class.
260
ClassFile referencedClassFile = classCpInfo.referencedClassFile;
261
if (referencedClassFile != null)
263
isInterfaceMethod = (referencedClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
268
// Implementations for MemberInfoVisitor.
270
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
272
// Fix the attributes.
273
programFieldInfo.attributesAccept(programClassFile, this);
277
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
279
// Fix the attributes.
280
programMethodInfo.attributesAccept(programClassFile, this);
284
public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
285
public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
288
// Implementations for AttrInfoVisitor.
290
public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
291
public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
292
public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
293
public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
294
public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
295
public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
296
public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
297
public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
298
public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
299
public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
300
public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
301
public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
304
public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
306
MemberInfo referencedMemberInfo = enclosingMethodAttrInfo.referencedMethodInfo;
307
if (referencedMemberInfo != null)
309
ClassFile referencedClassFile = enclosingMethodAttrInfo.referencedClassFile;
311
// Does it have a new class?
312
if (!enclosingMethodAttrInfo.getClassName(classFile).equals(referencedClassFile.getName()))
314
// Update the class index.
315
enclosingMethodAttrInfo.u2classIndex =
316
constantPoolEditor.addClassCpInfo((ProgramClassFile)classFile,
317
referencedClassFile);
320
// Does it have a new name or type?
321
if (!enclosingMethodAttrInfo.getName(classFile).equals(referencedMemberInfo.getName(referencedClassFile)) ||
322
!enclosingMethodAttrInfo.getType(classFile).equals(referencedMemberInfo.getDescriptor(referencedClassFile)))
324
// Update the name and type index.
325
enclosingMethodAttrInfo.u2nameAndTypeIndex =
326
constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
327
referencedMemberInfo.getName(referencedClassFile),
328
referencedMemberInfo.getDescriptor(referencedClassFile));
334
public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
336
// Recompute the maximum stack size if necessary.
337
if (stackSizesMayHaveChanged)
339
stackSizeUpdater.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
342
// Fix the nested attributes.
343
codeAttrInfo.attributesAccept(classFile, methodInfo, this);
347
public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
349
// Fix the annotations.
350
runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
354
public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
356
// Fix the annotations.
357
runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
361
public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
363
// Fix the annotations.
364
runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
368
public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
370
// Fix the annotations.
371
runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
375
public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
377
// Fix the annotation.
378
annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
382
// Implementations for AnnotationVisitor.
384
public void visitAnnotation(ClassFile classFile, Annotation annotation)
386
// Fix the element values.
387
annotation.elementValuesAccept(classFile, this);
391
// Implementations for ElementValueVisitor.
393
public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
395
fixElementValue(classFile, annotation, constantElementValue);
399
public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
401
fixElementValue(classFile, annotation, enumConstantElementValue);
405
public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
407
fixElementValue(classFile, annotation, classElementValue);
411
public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
413
fixElementValue(classFile, annotation, annotationElementValue);
415
// Fix the annotation.
416
annotationElementValue.annotationAccept(classFile, this);
420
public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
422
fixElementValue(classFile, annotation, arrayElementValue);
424
// Fix the element values.
425
arrayElementValue.elementValuesAccept(classFile, annotation, this);
429
// Small utility methods.
432
* Fixs the method reference of the element value, if any.
434
private void fixElementValue(ClassFile classFile,
435
Annotation annotation,
436
ElementValue elementValue)
438
// Do we know the referenced method?
439
MemberInfo referencedMemberInfo = elementValue.referencedMethodInfo;
440
if (referencedMemberInfo != null)
442
// Does it have a new name or type?
443
String methodName = elementValue.getMethodName(classFile);
444
String newMethodName = referencedMemberInfo.getName(annotation.referencedClassFiles[0]);
445
if (!methodName.equals(newMethodName))
447
// Update the element name index.
448
elementValue.u2elementName =
449
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
456
private void debug(ClassFile classFile,
458
ClassFile referencedClassFile,
459
MemberInfo referencedMemberInfo)
461
System.out.println("MemberReferenceFixer:");
462
System.out.println(" Class file = "+classFile.getName());
463
System.out.println(" Ref class file = "+referencedClassFile.getName());
464
System.out.println(" Ref member name = "+refCpInfo.getName(classFile));
465
System.out.println(" -> "+referencedMemberInfo.getName(referencedClassFile));
466
System.out.println(" Ref descriptor = "+refCpInfo.getType(classFile));
467
System.out.println(" -> "+referencedMemberInfo.getDescriptor(referencedClassFile));