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