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 */
21package proguard.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.annotation.*;
26import proguard.classfile.attribute.annotation.visitor.*;
27import proguard.classfile.attribute.visitor.AttributeVisitor;
28import proguard.classfile.constant.*;
29import proguard.classfile.constant.visitor.ConstantVisitor;
30import proguard.classfile.util.*;
31import 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 */
39public class MemberReferenceFixer
40extends      SimplifiedVisitor
41implements   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
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
91    public void visitAnyConstant(Clazz clazz, Constant constant) {}
92
93
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
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
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
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
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
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
291    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
292
293
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
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
335    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
336    {
337        // Fix the annotations.
338        annotationsAttribute.annotationsAccept(clazz, this);
339    }
340
341
342    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
343    {
344        // Fix the annotations.
345        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
346    }
347
348
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
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
367    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
368    {
369        fixElementValue(clazz, annotation, constantElementValue);
370    }
371
372
373    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
374    {
375        fixElementValue(clazz, annotation, enumConstantElementValue);
376    }
377
378
379    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
380    {
381        fixElementValue(clazz, annotation, classElementValue);
382    }
383
384
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
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     */
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
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
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