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.optimize.peephole;
22
23import proguard.classfile.*;
24import proguard.classfile.editor.*;
25import proguard.classfile.attribute.*;
26import proguard.classfile.attribute.annotation.*;
27import proguard.classfile.attribute.annotation.visitor.*;
28import proguard.classfile.attribute.visitor.*;
29import proguard.classfile.constant.*;
30import proguard.classfile.constant.visitor.ConstantVisitor;
31import proguard.classfile.util.*;
32import proguard.classfile.visitor.*;
33
34/**
35 * This ClassVisitor replaces references to classes and class members if the
36 * classes have targets that are intended to replace them.
37 *
38 * @see VerticalClassMerger
39 * @see ClassReferenceFixer
40 * @see MemberReferenceFixer
41 * @author Eric Lafortune
42 */
43public class TargetClassChanger
44extends      SimplifiedVisitor
45implements   ClassVisitor,
46             ConstantVisitor,
47             MemberVisitor,
48             AttributeVisitor,
49             LocalVariableInfoVisitor,
50             LocalVariableTypeInfoVisitor,
51             AnnotationVisitor,
52             ElementValueVisitor
53{
54    private static final boolean DEBUG = false;
55
56
57    // Implementations for ClassVisitor.
58
59    public void visitProgramClass(ProgramClass programClass)
60    {
61        // Change the references of the constant pool.
62        programClass.constantPoolEntriesAccept(this);
63
64        // Change the references of the class members.
65        programClass.fieldsAccept(this);
66        programClass.methodsAccept(this);
67
68        // Change the references of the attributes.
69        programClass.attributesAccept(this);
70
71        // Is the class itself being retargeted?
72        Clazz targetClass = ClassMerger.getTargetClass(programClass);
73        if (targetClass != null)
74        {
75            // Restore the class name. We have to add a new class entry
76            // to avoid an existing entry with the same name being reused. The
77            // names have to be fixed later, based on their referenced classes.
78            programClass.u2thisClass =
79                addNewClassConstant(programClass,
80                                    programClass.getName(),
81                                    programClass);
82
83            // This class will loose all its interfaces.
84            programClass.u2interfacesCount = 0;
85
86            // This class will loose all its subclasses.
87            programClass.subClasses = null;
88        }
89        else
90        {
91            // Remove interface classes that are pointing to this class.
92            int newInterfacesCount = 0;
93            for (int index = 0; index < programClass.u2interfacesCount; index++)
94            {
95                Clazz interfaceClass = programClass.getInterface(index);
96                if (!programClass.equals(interfaceClass))
97                {
98                    programClass.u2interfaces[newInterfacesCount++] =
99                        programClass.u2interfaces[index];
100                }
101            }
102            programClass.u2interfacesCount = newInterfacesCount;
103
104            // Update the subclasses of the superclass and interfaces of the
105            // target class.
106            ConstantVisitor subclassAdder =
107                new ReferencedClassVisitor(
108                new SubclassFilter(programClass,
109                new SubclassAdder(programClass)));
110
111            programClass.superClassConstantAccept(subclassAdder);
112            programClass.interfaceConstantsAccept(subclassAdder);
113
114            // TODO: Maybe restore private method references.
115        }
116    }
117
118
119    public void visitLibraryClass(LibraryClass libraryClass)
120    {
121        // Change the references of the class members.
122        libraryClass.fieldsAccept(this);
123        libraryClass.methodsAccept(this);
124    }
125
126
127    // Implementations for MemberVisitor.
128
129    public void visitProgramField(ProgramClass programClass, ProgramField programField)
130    {
131        // Change the referenced class.
132        programField.referencedClass =
133            updateReferencedClass(programField.referencedClass);
134
135        // Change the references of the attributes.
136        programField.attributesAccept(programClass, this);
137    }
138
139
140    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
141    {
142        // Change the referenced classes.
143        updateReferencedClasses(programMethod.referencedClasses);
144
145        // Change the references of the attributes.
146        programMethod.attributesAccept(programClass, this);
147    }
148
149
150    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
151    {
152        // Change the referenced class.
153        libraryField.referencedClass =
154            updateReferencedClass(libraryField.referencedClass);
155    }
156
157
158    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
159    {
160        // Change the referenced classes.
161        updateReferencedClasses(libraryMethod.referencedClasses);
162    }
163
164
165    // Implementations for ConstantVisitor.
166
167    public void visitAnyConstant(Clazz clazz, Constant constant) {}
168
169
170    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
171    {
172        // Does the string refer to a class, due to a Class.forName construct?
173        Clazz referencedClass    = stringConstant.referencedClass;
174        Clazz newReferencedClass = updateReferencedClass(referencedClass);
175        if (referencedClass != newReferencedClass)
176        {
177            // Change the referenced class.
178            stringConstant.referencedClass = newReferencedClass;
179
180            // Change the referenced class member, if applicable.
181            stringConstant.referencedMember =
182                updateReferencedMember(stringConstant.referencedMember,
183                                       stringConstant.getString(clazz),
184                                       null,
185                                       newReferencedClass);
186        }
187    }
188
189
190    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
191    {
192        Clazz referencedClass    = refConstant.referencedClass;
193        Clazz newReferencedClass = updateReferencedClass(referencedClass);
194        if (referencedClass != newReferencedClass)
195        {
196            if (DEBUG)
197            {
198                System.out.println("TargetClassChanger:");
199                System.out.println("  ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]");
200            }
201
202            // Change the referenced class.
203            refConstant.referencedClass  = newReferencedClass;
204
205            // Change the referenced class member.
206            refConstant.referencedMember =
207                updateReferencedMember(refConstant.referencedMember,
208                                       refConstant.getName(clazz),
209                                       refConstant.getType(clazz),
210                                       newReferencedClass);
211
212            if (DEBUG)
213            {
214                System.out.println("  ["+clazz.getName()+"]                    to   ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]");
215            }
216        }
217    }
218
219
220    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
221    {
222        // Change the referenced class.
223        classConstant.referencedClass =
224            updateReferencedClass(classConstant.referencedClass);
225    }
226
227
228    // Implementations for AttributeVisitor.
229
230    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
231
232
233    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
234    {
235        // Change the references of the attributes.
236        codeAttribute.attributesAccept(clazz, method, this);
237    }
238
239
240    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
241    {
242        // Change the references of the local variables.
243        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
244    }
245
246
247    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
248    {
249        // Change the references of the local variables.
250        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
251    }
252
253
254    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
255    {
256        // Change the referenced classes.
257        updateReferencedClasses(signatureAttribute.referencedClasses);
258    }
259
260
261    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
262    {
263        // Change the references of the annotations.
264        annotationsAttribute.annotationsAccept(clazz, this);
265    }
266
267
268    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
269    {
270        // Change the references of the annotations.
271        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
272    }
273
274
275    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
276    {
277        // Change the references of the annotation.
278        annotationDefaultAttribute.defaultValueAccept(clazz, this);
279    }
280
281
282
283   // Implementations for LocalVariableInfoVisitor.
284
285    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
286    {
287        // Change the referenced class.
288        localVariableInfo.referencedClass =
289            updateReferencedClass(localVariableInfo.referencedClass);
290    }
291
292    // Implementations for LocalVariableTypeInfoVisitor.
293
294    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
295    {
296        // Change the referenced classes.
297        updateReferencedClasses(localVariableTypeInfo.referencedClasses);
298    }
299
300    // Implementations for AnnotationVisitor.
301
302    public void visitAnnotation(Clazz clazz, Annotation annotation)
303    {
304        // Change the referenced classes.
305        updateReferencedClasses(annotation.referencedClasses);
306
307        // Change the references of the element values.
308        annotation.elementValuesAccept(clazz, this);
309    }
310
311
312    // Implementations for ElementValueVisitor.
313
314    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
315    {
316        Clazz referencedClass    = elementValue.referencedClass;
317        Clazz newReferencedClass = updateReferencedClass(referencedClass);
318        if (referencedClass != newReferencedClass)
319        {
320            // Change the referenced annotation class.
321            elementValue.referencedClass  = newReferencedClass;
322
323            // Change the referenced method.
324            elementValue.referencedMethod =
325                (Method)updateReferencedMember(elementValue.referencedMethod,
326                                               elementValue.getMethodName(clazz),
327                                               null,
328                                               newReferencedClass);
329        }
330    }
331
332
333    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
334    {
335        // Change the referenced annotation class and method.
336        visitAnyElementValue(clazz, annotation, constantElementValue);
337    }
338
339
340    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
341    {
342        // Change the referenced annotation class and method.
343        visitAnyElementValue(clazz, annotation, enumConstantElementValue);
344
345        // Change the referenced classes.
346        updateReferencedClasses(enumConstantElementValue.referencedClasses);
347    }
348
349
350    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
351    {
352        // Change the referenced annotation class and method.
353        visitAnyElementValue(clazz, annotation, classElementValue);
354
355        // Change the referenced classes.
356        updateReferencedClasses(classElementValue.referencedClasses);
357    }
358
359
360    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
361    {
362        // Change the referenced annotation class and method.
363        visitAnyElementValue(clazz, annotation, annotationElementValue);
364
365        // Change the references of the annotation.
366        annotationElementValue.annotationAccept(clazz, this);
367    }
368
369
370    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
371    {
372        // Change the referenced annotation class and method.
373        visitAnyElementValue(clazz, annotation, arrayElementValue);
374
375        // Change the references of the element values.
376        arrayElementValue.elementValuesAccept(clazz, annotation, this);
377    }
378
379
380    // Small utility methods.
381
382    /**
383     * Updates the retargeted classes in the given array of classes.
384     */
385    private void updateReferencedClasses(Clazz[] referencedClasses)
386    {
387        if (referencedClasses == null)
388        {
389            return;
390        }
391
392        for (int index = 0; index < referencedClasses.length; index++)
393        {
394            referencedClasses[index] =
395                updateReferencedClass(referencedClasses[index]);
396        }
397    }
398
399
400    /**
401     * Returns the retargeted class of the given class.
402     */
403    private Clazz updateReferencedClass(Clazz referencedClass)
404    {
405        if (referencedClass == null)
406        {
407            return null;
408        }
409
410        Clazz targetClazz = ClassMerger.getTargetClass(referencedClass);
411        return targetClazz != null ?
412            targetClazz :
413            referencedClass;
414    }
415
416
417    /**
418     * Returns the retargeted class member of the given class member.
419     */
420    private Member updateReferencedMember(Member referencedMember,
421                                          String name,
422                                          String type,
423                                          Clazz  newReferencedClass)
424    {
425        if (referencedMember == null)
426        {
427            return null;
428        }
429
430        return referencedMember instanceof Field ?
431            (Member)newReferencedClass.findField(name, type) :
432            (Member)newReferencedClass.findMethod(name, type);
433    }
434
435
436    /**
437     * Explicitly adds a new class constant for the given class in the given
438     * program class.
439     */
440    private int addNewClassConstant(ProgramClass programClass,
441                                    String       className,
442                                    Clazz        referencedClass)
443    {
444        ConstantPoolEditor constantPoolEditor =
445            new ConstantPoolEditor(programClass);
446
447        int nameIndex =
448            constantPoolEditor.addUtf8Constant(className);
449
450        int classConstantIndex =
451            constantPoolEditor.addConstant(new ClassConstant(nameIndex,
452                                                             referencedClass));
453        return classConstantIndex;
454    }
455}