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