1
/* $Id: ClassFileReferenceFixer.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.util.*;
27
import proguard.classfile.visitor.*;
30
* This ClassFileVisitor fixes constant pool references to classes whose
31
* names have changed. Descriptors of member references are not updated yet.
33
* @see MemberReferenceFixer
34
* @author Eric Lafortune
36
public class ClassFileReferenceFixer
37
implements ClassFileVisitor,
41
LocalVariableInfoVisitor,
42
LocalVariableTypeInfoVisitor,
46
private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
49
// Implementations for ClassFileVisitor.
51
public void visitProgramClassFile(ProgramClassFile programClassFile)
53
// Fix the constant pool.
54
programClassFile.constantPoolEntriesAccept(this);
57
programClassFile.fieldsAccept(this);
58
programClassFile.methodsAccept(this);
60
// Fix the attributes.
61
programClassFile.attributesAccept(this);
65
public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
68
libraryClassFile.fieldsAccept(this);
69
libraryClassFile.methodsAccept(this);
73
// Implementations for MemberInfoVisitor.
75
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
77
// Has the descriptor changed?
78
String descriptor = programFieldInfo.getDescriptor(programClassFile);
79
String newDescriptor = newDescriptor(descriptor,
80
programFieldInfo.referencedClassFile);
82
if (!descriptor.equals(newDescriptor))
84
// Update the descriptor.
85
programFieldInfo.u2descriptorIndex =
86
constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
89
// Fix the attributes.
90
programFieldInfo.attributesAccept(programClassFile, this);
94
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
96
// Has the descriptor changed?
97
String descriptor = programMethodInfo.getDescriptor(programClassFile);
98
String newDescriptor = newDescriptor(descriptor,
99
programMethodInfo.referencedClassFiles);
101
if (!descriptor.equals(newDescriptor))
103
// Update the descriptor.
104
programMethodInfo.u2descriptorIndex =
105
constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
108
// Fix the attributes.
109
programMethodInfo.attributesAccept(programClassFile, this);
113
public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
115
// Has the descriptor changed?
116
String descriptor = libraryFieldInfo.getDescriptor(libraryClassFile);
117
String newDescriptor = newDescriptor(descriptor,
118
libraryFieldInfo.referencedClassFile);
120
// Update the descriptor.
121
libraryFieldInfo.descriptor = newDescriptor;
125
public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
127
// Has the descriptor changed?
128
String descriptor = libraryMethodInfo.getDescriptor(libraryClassFile);
129
String newDescriptor = newDescriptor(descriptor,
130
libraryMethodInfo.referencedClassFiles);
132
// Update the descriptor.
133
libraryMethodInfo.descriptor = newDescriptor;
137
// Implementations for CpInfoVisitor.
139
public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
140
public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
141
public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
142
public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
143
public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
144
public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
145
public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
146
public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
147
public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
150
public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
152
// Does the string refer to a class file, due to a Class.forName construct?
153
ClassFile referencedClassFile = stringCpInfo.referencedClassFile;
154
if (referencedClassFile != null)
156
// Reconstruct the new class name.
157
String externalClassName = stringCpInfo.getString(classFile);
158
String internalClassName = ClassUtil.internalClassName(externalClassName);
159
String newInternalClassName = newClassName(internalClassName,
160
referencedClassFile);
162
// Update the String entry if required.
163
if (!newInternalClassName.equals(internalClassName))
165
String newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
167
// Refer to a new Utf8 entry.
168
stringCpInfo.u2stringIndex =
169
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
170
newExternalClassName);
176
public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
178
// Do we know the referenced class file?
179
ClassFile referencedClassFile = classCpInfo.referencedClassFile;
180
if (referencedClassFile != null)
182
// Has the class name changed?
183
String className = classCpInfo.getName(classFile);
184
String newClassName = newClassName(className, referencedClassFile);
185
if (!className.equals(newClassName))
187
// Refer to a new Utf8 entry.
188
classCpInfo.u2nameIndex =
189
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
196
// Implementations for AttrInfoVisitor.
198
public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
199
public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
200
public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
201
public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
202
public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
203
public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
204
public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
205
public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
206
public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
207
public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
210
public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
212
// Fix the attributes.
213
codeAttrInfo.attributesAccept(classFile, methodInfo, this);
217
public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
219
// Fix the types of the local variables.
220
localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
224
public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
226
// Fix the signatures of the local variables.
227
localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
231
public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
233
// Compute the new signature.
234
String signature = classFile.getCpString(signatureAttrInfo.u2signatureIndex);
235
String newSignature = newDescriptor(signature,
236
signatureAttrInfo.referencedClassFiles);
238
if (!signature.equals(newSignature))
240
signatureAttrInfo.u2signatureIndex =
241
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
247
public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
249
// Fix the annotations.
250
runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
254
public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
256
// Fix the annotations.
257
runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
261
public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
263
// Fix the annotations.
264
runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
268
public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
270
// Fix the annotations.
271
runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
275
public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
277
// Fix the annotation.
278
annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
282
// Implementations for LocalVariableInfoVisitor.
284
public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
286
// Has the descriptor changed?
287
String descriptor = classFile.getCpString(localVariableInfo.u2descriptorIndex);
288
String newDescriptor = newDescriptor(descriptor,
289
localVariableInfo.referencedClassFile);
291
if (!descriptor.equals(newDescriptor))
293
// Refer to a new Utf8 entry.
294
localVariableInfo.u2descriptorIndex =
295
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
301
// Implementations for LocalVariableTypeInfoVisitor.
303
public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
305
// Has the signature changed?
306
String signature = classFile.getCpString(localVariableTypeInfo.u2signatureIndex);
307
String newSignature = newDescriptor(signature,
308
localVariableTypeInfo.referencedClassFiles);
310
if (!signature.equals(newSignature))
312
localVariableTypeInfo.u2signatureIndex =
313
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
319
// Implementations for AnnotationVisitor.
321
public void visitAnnotation(ClassFile classFile, Annotation annotation)
323
// Compute the new type name.
324
String typeName = classFile.getCpString(annotation.u2typeIndex);
325
String newTypeName = newDescriptor(typeName,
326
annotation.referencedClassFiles);
328
if (!typeName.equals(newTypeName))
330
// Refer to a new Utf8 entry.
331
annotation.u2typeIndex =
332
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
336
// Fix the element values.
337
annotation.elementValuesAccept(classFile, this);
341
// Implementations for ElementValueVisitor.
343
public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
348
public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
350
// Compute the new type name.
351
String typeName = classFile.getCpString(enumConstantElementValue.u2typeNameIndex);
352
String newTypeName = newDescriptor(typeName,
353
enumConstantElementValue.referencedClassFiles);
355
if (!typeName.equals(newTypeName))
357
// Refer to a new Utf8 entry.
358
enumConstantElementValue.u2typeNameIndex =
359
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
365
public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
367
// Compute the new class name.
368
String className = classFile.getCpString(classElementValue.u2classInfoIndex);
369
String newClassName = newDescriptor(className,
370
classElementValue.referencedClassFiles);
372
if (!className.equals(newClassName))
374
// Refer to a new Utf8 entry.
375
classElementValue.u2classInfoIndex =
376
constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
382
public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
384
// Fix the annotation.
385
annotationElementValue.annotationAccept(classFile, this);
389
public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
391
// Fix the element values.
392
arrayElementValue.elementValuesAccept(classFile, annotation, this);
396
// Small utility methods.
398
private static String newDescriptor(String descriptor,
399
ClassFile referencedClassFile)
401
// If there is no referenced class, the descriptor won't change.
402
if (referencedClassFile == null)
407
// Unravel and reconstruct the class element of the descriptor.
408
DescriptorClassEnumeration descriptorClassEnumeration =
409
new DescriptorClassEnumeration(descriptor);
411
StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
412
newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
414
// Only if the descriptor contains a class name (e.g. with an array of
415
// primitive types), the descriptor can change.
416
if (descriptorClassEnumeration.hasMoreClassNames())
418
String className = descriptorClassEnumeration.nextClassName();
419
String fluff = descriptorClassEnumeration.nextFluff();
421
String newClassName = newClassName(className,
422
referencedClassFile);
424
newDescriptorBuffer.append(newClassName);
425
newDescriptorBuffer.append(fluff);
428
return newDescriptorBuffer.toString();
432
private static String newDescriptor(String descriptor,
433
ClassFile[] referencedClassFiles)
435
// If there are no referenced classes, the descriptor won't change.
436
if (referencedClassFiles == null ||
437
referencedClassFiles.length == 0)
442
// Unravel and reconstruct the class elements of the descriptor.
443
DescriptorClassEnumeration descriptorClassEnumeration =
444
new DescriptorClassEnumeration(descriptor);
446
StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
447
newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
450
while (descriptorClassEnumeration.hasMoreClassNames())
452
String className = descriptorClassEnumeration.nextClassName();
453
String fluff = descriptorClassEnumeration.nextFluff();
455
String newClassName = newClassName(className,
456
referencedClassFiles[index++]);
458
newDescriptorBuffer.append(newClassName);
459
newDescriptorBuffer.append(fluff);
462
return newDescriptorBuffer.toString();
467
* Returns the new class name based on the given class name and the new
468
* name of the given referenced class file. Class names of array types
469
* are handled properly.
471
private static String newClassName(String className,
472
ClassFile referencedClassFile)
474
// If there is no referenced class, the class name won't change.
475
if (referencedClassFile == null)
480
// Reconstruct the class name.
481
String newClassName = referencedClassFile.getName();
483
// Is it an array type?
484
if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
486
// Add the array prefixes and suffix "[L...;".
488
className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
490
ClassConstants.INTERNAL_TYPE_CLASS_END;