• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2009 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.AttributeVisitor;
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 constant pool field and method references to fields
35  * and methods whose names or descriptors have changed.
36  *
37  * @author Eric Lafortune
38  */
39 public class MemberReferenceFixer
40 extends      SimplifiedVisitor
41 implements   ClassVisitor,
42              ConstantVisitor,
43              MemberVisitor,
44              AttributeVisitor,
45              AnnotationVisitor,
46              ElementValueVisitor
47 {
48     private static final boolean DEBUG = false;
49 
50 
51     private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
52 
53     // Parameter for the visitor methods.
54     private int constantIndex;
55 
56     // Return values for the visitor methods.
57     private boolean isInterfaceMethod;
58     private boolean stackSizesMayHaveChanged;
59 
60 
61     // Implementations for ClassVisitor.
62 
visitProgramClass(ProgramClass programClass)63     public void visitProgramClass(ProgramClass programClass)
64     {
65         stackSizesMayHaveChanged = false;
66 
67         // Fix the constant pool entries.
68         for (int index = 1; index < programClass.u2constantPoolCount; index++)
69         {
70             Constant constant = programClass.constantPool[index];
71             if (constant != null)
72             {
73                 // Fix the entry, replacing it entirely if needed.
74                 this.constantIndex = index;
75 
76                 constant.accept(programClass, this);
77             }
78         }
79 
80         // Fix the class members.
81         programClass.fieldsAccept(this);
82         programClass.methodsAccept(this);
83 
84         // Fix the attributes.
85         programClass.attributesAccept(this);
86     }
87 
88 
89     // Implementations for ConstantVisitor.
90 
visitAnyConstant(Clazz clazz, Constant constant)91     public void visitAnyConstant(Clazz clazz, Constant constant) {}
92 
93 
visitStringConstant(Clazz clazz, StringConstant stringConstant)94     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
95     {
96         // Does the string refer to a class member, due to a
97         // Class.get[Declared]{Field,Method} construct?
98         Member referencedMember = stringConstant.referencedMember;
99         if (referencedMember != null)
100         {
101             Clazz referencedClass = stringConstant.referencedClass;
102 
103             // Does it have a new name?
104             String newName = referencedMember.getName(referencedClass);
105 
106             if (!stringConstant.getString(clazz).equals(newName))
107             {
108                 if (DEBUG)
109                 {
110                     debug(clazz, stringConstant, referencedClass, referencedMember);
111                 }
112 
113                 // Update the name.
114                 stringConstant.u2stringIndex =
115                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName);
116             }
117         }
118     }
119 
120 
visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)121     public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
122     {
123         // Do we know the referenced field?
124         Member referencedMember = fieldrefConstant.referencedMember;
125         if (referencedMember != null)
126         {
127             Clazz referencedClass = fieldrefConstant.referencedClass;
128 
129             // Does it have a new name or type?
130             String newName = referencedMember.getName(referencedClass);
131             String newType = referencedMember.getDescriptor(referencedClass);
132 
133             if (!fieldrefConstant.getName(clazz).equals(newName) ||
134                 !fieldrefConstant.getType(clazz).equals(newType))
135             {
136                 if (DEBUG)
137                 {
138                     debug(clazz, fieldrefConstant, referencedClass, referencedMember);
139                 }
140 
141                 // Update the name and type index.
142                 fieldrefConstant.u2nameAndTypeIndex =
143                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
144             }
145         }
146     }
147 
148 
visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)149     public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
150     {
151         // Do we know the referenced interface method?
152         Member referencedMember = interfaceMethodrefConstant.referencedMember;
153         if (referencedMember != null)
154         {
155             Clazz referencedClass = interfaceMethodrefConstant.referencedClass;
156 
157             // Does it have a new name or type?
158             String newName = referencedMember.getName(referencedClass);
159             String newType = referencedMember.getDescriptor(referencedClass);
160 
161             if (!interfaceMethodrefConstant.getName(clazz).equals(newName) ||
162                 !interfaceMethodrefConstant.getType(clazz).equals(newType))
163             {
164                 if (DEBUG)
165                 {
166                     debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember);
167                 }
168 
169                 // Update the name and type index.
170                 interfaceMethodrefConstant.u2nameAndTypeIndex =
171                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
172 
173                 // Remember that the stack sizes of the methods in this class
174                 // may have changed.
175                 stackSizesMayHaveChanged = true;
176             }
177 
178             // Check if this is an interface method.
179             isInterfaceMethod = true;
180             clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this);
181 
182             // Has the method become a non-interface method?
183             if (!isInterfaceMethod)
184             {
185                 if (DEBUG)
186                 {
187                     System.out.println("MemberReferenceFixer:");
188                     System.out.println("  Class file     = "+clazz.getName());
189                     System.out.println("  Ref class      = "+referencedClass.getName());
190                     System.out.println("  Ref method     = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz));
191                     System.out.println("    -> ordinary method");
192                 }
193 
194                 // Replace the interface method reference by a method reference.
195                 ((ProgramClass)clazz).constantPool[this.constantIndex] =
196                     new MethodrefConstant(interfaceMethodrefConstant.u2classIndex,
197                                           interfaceMethodrefConstant.u2nameAndTypeIndex,
198                                           referencedClass,
199                                           referencedMember);
200             }
201         }
202     }
203 
204 
visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)205     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
206     {
207         // Do we know the referenced method?
208         Member referencedMember = methodrefConstant.referencedMember;
209         if (referencedMember != null)
210         {
211             Clazz referencedClass = methodrefConstant.referencedClass;
212 
213             // Does it have a new name or type?
214             String newName = referencedMember.getName(referencedClass);
215             String newType = referencedMember.getDescriptor(referencedClass);
216 
217             if (!methodrefConstant.getName(clazz).equals(newName) ||
218                 !methodrefConstant.getType(clazz).equals(newType))
219             {
220                 if (DEBUG)
221                 {
222                     debug(clazz, methodrefConstant, referencedClass, referencedMember);
223                 }
224 
225                 // Update the name and type index.
226                 methodrefConstant.u2nameAndTypeIndex =
227                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
228 
229                 // Remember that the stack sizes of the methods in this class
230                 // may have changed.
231                 stackSizesMayHaveChanged = true;
232             }
233 
234             // Check if this is an interface method.
235             isInterfaceMethod = false;
236             clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this);
237 
238             // Has the method become an interface method?
239             if (isInterfaceMethod)
240             {
241                 if (DEBUG)
242                 {
243                     System.out.println("MemberReferenceFixer:");
244                     System.out.println("  Class file     = "+clazz.getName());
245                     System.out.println("  Ref class      = "+referencedClass.getName());
246                     System.out.println("  Ref method     = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz));
247                     System.out.println("    -> interface method");
248                 }
249 
250                 // Replace the method reference by an interface method reference.
251                 ((ProgramClass)clazz).constantPool[this.constantIndex] =
252                     new InterfaceMethodrefConstant(methodrefConstant.u2classIndex,
253                                                    methodrefConstant.u2nameAndTypeIndex,
254                                                    referencedClass,
255                                                    referencedMember);
256             }
257         }
258     }
259 
260 
visitClassConstant(Clazz clazz, ClassConstant classConstant)261     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
262     {
263         // Check if this class entry is an array type.
264         if (ClassUtil.isInternalArrayType(classConstant.getName(clazz)))
265         {
266             isInterfaceMethod = false;
267         }
268         else
269         {
270             // Check if this class entry refers to an interface class.
271             Clazz referencedClass = classConstant.referencedClass;
272             if (referencedClass != null)
273             {
274                 isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
275             }
276         }
277     }
278 
279 
280     // Implementations for MemberVisitor.
281 
visitProgramMember(ProgramClass programClass, ProgramMember programMember)282     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
283     {
284         // Fix the attributes.
285         programMember.attributesAccept(programClass, this);
286     }
287 
288 
289     // Implementations for AttributeVisitor.
290 
visitAnyAttribute(Clazz clazz, Attribute attribute)291     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
292 
293 
visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)294     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
295     {
296         Member referencedMember = enclosingMethodAttribute.referencedMethod;
297         if (referencedMember != null)
298         {
299             Clazz referencedClass = enclosingMethodAttribute.referencedClass;
300 
301             // Does it have a new class?
302             if (!enclosingMethodAttribute.getClassName(clazz).equals(referencedClass.getName()))
303             {
304                 // Update the class index.
305                 enclosingMethodAttribute.u2classIndex =
306                     new ConstantPoolEditor((ProgramClass)clazz).addClassConstant(referencedClass);
307             }
308 
309             // Does it have a new name or type?
310             if (!enclosingMethodAttribute.getName(clazz).equals(referencedMember.getName(referencedClass)) ||
311                 !enclosingMethodAttribute.getType(clazz).equals(referencedMember.getDescriptor(referencedClass)))
312             {
313                 // Update the name and type index.
314                 enclosingMethodAttribute.u2nameAndTypeIndex =
315                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(referencedMember.getName(referencedClass),
316                                                                                        referencedMember.getDescriptor(referencedClass));
317             }
318         }
319     }
320 
321 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)322     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
323     {
324         // Recompute the maximum stack size if necessary.
325         if (stackSizesMayHaveChanged)
326         {
327                 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
328         }
329 
330         // Fix the nested attributes.
331         codeAttribute.attributesAccept(clazz, method, this);
332     }
333 
334 
visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)335     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
336     {
337         // Fix the annotations.
338         annotationsAttribute.annotationsAccept(clazz, this);
339     }
340 
341 
visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)342     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
343     {
344         // Fix the annotations.
345         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
346     }
347 
348 
visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)349     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
350     {
351         // Fix the annotation.
352         annotationDefaultAttribute.defaultValueAccept(clazz, this);
353     }
354 
355 
356     // Implementations for AnnotationVisitor.
357 
visitAnnotation(Clazz clazz, Annotation annotation)358     public void visitAnnotation(Clazz clazz, Annotation annotation)
359     {
360         // Fix the element values.
361         annotation.elementValuesAccept(clazz, this);
362     }
363 
364 
365     // Implementations for ElementValueVisitor.
366 
visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)367     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
368     {
369         fixElementValue(clazz, annotation, constantElementValue);
370     }
371 
372 
visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)373     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
374     {
375         fixElementValue(clazz, annotation, enumConstantElementValue);
376     }
377 
378 
visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)379     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
380     {
381         fixElementValue(clazz, annotation, classElementValue);
382     }
383 
384 
visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)385     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
386     {
387         fixElementValue(clazz, annotation, annotationElementValue);
388 
389         // Fix the annotation.
390         annotationElementValue.annotationAccept(clazz, this);
391     }
392 
393 
visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)394     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
395     {
396         fixElementValue(clazz, annotation, arrayElementValue);
397 
398         // Fix the element values.
399         arrayElementValue.elementValuesAccept(clazz, annotation, this);
400     }
401 
402 
403     // Small utility methods.
404 
405     /**
406      * Fixes the method reference of the element value, if any.
407      */
fixElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)408     private void fixElementValue(Clazz        clazz,
409                                  Annotation   annotation,
410                                  ElementValue elementValue)
411     {
412         // Do we know the referenced method?
413         Member referencedMember = elementValue.referencedMethod;
414         if (referencedMember != null)
415         {
416             // Does it have a new name or type?
417             String methodName    = elementValue.getMethodName(clazz);
418             String newMethodName = referencedMember.getName(elementValue.referencedClass);
419 
420             if (!methodName.equals(newMethodName))
421             {
422                 // Update the element name index.
423                 elementValue.u2elementNameIndex =
424                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName);
425             }
426         }
427     }
428 
429 
debug(Clazz clazz, StringConstant stringConstant, Clazz referencedClass, Member referencedMember)430     private void debug(Clazz          clazz,
431                        StringConstant stringConstant,
432                        Clazz          referencedClass,
433                        Member         referencedMember)
434     {
435         System.out.println("MemberReferenceFixer:");
436         System.out.println("  Class file      = "+clazz.getName());
437         System.out.println("  Ref class       = "+referencedClass.getName());
438         System.out.println("  Ref member name = "+stringConstant.getString(clazz));
439         System.out.println("                 -> "+referencedMember.getName(referencedClass));
440     }
441 
442 
debug(Clazz clazz, RefConstant refConstant, Clazz referencedClass, Member referencedMember)443     private void debug(Clazz       clazz,
444                        RefConstant refConstant,
445                        Clazz       referencedClass,
446                        Member      referencedMember)
447     {
448         System.out.println("MemberReferenceFixer:");
449         System.out.println("  Class file      = "+clazz.getName());
450         System.out.println("  Ref class       = "+referencedClass.getName());
451         System.out.println("  Ref member name = "+refConstant.getName(clazz));
452         System.out.println("                 -> "+referencedMember.getName(referencedClass));
453         System.out.println("  Ref descriptor  = "+refConstant.getType(clazz));
454         System.out.println("                 -> "+referencedMember.getDescriptor(referencedClass));
455     }
456 }
457