• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 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         if (!descriptor.equals(newDescriptor))
177         {
178             // Update the descriptor.
179             libraryMethod.descriptor = newDescriptor;
180         }
181     }
182 
183 
184     // Implementations for ConstantVisitor.
185 
visitAnyConstant(Clazz clazz, Constant constant)186     public void visitAnyConstant(Clazz clazz, Constant constant) {}
187 
188 
visitStringConstant(Clazz clazz, StringConstant stringConstant)189     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
190     {
191         // Does the string refer to a class, due to a Class.forName construct?
192         Clazz  referencedClass  = stringConstant.referencedClass;
193         Member referencedMember = stringConstant.referencedMember;
194         if (referencedClass  != null &&
195             referencedMember == null)
196         {
197             // Reconstruct the new class name.
198             String externalClassName    = stringConstant.getString(clazz);
199             String internalClassName    = ClassUtil.internalClassName(externalClassName);
200             String newInternalClassName = newClassName(internalClassName,
201                                                        referencedClass);
202 
203             // Update the String entry if required.
204             if (!newInternalClassName.equals(internalClassName))
205             {
206                 // Only convert to an external class name if the original was
207                 // an external class name too.
208                 String newExternalClassName =
209                     externalClassName.indexOf(JavaConstants.PACKAGE_SEPARATOR) >= 0 ?
210                         ClassUtil.externalClassName(newInternalClassName) :
211                         newInternalClassName;
212 
213                 // Refer to a new Utf8 entry.
214                 stringConstant.u2stringIndex =
215                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newExternalClassName);
216             }
217         }
218     }
219 
220 
visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)221     public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
222     {
223         // Has the descriptor changed?
224         String descriptor    = invokeDynamicConstant.getType(clazz);
225         String newDescriptor = newDescriptor(descriptor,
226                                              invokeDynamicConstant.referencedClasses);
227 
228         if (!descriptor.equals(newDescriptor))
229         {
230             String name = invokeDynamicConstant.getName(clazz);
231 
232             // Refer to a new NameAndType entry.
233             invokeDynamicConstant.u2nameAndTypeIndex =
234                 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(name, newDescriptor);
235         }
236     }
237 
238 
visitClassConstant(Clazz clazz, ClassConstant classConstant)239     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
240     {
241         // Do we know the referenced class?
242         Clazz referencedClass = classConstant.referencedClass;
243         if (referencedClass != null)
244         {
245             // Has the class name changed?
246             String className    = classConstant.getName(clazz);
247             String newClassName = newClassName(className, referencedClass);
248             if (!className.equals(newClassName))
249             {
250                 // Refer to a new Utf8 entry.
251                 classConstant.u2nameIndex =
252                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
253             }
254         }
255     }
256 
257 
visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)258     public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
259     {
260         // Has the descriptor changed?
261         String descriptor    = methodTypeConstant.getType(clazz);
262         String newDescriptor = newDescriptor(descriptor,
263                                              methodTypeConstant.referencedClasses);
264 
265         if (!descriptor.equals(newDescriptor))
266         {
267             // Update the descriptor.
268             methodTypeConstant.u2descriptorIndex =
269                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor);
270         }
271     }
272 
273 
274     // Implementations for AttributeVisitor.
275 
visitAnyAttribute(Clazz clazz, Attribute attribute)276     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
277 
278 
visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)279     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
280     {
281         // Fix the inner class names.
282         innerClassesAttribute.innerClassEntriesAccept(clazz, this);
283     }
284 
285 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)286     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
287     {
288         // Fix the attributes.
289         codeAttribute.attributesAccept(clazz, method, this);
290     }
291 
292 
visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)293     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
294     {
295         // Fix the types of the local variables.
296         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
297     }
298 
299 
visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)300     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
301     {
302         // Fix the signatures of the local variables.
303         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
304     }
305 
306 
visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)307     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
308     {
309         // Has the signature changed?
310         String signature    = signatureAttribute.getSignature(clazz);
311         String newSignature = newDescriptor(signature,
312                                             signatureAttribute.referencedClasses);
313 
314         if (!signature.equals(newSignature))
315         {
316             // Update the signature.
317             signatureAttribute.u2signatureIndex =
318                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
319         }
320     }
321 
322 
visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)323     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
324     {
325         // Fix the annotations.
326         annotationsAttribute.annotationsAccept(clazz, this);
327     }
328 
329 
visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)330     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
331     {
332         // Fix the annotations.
333         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
334     }
335 
336 
visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)337     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
338     {
339         // Fix the annotation.
340         annotationDefaultAttribute.defaultValueAccept(clazz, this);
341     }
342 
343 
344     // Implementations for InnerClassesInfoVisitor.
345 
visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)346     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
347     {
348         // Fix the inner class name.
349         int innerClassIndex = innerClassesInfo.u2innerClassIndex;
350         int innerNameIndex  = innerClassesInfo.u2innerNameIndex;
351         if (innerClassIndex != 0 &&
352             innerNameIndex  != 0)
353         {
354             String newInnerName = clazz.getClassName(innerClassIndex);
355             int index = newInnerName.lastIndexOf(ClassConstants.INNER_CLASS_SEPARATOR);
356             if (index >= 0)
357             {
358                 innerClassesInfo.u2innerNameIndex =
359                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newInnerName.substring(index + 1));
360             }
361         }
362     }
363 
364 
365     // Implementations for LocalVariableInfoVisitor.
366 
visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)367     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
368     {
369         // Has the descriptor changed?
370         String descriptor    = localVariableInfo.getDescriptor(clazz);
371         String newDescriptor = newDescriptor(descriptor,
372                                              localVariableInfo.referencedClass);
373 
374         if (!descriptor.equals(newDescriptor))
375         {
376             // Refer to a new Utf8 entry.
377             localVariableInfo.u2descriptorIndex =
378                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor);
379         }
380     }
381 
382     // Implementations for LocalVariableTypeInfoVisitor.
383 
visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)384     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
385     {
386         // Has the signature changed?
387         String signature    = localVariableTypeInfo.getSignature(clazz);
388         String newSignature = newDescriptor(signature,
389                                             localVariableTypeInfo.referencedClasses);
390 
391         if (!signature.equals(newSignature))
392         {
393             // Update the signature.
394             localVariableTypeInfo.u2signatureIndex =
395                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
396         }
397     }
398 
399     // Implementations for AnnotationVisitor.
400 
visitAnnotation(Clazz clazz, Annotation annotation)401     public void visitAnnotation(Clazz clazz, Annotation annotation)
402     {
403         // Has the type changed?
404         String typeName    = annotation.getType(clazz);
405         String newTypeName = newDescriptor(typeName,
406                                            annotation.referencedClasses);
407 
408         if (!typeName.equals(newTypeName))
409         {
410             // Update the type.
411             annotation.u2typeIndex =
412                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
413         }
414 
415         // Fix the element values.
416         annotation.elementValuesAccept(clazz, this);
417     }
418 
419 
420     // Implementations for ElementValueVisitor.
421 
visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)422     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
423     {
424     }
425 
426 
visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)427     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
428     {
429         // Has the type name chamged?
430         String typeName    = enumConstantElementValue.getTypeName(clazz);
431         String newTypeName = newDescriptor(typeName,
432                                            enumConstantElementValue.referencedClasses);
433 
434         if (!typeName.equals(newTypeName))
435         {
436             // Update the type name.
437             enumConstantElementValue.u2typeNameIndex =
438                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
439         }
440     }
441 
442 
visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)443     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
444     {
445         // Has the class info changed?
446         String className    = classElementValue.getClassName(clazz);
447         String newClassName = newDescriptor(className,
448                                             classElementValue.referencedClasses);
449 
450         if (!className.equals(newClassName))
451         {
452             // Update the class info.
453             classElementValue.u2classInfoIndex =
454                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
455         }
456     }
457 
458 
visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)459     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
460     {
461         // Fix the annotation.
462         annotationElementValue.annotationAccept(clazz, this);
463     }
464 
465 
visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)466     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
467     {
468         // Fix the element values.
469         arrayElementValue.elementValuesAccept(clazz, annotation, this);
470     }
471 
472 
473     // Small utility methods.
474 
newDescriptor(String descriptor, Clazz referencedClass)475     private static String newDescriptor(String descriptor,
476                                         Clazz  referencedClass)
477     {
478         // If there is no referenced class, the descriptor won't change.
479         if (referencedClass == null)
480         {
481             return descriptor;
482         }
483 
484         // Unravel and reconstruct the class element of the descriptor.
485         DescriptorClassEnumeration descriptorClassEnumeration =
486             new DescriptorClassEnumeration(descriptor);
487 
488         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
489         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
490 
491         // Only if the descriptor contains a class name (e.g. with an array of
492         // primitive types), the descriptor can change.
493         if (descriptorClassEnumeration.hasMoreClassNames())
494         {
495             String className = descriptorClassEnumeration.nextClassName();
496             String fluff     = descriptorClassEnumeration.nextFluff();
497 
498             String newClassName = newClassName(className,
499                                                referencedClass);
500 
501             newDescriptorBuffer.append(newClassName);
502             newDescriptorBuffer.append(fluff);
503         }
504 
505         return newDescriptorBuffer.toString();
506     }
507 
508 
newDescriptor(String descriptor, Clazz[] referencedClasses)509     private static String newDescriptor(String  descriptor,
510                                         Clazz[] referencedClasses)
511     {
512         // If there are no referenced classes, the descriptor won't change.
513         if (referencedClasses == null ||
514             referencedClasses.length == 0)
515         {
516             return descriptor;
517         }
518 
519         // Unravel and reconstruct the class elements of the descriptor.
520         DescriptorClassEnumeration descriptorClassEnumeration =
521             new DescriptorClassEnumeration(descriptor);
522 
523         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
524         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
525 
526         int index = 0;
527         while (descriptorClassEnumeration.hasMoreClassNames())
528         {
529             String  className        = descriptorClassEnumeration.nextClassName();
530             boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName();
531             String  fluff            = descriptorClassEnumeration.nextFluff();
532 
533             String newClassName = newClassName(className,
534                                                referencedClasses[index++]);
535 
536             // Strip the outer class name again, if it's an inner class.
537             if (isInnerClassName)
538             {
539                 newClassName =
540                     newClassName.substring(newClassName.lastIndexOf(ClassConstants.INNER_CLASS_SEPARATOR)+1);
541             }
542 
543             newDescriptorBuffer.append(newClassName);
544             newDescriptorBuffer.append(fluff);
545         }
546 
547         return newDescriptorBuffer.toString();
548     }
549 
550 
551     /**
552      * Returns a unique class member name, based on the given name and descriptor.
553      */
newUniqueMemberName(String name, String descriptor)554     private String newUniqueMemberName(String name, String descriptor)
555     {
556         return name.equals(ClassConstants.METHOD_NAME_INIT) ?
557             ClassConstants.METHOD_NAME_INIT :
558             name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
559     }
560 
561 
562     /**
563      * Returns the new class name based on the given class name and the new
564      * name of the given referenced class. Class names of array types
565      * are handled properly.
566      */
newClassName(String className, Clazz referencedClass)567     private static String newClassName(String className,
568                                        Clazz  referencedClass)
569     {
570         // If there is no referenced class, the class name won't change.
571         if (referencedClass == null)
572         {
573             return className;
574         }
575 
576         // Reconstruct the class name.
577         String newClassName = referencedClass.getName();
578 
579         // Is it an array type?
580         if (className.charAt(0) == ClassConstants.TYPE_ARRAY)
581         {
582             // Add the array prefixes and suffix "[L...;".
583             newClassName =
584                  className.substring(0, className.indexOf(ClassConstants.TYPE_CLASS_START)+1) +
585                  newClassName +
586                  ClassConstants.TYPE_CLASS_END;
587         }
588 
589         return newClassName;
590     }
591 }
592