• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2013 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 final boolean ensureUniqueMemberNames;
54 
55 
56     /**
57      * Creates a new ClassReferenceFixer.
58      * @param ensureUniqueMemberNames specifies whether class members whose
59      *                                descriptor changes should get new, unique
60      *                                names, in order to avoid naming conflicts
61      *                                with similar methods.
62      */
ClassReferenceFixer(boolean ensureUniqueMemberNames)63     public ClassReferenceFixer(boolean ensureUniqueMemberNames)
64     {
65         this.ensureUniqueMemberNames = ensureUniqueMemberNames;
66     }
67 
68 
69     // Implementations for ClassVisitor.
70 
visitProgramClass(ProgramClass programClass)71     public void visitProgramClass(ProgramClass programClass)
72     {
73         // Fix the constant pool.
74         programClass.constantPoolEntriesAccept(this);
75 
76         // Fix class members.
77         programClass.fieldsAccept(this);
78         programClass.methodsAccept(this);
79 
80         // Fix the attributes.
81         programClass.attributesAccept(this);
82     }
83 
84 
visitLibraryClass(LibraryClass libraryClass)85     public void visitLibraryClass(LibraryClass libraryClass)
86     {
87         // Fix class members.
88         libraryClass.fieldsAccept(this);
89         libraryClass.methodsAccept(this);
90     }
91 
92 
93     // Implementations for MemberVisitor.
94 
visitProgramField(ProgramClass programClass, ProgramField programField)95     public void visitProgramField(ProgramClass programClass, ProgramField programField)
96     {
97         // Has the descriptor changed?
98         String descriptor    = programField.getDescriptor(programClass);
99         String newDescriptor = newDescriptor(descriptor,
100                                              programField.referencedClass);
101 
102         if (!descriptor.equals(newDescriptor))
103         {
104             ConstantPoolEditor constantPoolEditor =
105                 new ConstantPoolEditor(programClass);
106 
107             // Update the descriptor.
108             programField.u2descriptorIndex =
109                 constantPoolEditor.addUtf8Constant(newDescriptor);
110 
111             // Update the name, if requested.
112             if (ensureUniqueMemberNames)
113             {
114                 String name    = programField.getName(programClass);
115                 String newName = newUniqueMemberName(name, descriptor);
116                 programField.u2nameIndex =
117                     constantPoolEditor.addUtf8Constant(newName);
118             }
119         }
120 
121         // Fix the attributes.
122         programField.attributesAccept(programClass, this);
123     }
124 
125 
visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)126     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
127     {
128         // Has the descriptor changed?
129         String descriptor    = programMethod.getDescriptor(programClass);
130         String newDescriptor = newDescriptor(descriptor,
131                                              programMethod.referencedClasses);
132 
133         if (!descriptor.equals(newDescriptor))
134         {
135             ConstantPoolEditor constantPoolEditor =
136                 new ConstantPoolEditor(programClass);
137 
138             // Update the descriptor.
139             programMethod.u2descriptorIndex =
140                 constantPoolEditor.addUtf8Constant(newDescriptor);
141 
142             // Update the name, if requested.
143             if (ensureUniqueMemberNames)
144             {
145                 String name    = programMethod.getName(programClass);
146                 String newName = newUniqueMemberName(name, descriptor);
147                 programMethod.u2nameIndex =
148                     constantPoolEditor.addUtf8Constant(newName);
149             }
150         }
151 
152         // Fix the attributes.
153         programMethod.attributesAccept(programClass, this);
154     }
155 
156 
visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)157     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
158     {
159         // Has the descriptor changed?
160         String descriptor    = libraryField.getDescriptor(libraryClass);
161         String newDescriptor = newDescriptor(descriptor,
162                                              libraryField.referencedClass);
163 
164         // Update the descriptor.
165         libraryField.descriptor = newDescriptor;
166     }
167 
168 
visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)169     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
170     {
171         // Has the descriptor changed?
172         String descriptor    = libraryMethod.getDescriptor(libraryClass);
173         String newDescriptor = newDescriptor(descriptor,
174                                              libraryMethod.referencedClasses);
175 
176         // Update the descriptor.
177         libraryMethod.descriptor = newDescriptor;
178     }
179 
180 
181     // Implementations for ConstantVisitor.
182 
visitAnyConstant(Clazz clazz, Constant constant)183     public void visitAnyConstant(Clazz clazz, Constant constant) {}
184 
185 
visitStringConstant(Clazz clazz, StringConstant stringConstant)186     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
187     {
188         // Does the string refer to a class, due to a Class.forName construct?
189         Clazz  referencedClass  = stringConstant.referencedClass;
190         Member referencedMember = stringConstant.referencedMember;
191         if (referencedClass  != null &&
192             referencedMember == null)
193         {
194             // Reconstruct the new class name.
195             String externalClassName    = stringConstant.getString(clazz);
196             String internalClassName    = ClassUtil.internalClassName(externalClassName);
197             String newInternalClassName = newClassName(internalClassName,
198                                                        referencedClass);
199 
200             // Update the String entry if required.
201             if (!newInternalClassName.equals(internalClassName))
202             {
203                 String newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
204 
205                 // Refer to a new Utf8 entry.
206                 stringConstant.u2stringIndex =
207                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newExternalClassName);
208             }
209         }
210     }
211 
212 
visitClassConstant(Clazz clazz, ClassConstant classConstant)213     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
214     {
215         // Do we know the referenced class?
216         Clazz referencedClass = classConstant.referencedClass;
217         if (referencedClass != null)
218         {
219             // Has the class name changed?
220             String className    = classConstant.getName(clazz);
221             String newClassName = newClassName(className, referencedClass);
222             if (!className.equals(newClassName))
223             {
224                 // Refer to a new Utf8 entry.
225                 classConstant.u2nameIndex =
226                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
227             }
228         }
229     }
230 
231     // Implementations for AttributeVisitor.
232 
visitAnyAttribute(Clazz clazz, Attribute attribute)233     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
234 
235 
visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)236     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
237     {
238         // Fix the inner class names.
239         innerClassesAttribute.innerClassEntriesAccept(clazz, this);
240     }
241 
242 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)243     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
244     {
245         // Fix the attributes.
246         codeAttribute.attributesAccept(clazz, method, this);
247     }
248 
249 
visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)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 
visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)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 
visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)264     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
265     {
266         // Compute the new signature.
267         String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
268         String newSignature = newDescriptor(signature,
269                                             signatureAttribute.referencedClasses);
270 
271         if (!signature.equals(newSignature))
272         {
273             signatureAttribute.u2signatureIndex =
274                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
275         }
276     }
277 
278 
visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)279     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
280     {
281         // Fix the annotations.
282         annotationsAttribute.annotationsAccept(clazz, this);
283     }
284 
285 
visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)286     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
287     {
288         // Fix the annotations.
289         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
290     }
291 
292 
visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)293     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
294     {
295         // Fix the annotation.
296         annotationDefaultAttribute.defaultValueAccept(clazz, this);
297     }
298 
299 
300     // Implementations for InnerClassesInfoVisitor.
301 
visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)302     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
303     {
304         // Fix the inner class name.
305         int innerClassIndex = innerClassesInfo.u2innerClassIndex;
306         int innerNameIndex  = innerClassesInfo.u2innerNameIndex;
307         if (innerClassIndex != 0 &&
308             innerNameIndex  != 0)
309         {
310             String newInnerName = clazz.getClassName(innerClassIndex);
311             int index = newInnerName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR);
312             if (index >= 0)
313             {
314                 innerClassesInfo.u2innerNameIndex =
315                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newInnerName.substring(index + 1));
316             }
317         }
318     }
319 
320 
321     // Implementations for LocalVariableInfoVisitor.
322 
visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)323     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
324     {
325         // Has the descriptor changed?
326         String descriptor    = clazz.getString(localVariableInfo.u2descriptorIndex);
327         String newDescriptor = newDescriptor(descriptor,
328                                              localVariableInfo.referencedClass);
329 
330         if (!descriptor.equals(newDescriptor))
331         {
332             // Refer to a new Utf8 entry.
333             localVariableInfo.u2descriptorIndex =
334                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor);
335         }
336     }
337 
338     // Implementations for LocalVariableTypeInfoVisitor.
339 
visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)340     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
341     {
342         // Has the signature changed?
343         String signature    = clazz.getString(localVariableTypeInfo.u2signatureIndex);
344         String newSignature = newDescriptor(signature,
345                                             localVariableTypeInfo.referencedClasses);
346 
347         if (!signature.equals(newSignature))
348         {
349             localVariableTypeInfo.u2signatureIndex =
350                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
351         }
352     }
353 
354     // Implementations for AnnotationVisitor.
355 
visitAnnotation(Clazz clazz, Annotation annotation)356     public void visitAnnotation(Clazz clazz, Annotation annotation)
357     {
358         // Compute the new type name.
359         String typeName    = clazz.getString(annotation.u2typeIndex);
360         String newTypeName = newDescriptor(typeName,
361                                            annotation.referencedClasses);
362 
363         if (!typeName.equals(newTypeName))
364         {
365             // Refer to a new Utf8 entry.
366             annotation.u2typeIndex =
367                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
368         }
369 
370         // Fix the element values.
371         annotation.elementValuesAccept(clazz, this);
372     }
373 
374 
375     // Implementations for ElementValueVisitor.
376 
visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)377     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
378     {
379     }
380 
381 
visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)382     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
383     {
384         // Compute the new type name.
385         String typeName    = clazz.getString(enumConstantElementValue.u2typeNameIndex);
386         String newTypeName = newDescriptor(typeName,
387                                            enumConstantElementValue.referencedClasses);
388 
389         if (!typeName.equals(newTypeName))
390         {
391             // Refer to a new Utf8 entry.
392             enumConstantElementValue.u2typeNameIndex =
393                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
394         }
395     }
396 
397 
visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)398     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
399     {
400         // Compute the new class name.
401         String className    = clazz.getString(classElementValue.u2classInfoIndex);
402         String newClassName = newDescriptor(className,
403                                             classElementValue.referencedClasses);
404 
405         if (!className.equals(newClassName))
406         {
407             // Refer to a new Utf8 entry.
408             classElementValue.u2classInfoIndex =
409                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
410         }
411     }
412 
413 
visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)414     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
415     {
416         // Fix the annotation.
417         annotationElementValue.annotationAccept(clazz, this);
418     }
419 
420 
visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)421     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
422     {
423         // Fix the element values.
424         arrayElementValue.elementValuesAccept(clazz, annotation, this);
425     }
426 
427 
428     // Small utility methods.
429 
newDescriptor(String descriptor, Clazz referencedClass)430     private static String newDescriptor(String descriptor,
431                                         Clazz  referencedClass)
432     {
433         // If there is no referenced class, the descriptor won't change.
434         if (referencedClass == null)
435         {
436             return descriptor;
437         }
438 
439         // Unravel and reconstruct the class element of the descriptor.
440         DescriptorClassEnumeration descriptorClassEnumeration =
441             new DescriptorClassEnumeration(descriptor);
442 
443         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
444         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
445 
446         // Only if the descriptor contains a class name (e.g. with an array of
447         // primitive types), the descriptor can change.
448         if (descriptorClassEnumeration.hasMoreClassNames())
449         {
450             String className = descriptorClassEnumeration.nextClassName();
451             String fluff     = descriptorClassEnumeration.nextFluff();
452 
453             String newClassName = newClassName(className,
454                                                referencedClass);
455 
456             newDescriptorBuffer.append(newClassName);
457             newDescriptorBuffer.append(fluff);
458         }
459 
460         return newDescriptorBuffer.toString();
461     }
462 
463 
newDescriptor(String descriptor, Clazz[] referencedClasses)464     private static String newDescriptor(String  descriptor,
465                                         Clazz[] referencedClasses)
466     {
467         // If there are no referenced classes, the descriptor won't change.
468         if (referencedClasses == null ||
469             referencedClasses.length == 0)
470         {
471             return descriptor;
472         }
473 
474         // Unravel and reconstruct the class elements of the descriptor.
475         DescriptorClassEnumeration descriptorClassEnumeration =
476             new DescriptorClassEnumeration(descriptor);
477 
478         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
479         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
480 
481         int index = 0;
482         while (descriptorClassEnumeration.hasMoreClassNames())
483         {
484             String  className        = descriptorClassEnumeration.nextClassName();
485             boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName();
486             String  fluff            = descriptorClassEnumeration.nextFluff();
487 
488             String newClassName = newClassName(className,
489                                                referencedClasses[index++]);
490 
491             // Strip the outer class name again, if it's an inner class.
492             if (isInnerClassName)
493             {
494                 newClassName =
495                     newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1);
496             }
497 
498             newDescriptorBuffer.append(newClassName);
499             newDescriptorBuffer.append(fluff);
500         }
501 
502         return newDescriptorBuffer.toString();
503     }
504 
505 
506     /**
507      * Returns a unique class member name, based on the given name and descriptor.
508      */
newUniqueMemberName(String name, String descriptor)509     private String newUniqueMemberName(String name, String descriptor)
510     {
511         return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
512             ClassConstants.INTERNAL_METHOD_NAME_INIT :
513             name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
514     }
515 
516 
517     /**
518      * Returns the new class name based on the given class name and the new
519      * name of the given referenced class. Class names of array types
520      * are handled properly.
521      */
newClassName(String className, Clazz referencedClass)522     private static String newClassName(String className,
523                                        Clazz  referencedClass)
524     {
525         // If there is no referenced class, the class name won't change.
526         if (referencedClass == null)
527         {
528             return className;
529         }
530 
531         // Reconstruct the class name.
532         String newClassName = referencedClass.getName();
533 
534         // Is it an array type?
535         if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
536         {
537             // Add the array prefixes and suffix "[L...;".
538             newClassName =
539                  className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
540                  newClassName +
541                  ClassConstants.INTERNAL_TYPE_CLASS_END;
542         }
543 
544         return newClassName;
545     }
546 }
547