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 */
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 name or type?
302            String newName = referencedMember.getName(referencedClass);
303            String newType = referencedMember.getDescriptor(referencedClass);
304
305            if (!enclosingMethodAttribute.getName(clazz).equals(newName) ||
306                !enclosingMethodAttribute.getType(clazz).equals(newType))
307            {
308                // Update the name and type index.
309                enclosingMethodAttribute.u2nameAndTypeIndex =
310                    new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName,
311                                                                                       newType);
312            }
313        }
314    }
315
316
317    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
318    {
319        // Recompute the maximum stack size if necessary.
320        if (stackSizesMayHaveChanged)
321        {
322            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
323        }
324
325        // Fix the nested attributes.
326        codeAttribute.attributesAccept(clazz, method, this);
327    }
328
329
330    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
331    {
332        // Fix the annotations.
333        annotationsAttribute.annotationsAccept(clazz, this);
334    }
335
336
337    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
338    {
339        // Fix the annotations.
340        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
341    }
342
343
344    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
345    {
346        // Fix the annotation.
347        annotationDefaultAttribute.defaultValueAccept(clazz, this);
348    }
349
350
351    // Implementations for AnnotationVisitor.
352
353    public void visitAnnotation(Clazz clazz, Annotation annotation)
354    {
355        // Fix the element values.
356        annotation.elementValuesAccept(clazz, this);
357    }
358
359
360    // Implementations for ElementValueVisitor.
361
362    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
363    {
364        fixElementValue(clazz, annotation, constantElementValue);
365    }
366
367
368    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
369    {
370        fixElementValue(clazz, annotation, enumConstantElementValue);
371    }
372
373
374    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
375    {
376        fixElementValue(clazz, annotation, classElementValue);
377    }
378
379
380    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
381    {
382        fixElementValue(clazz, annotation, annotationElementValue);
383
384        // Fix the annotation.
385        annotationElementValue.annotationAccept(clazz, this);
386    }
387
388
389    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
390    {
391        fixElementValue(clazz, annotation, arrayElementValue);
392
393        // Fix the element values.
394        arrayElementValue.elementValuesAccept(clazz, annotation, this);
395    }
396
397
398    // Small utility methods.
399
400    /**
401     * Fixes the method reference of the element value, if any.
402     */
403    private void fixElementValue(Clazz        clazz,
404                                 Annotation   annotation,
405                                 ElementValue elementValue)
406    {
407        // Do we know the referenced method?
408        Member referencedMember = elementValue.referencedMethod;
409        if (referencedMember != null)
410        {
411            // Does it have a new name or type?
412            String methodName    = elementValue.getMethodName(clazz);
413            String newMethodName = referencedMember.getName(elementValue.referencedClass);
414
415            if (!methodName.equals(newMethodName))
416            {
417                // Update the element name index.
418                elementValue.u2elementNameIndex =
419                    new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName);
420            }
421        }
422    }
423
424
425    private void debug(Clazz          clazz,
426                       StringConstant stringConstant,
427                       Clazz          referencedClass,
428                       Member         referencedMember)
429    {
430        System.out.println("MemberReferenceFixer:");
431        System.out.println("  ["+clazz.getName()+"]: String ["+
432                           stringConstant.getString(clazz)+"] -> ["+
433                           referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]");
434    }
435
436
437    private void debug(Clazz       clazz,
438                       RefConstant refConstant,
439                       Clazz       referencedClass,
440                       Member      referencedMember)
441    {
442        System.out.println("MemberReferenceFixer:");
443        System.out.println("  ["+clazz.getName()+"]: ["+
444                           refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+" "+refConstant.getType(clazz)+"] -> ["+
445                           referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]");
446    }
447}
448