KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > classfile > editor > ClassReferenceFixer


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 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.classfile.editor;
22
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.annotation.*;
26 import proguard.classfile.attribute.annotation.visitor.*;
27 import proguard.classfile.attribute.visitor.*;
28 import proguard.classfile.constant.*;
29 import proguard.classfile.constant.visitor.ConstantVisitor;
30 import proguard.classfile.util.*;
31 import proguard.classfile.visitor.*;
32
33 /**
34  * This ClassVisitor fixes references of constant pool entries, fields,
35  * methods, and attributes to classes whose names have changed. Descriptors
36  * of member references are not updated yet.
37  *
38  * @see MemberReferenceFixer
39  * @author Eric Lafortune
40  */

41 public class ClassReferenceFixer
42 extends SimplifiedVisitor
43 implements ClassVisitor,
44              ConstantVisitor,
45              MemberVisitor,
46              AttributeVisitor,
47              InnerClassesInfoVisitor,
48              LocalVariableInfoVisitor,
49              LocalVariableTypeInfoVisitor,
50              AnnotationVisitor,
51              ElementValueVisitor
52 {
53     private boolean ensureUniqueMemberNames;
54
55
56     private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
57
58
59     /**
60      * Creates a new ClassReferenceFixer.
61      * @param ensureUniqueMemberNames specifies whether class members whose
62      * descriptor changes should get new, unique
63      * names, in order to avoid naming conflicts
64      * with similar methods.
65      */

66     public ClassReferenceFixer(boolean ensureUniqueMemberNames)
67     {
68         this.ensureUniqueMemberNames = ensureUniqueMemberNames;
69     }
70
71
72     // Implementations for ClassVisitor.
73

74     public void visitProgramClass(ProgramClass programClass)
75     {
76         // Fix the constant pool.
77
programClass.constantPoolEntriesAccept(this);
78
79         // Fix class members.
80
programClass.fieldsAccept(this);
81         programClass.methodsAccept(this);
82
83         // Fix the attributes.
84
programClass.attributesAccept(this);
85     }
86
87
88     public void visitLibraryClass(LibraryClass libraryClass)
89     {
90         // Fix class members.
91
libraryClass.fieldsAccept(this);
92         libraryClass.methodsAccept(this);
93     }
94
95
96     // Implementations for MemberVisitor.
97

98     public void visitProgramField(ProgramClass programClass, ProgramField programField)
99     {
100         // Has the descriptor changed?
101
String JavaDoc descriptor = programField.getDescriptor(programClass);
102         String JavaDoc newDescriptor = newDescriptor(descriptor,
103                                              programField.referencedClass);
104
105         if (!descriptor.equals(newDescriptor))
106         {
107             // Update the descriptor.
108
programField.u2descriptorIndex =
109                 constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
110
111             // Update the name, if requested.
112
if (ensureUniqueMemberNames)
113             {
114                 String JavaDoc name = programField.getName(programClass);
115                 String JavaDoc newName = newUniqueMemberName(name, descriptor);
116                 programField.u2nameIndex =
117                     constantPoolEditor.addUtf8Constant(programClass, newName);
118             }
119         }
120
121         // Fix the attributes.
122
programField.attributesAccept(programClass, this);
123     }
124
125
126     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
127     {
128         // Has the descriptor changed?
129
String JavaDoc descriptor = programMethod.getDescriptor(programClass);
130         String JavaDoc newDescriptor = newDescriptor(descriptor,
131                                              programMethod.referencedClasses);
132
133         if (!descriptor.equals(newDescriptor))
134         {
135             // Update the descriptor.
136
programMethod.u2descriptorIndex =
137                 constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
138
139             // Update the name, if requested.
140
if (ensureUniqueMemberNames)
141             {
142                 String JavaDoc name = programMethod.getName(programClass);
143                 String JavaDoc newName = newUniqueMemberName(name, descriptor);
144                 programMethod.u2nameIndex =
145                     constantPoolEditor.addUtf8Constant(programClass, newName);
146             }
147         }
148
149         // Fix the attributes.
150
programMethod.attributesAccept(programClass, this);
151     }
152
153
154     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
155     {
156         // Has the descriptor changed?
157
String JavaDoc descriptor = libraryField.getDescriptor(libraryClass);
158         String JavaDoc newDescriptor = newDescriptor(descriptor,
159                                              libraryField.referencedClass);
160
161         // Update the descriptor.
162
libraryField.descriptor = newDescriptor;
163     }
164
165
166     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
167     {
168         // Has the descriptor changed?
169
String JavaDoc descriptor = libraryMethod.getDescriptor(libraryClass);
170         String JavaDoc newDescriptor = newDescriptor(descriptor,
171                                              libraryMethod.referencedClasses);
172
173         // Update the descriptor.
174
libraryMethod.descriptor = newDescriptor;
175     }
176
177
178     // Implementations for ConstantVisitor.
179

180     public void visitAnyConstant(Clazz clazz, Constant constant) {}
181
182
183     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
184     {
185         // Does the string refer to a class, due to a Class.forName construct?
186
Clazz referencedClass = stringConstant.referencedClass;
187         Member referencedMember = stringConstant.referencedMember;
188         if (referencedClass != null &&
189             referencedMember == null)
190         {
191             // Reconstruct the new class name.
192
String JavaDoc externalClassName = stringConstant.getString(clazz);
193             String JavaDoc internalClassName = ClassUtil.internalClassName(externalClassName);
194             String JavaDoc newInternalClassName = newClassName(internalClassName,
195                                                        referencedClass);
196
197             // Update the String entry if required.
198
if (!newInternalClassName.equals(internalClassName))
199             {
200                 String JavaDoc newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
201
202                 // Refer to a new Utf8 entry.
203
stringConstant.u2stringIndex =
204                     constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
205                                                        newExternalClassName);
206             }
207         }
208     }
209
210
211     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
212     {
213         // Do we know the referenced class?
214
Clazz referencedClass = classConstant.referencedClass;
215         if (referencedClass != null)
216         {
217             // Has the class name changed?
218
String JavaDoc className = classConstant.getName(clazz);
219             String JavaDoc newClassName = newClassName(className, referencedClass);
220             if (!className.equals(newClassName))
221             {
222                 // Refer to a new Utf8 entry.
223
classConstant.u2nameIndex =
224                     constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
225                                                        newClassName);
226             }
227         }
228     }
229
230
231     // Implementations for AttributeVisitor.
232

233     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
234
235
236     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
237     {
238         // Fix the inner class names.
239
innerClassesAttribute.innerClassEntriesAccept(clazz, this);
240     }
241
242
243     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
244     {
245         // Fix the attributes.
246
codeAttribute.attributesAccept(clazz, method, this);
247     }
248
249
250     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
251     {
252         // Fix the types of the local variables.
253
localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
254     }
255
256
257     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
258     {
259         // Fix the signatures of the local variables.
260
localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
261     }
262
263
264     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
265     {
266         // Compute the new signature.
267
String JavaDoc signature = clazz.getString(signatureAttribute.u2signatureIndex);
268         String JavaDoc newSignature = newDescriptor(signature,
269                                             signatureAttribute.referencedClasses);
270
271         if (!signature.equals(newSignature))
272         {
273             signatureAttribute.u2signatureIndex =
274                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
275                                                    newSignature);
276         }
277     }
278
279
280     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
281     {
282         // Fix the annotations.
283
annotationsAttribute.annotationsAccept(clazz, this);
284     }
285
286
287     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
288     {
289         // Fix the annotations.
290
parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
291     }
292
293
294     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
295     {
296         // Fix the annotation.
297
annotationDefaultAttribute.defaultValueAccept(clazz, this);
298     }
299
300
301     // Implementations for InnerClassesInfoVisitor.
302

303     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
304     {
305         // Fix the inner class name.
306
int innerClassIndex = innerClassesInfo.u2innerClassIndex;
307         int innerNameIndex = innerClassesInfo.u2innerNameIndex;
308         if (innerClassIndex != 0 &&
309             innerNameIndex != 0)
310         {
311             String JavaDoc newInnerName = clazz.getClassName(innerClassIndex);
312             int index = newInnerName.lastIndexOf(ClassConstants.INNER_CLASS_SEPARATOR);
313             if (index >= 0)
314             {
315                 innerClassesInfo.u2innerNameIndex =
316                     constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
317                                                        newInnerName.substring(index + 1));
318             }
319         }
320     }
321
322
323     // Implementations for LocalVariableInfoVisitor.
324

325     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
326     {
327         // Has the descriptor changed?
328
String JavaDoc descriptor = clazz.getString(localVariableInfo.u2descriptorIndex);
329         String JavaDoc newDescriptor = newDescriptor(descriptor,
330                                              localVariableInfo.referencedClass);
331
332         if (!descriptor.equals(newDescriptor))
333         {
334             // Refer to a new Utf8 entry.
335
localVariableInfo.u2descriptorIndex =
336                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
337                                                    newDescriptor);
338         }
339     }
340
341
342     // Implementations for LocalVariableTypeInfoVisitor.
343

344     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
345     {
346         // Has the signature changed?
347
String JavaDoc signature = clazz.getString(localVariableTypeInfo.u2signatureIndex);
348         String JavaDoc newSignature = newDescriptor(signature,
349                                             localVariableTypeInfo.referencedClasses);
350
351         if (!signature.equals(newSignature))
352         {
353             localVariableTypeInfo.u2signatureIndex =
354                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
355                                                    newSignature);
356         }
357     }
358
359
360     // Implementations for AnnotationVisitor.
361

362     public void visitAnnotation(Clazz clazz, Annotation annotation)
363     {
364         // Compute the new type name.
365
String JavaDoc typeName = clazz.getString(annotation.u2typeIndex);
366         String JavaDoc newTypeName = newDescriptor(typeName,
367                                            annotation.referencedClasses);
368
369         if (!typeName.equals(newTypeName))
370         {
371             // Refer to a new Utf8 entry.
372
annotation.u2typeIndex =
373                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
374                                                    newTypeName);
375         }
376
377         // Fix the element values.
378
annotation.elementValuesAccept(clazz, this);
379     }
380
381
382     // Implementations for ElementValueVisitor.
383

384     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
385     {
386     }
387
388
389     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
390     {
391         // Compute the new type name.
392
String JavaDoc typeName = clazz.getString(enumConstantElementValue.u2typeNameIndex);
393         String JavaDoc newTypeName = newDescriptor(typeName,
394                                            enumConstantElementValue.referencedClasses);
395
396         if (!typeName.equals(newTypeName))
397         {
398             // Refer to a new Utf8 entry.
399
enumConstantElementValue.u2typeNameIndex =
400                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
401                                                    newTypeName);
402         }
403     }
404
405
406     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
407     {
408         // Compute the new class name.
409
String JavaDoc className = clazz.getString(classElementValue.u2classInfoIndex);
410         String JavaDoc newClassName = newDescriptor(className,
411                                             classElementValue.referencedClasses);
412
413         if (!className.equals(newClassName))
414         {
415             // Refer to a new Utf8 entry.
416
classElementValue.u2classInfoIndex =
417                 constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
418                                                    newClassName);
419         }
420     }
421
422
423     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
424     {
425         // Fix the annotation.
426
annotationElementValue.annotationAccept(clazz, this);
427     }
428
429
430     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
431     {
432         // Fix the element values.
433
arrayElementValue.elementValuesAccept(clazz, annotation, this);
434     }
435
436
437     // Small utility methods.
438

439     private static String JavaDoc newDescriptor(String JavaDoc descriptor,
440                                         Clazz referencedClass)
441     {
442         // If there is no referenced class, the descriptor won't change.
443
if (referencedClass == null)
444         {
445             return descriptor;
446         }
447
448         // Unravel and reconstruct the class element of the descriptor.
449
DescriptorClassEnumeration descriptorClassEnumeration =
450             new DescriptorClassEnumeration(descriptor);
451
452         StringBuffer JavaDoc newDescriptorBuffer = new StringBuffer JavaDoc(descriptor.length());
453         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
454
455         // Only if the descriptor contains a class name (e.g. with an array of
456
// primitive types), the descriptor can change.
457
if (descriptorClassEnumeration.hasMoreClassNames())
458         {
459             String JavaDoc className = descriptorClassEnumeration.nextClassName();
460             String JavaDoc fluff = descriptorClassEnumeration.nextFluff();
461
462             String JavaDoc newClassName = newClassName(className,
463                                                referencedClass);
464
465             newDescriptorBuffer.append(newClassName);
466             newDescriptorBuffer.append(fluff);
467         }
468
469         return newDescriptorBuffer.toString();
470     }
471
472
473     private static String JavaDoc newDescriptor(String JavaDoc descriptor,
474                                         Clazz[] referencedClasses)
475     {
476         // If there are no referenced classes, the descriptor won't change.
477
if (referencedClasses == null ||
478             referencedClasses.length == 0)
479         {
480             return descriptor;
481         }
482
483         // Unravel and reconstruct the class elements of the descriptor.
484
DescriptorClassEnumeration descriptorClassEnumeration =
485             new DescriptorClassEnumeration(descriptor);
486
487         StringBuffer JavaDoc newDescriptorBuffer = new StringBuffer JavaDoc(descriptor.length());
488         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
489
490         int index = 0;
491         while (descriptorClassEnumeration.hasMoreClassNames())
492         {
493             String JavaDoc className = descriptorClassEnumeration.nextClassName();
494             String JavaDoc fluff = descriptorClassEnumeration.nextFluff();
495
496             String JavaDoc newClassName = newClassName(className,
497                                                referencedClasses[index++]);
498
499             newDescriptorBuffer.append(newClassName);
500             newDescriptorBuffer.append(fluff);
501         }
502
503         return newDescriptorBuffer.toString();
504     }
505
506
507     /**
508      * Returns a unique class member name, based on the given name and descriptor.
509      */

510     private String JavaDoc newUniqueMemberName(String JavaDoc name, String JavaDoc descriptor)
511     {
512         // TODO: Avoid duplicate constructors.
513
return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
514             ClassConstants.INTERNAL_METHOD_NAME_INIT :
515             name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
516     }
517
518
519     /**
520      * Returns the new class name based on the given class name and the new
521      * name of the given referenced class. Class names of array types
522      * are handled properly.
523      */

524     private static String JavaDoc newClassName(String JavaDoc className,
525                                        Clazz referencedClass)
526     {
527         // If there is no referenced class, the class name won't change.
528
if (referencedClass == null)
529         {
530             return className;
531         }
532
533         // Reconstruct the class name.
534
String JavaDoc newClassName = referencedClass.getName();
535
536         // Is it an array type?
537
if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
538         {
539             // Add the array prefixes and suffix "[L...;".
540
newClassName =
541                  className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
542                  newClassName +
543                  ClassConstants.INTERNAL_TYPE_CLASS_END;
544         }
545
546         return newClassName;
547     }
548 }
549
Popular Tags