ClassReferenceInitializer.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
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.util;
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.visitor.*;
31
32
33/**
34 * This ClassVisitor initializes the references of all classes that
35 * it visits.
36 * <p>
37 * All class constant pool entries get direct references to the corresponding
38 * classes. These references make it more convenient to travel up and across
39 * the class hierarchy.
40 * <p>
41 * All field and method reference constant pool entries get direct references
42 * to the corresponding classes, fields, and methods.
43 * <p>
44 * All name and type constant pool entries get a list of direct references to
45 * the classes listed in the type.
46 * <p>
47 * This visitor optionally prints warnings if some items can't be found.
48 * <p>
49 * The class hierarchy must be initialized before using this visitor.
50 *
51 * @author Eric Lafortune
52 */
53public class ClassReferenceInitializer
54extends      SimplifiedVisitor
55implements   ClassVisitor,
56             MemberVisitor,
57             ConstantVisitor,
58             AttributeVisitor,
59             LocalVariableInfoVisitor,
60             LocalVariableTypeInfoVisitor,
61             AnnotationVisitor,
62             ElementValueVisitor
63{
64    private final ClassPool      programClassPool;
65    private final ClassPool      libraryClassPool;
66    private final WarningPrinter missingClassWarningPrinter;
67    private final WarningPrinter missingProgramMemberWarningPrinter;
68    private final WarningPrinter missingLibraryMemberWarningPrinter;
69    private final WarningPrinter dependencyWarningPrinter;
70
71    private final MemberFinder memberFinder = new MemberFinder();
72
73
74    /**
75     * Creates a new ClassReferenceInitializer that initializes the references
76     * of all visited class files, optionally printing warnings if some classes
77     * or class members can't be found or if they are in the program class pool.
78     */
79    public ClassReferenceInitializer(ClassPool      programClassPool,
80                                     ClassPool      libraryClassPool,
81                                     WarningPrinter missingClassWarningPrinter,
82                                     WarningPrinter missingProgramMemberWarningPrinter,
83                                     WarningPrinter missingLibraryMemberWarningPrinter,
84                                     WarningPrinter dependencyWarningPrinter)
85    {
86        this.programClassPool                   = programClassPool;
87        this.libraryClassPool                   = libraryClassPool;
88        this.missingClassWarningPrinter         = missingClassWarningPrinter;
89        this.missingProgramMemberWarningPrinter = missingProgramMemberWarningPrinter;
90        this.missingLibraryMemberWarningPrinter = missingLibraryMemberWarningPrinter;
91        this.dependencyWarningPrinter           = dependencyWarningPrinter;
92    }
93
94
95    // Implementations for ClassVisitor.
96
97    public void visitProgramClass(ProgramClass programClass)
98    {
99        // Initialize the constant pool entries.
100        programClass.constantPoolEntriesAccept(this);
101
102        // Initialize all fields and methods.
103        programClass.fieldsAccept(this);
104        programClass.methodsAccept(this);
105
106        // Initialize the attributes.
107        programClass.attributesAccept(this);
108    }
109
110
111    public void visitLibraryClass(LibraryClass libraryClass)
112    {
113        // Initialize all fields and methods.
114        libraryClass.fieldsAccept(this);
115        libraryClass.methodsAccept(this);
116    }
117
118
119    // Implementations for MemberVisitor.
120
121    public void visitProgramField(ProgramClass programClass, ProgramField programField)
122    {
123        programField.referencedClass =
124            findReferencedClass(programClass.getName(),
125                                programField.getDescriptor(programClass));
126
127        // Initialize the attributes.
128        programField.attributesAccept(programClass, this);
129    }
130
131
132    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
133    {
134        programMethod.referencedClasses =
135            findReferencedClasses(programClass.getName(),
136                                  programMethod.getDescriptor(programClass));
137
138        // Initialize the attributes.
139        programMethod.attributesAccept(programClass, this);
140    }
141
142
143    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
144    {
145        libraryField.referencedClass =
146            findReferencedClass(libraryClass.getName(),
147                                libraryField.getDescriptor(libraryClass));
148    }
149
150
151    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
152    {
153        libraryMethod.referencedClasses =
154            findReferencedClasses(libraryClass.getName(),
155                                  libraryMethod.getDescriptor(libraryClass));
156    }
157
158
159    // Implementations for ConstantVisitor.
160
161    public void visitAnyConstant(Clazz clazz, Constant constant) {}
162
163
164    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
165    {
166        // Fill out the String class.
167        stringConstant.javaLangStringClass =
168            findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING);
169    }
170
171
172    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
173    {
174        // Fill out the MethodHandle class.
175        methodHandleConstant.javaLangInvokeMethodHandleClass =
176            findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_HANDLE);
177    }
178
179
180    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
181    {
182        String className = refConstant.getClassName(clazz);
183
184        // Methods for array types should be found in the Object class.
185        if (ClassUtil.isInternalArrayType(className))
186        {
187            className = ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT;
188        }
189
190        // See if we can find the referenced class.
191        // Unresolved references are assumed to refer to library classes
192        // that will not change anyway.
193        Clazz referencedClass = findClass(clazz.getName(), className);
194
195        if (referencedClass != null)
196        {
197            String name = refConstant.getName(clazz);
198            String type = refConstant.getType(clazz);
199
200            boolean isFieldRef = refConstant.getTag() == ClassConstants.CONSTANT_Fieldref;
201
202            // See if we can find the referenced class member somewhere in the
203            // hierarchy.
204            refConstant.referencedMember = memberFinder.findMember(clazz,
205                                                                   referencedClass,
206                                                                   name,
207                                                                   type,
208                                                                   isFieldRef);
209            refConstant.referencedClass  = memberFinder.correspondingClass();
210
211            if (refConstant.referencedMember == null)
212            {
213                // We haven't found the class member anywhere in the hierarchy.
214                boolean isProgramClass = referencedClass instanceof ProgramClass;
215
216                WarningPrinter missingMemberWarningPrinter = isProgramClass ?
217                    missingProgramMemberWarningPrinter :
218                    missingLibraryMemberWarningPrinter;
219
220                missingMemberWarningPrinter.print(clazz.getName(),
221                                                  className,
222                                                  "Warning: " +
223                                                  ClassUtil.externalClassName(clazz.getName()) +
224                                                  ": can't find referenced " +
225                                                  (isFieldRef ?
226                                                      "field '"  + ClassUtil.externalFullFieldDescription(0, name, type) :
227                                                      "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
228                                                  "' in " +
229                                                  (isProgramClass ?
230                                                      "program" :
231                                                      "library") +
232                                                  " class " +
233                                                  ClassUtil.externalClassName(className));
234            }
235        }
236    }
237
238
239    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
240    {
241        String className = clazz.getName();
242
243        // Fill out the referenced class.
244        classConstant.referencedClass =
245            findClass(className, ClassUtil.internalClassNameFromClassType(classConstant.getName(clazz)));
246
247        // Fill out the Class class.
248        classConstant.javaLangClassClass =
249            findClass(className, ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS);
250    }
251
252
253    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
254    {
255        // Fill out the MethodType class.
256        methodTypeConstant.javaLangInvokeMethodTypeClass =
257            findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_TYPE);
258    }
259
260
261    // Implementations for AttributeVisitor.
262
263    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
264
265
266    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
267    {
268        String className          = clazz.getName();
269        String enclosingClassName = enclosingMethodAttribute.getClassName(clazz);
270
271        // See if we can find the referenced class.
272        enclosingMethodAttribute.referencedClass =
273            findClass(className, enclosingClassName);
274
275        if (enclosingMethodAttribute.referencedClass != null)
276        {
277            // Is there an enclosing method? Otherwise it's just initialization
278            // code outside of the constructors.
279            if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
280            {
281                String name = enclosingMethodAttribute.getName(clazz);
282                String type = enclosingMethodAttribute.getType(clazz);
283
284                // See if we can find the method in the referenced class.
285                enclosingMethodAttribute.referencedMethod =
286                    enclosingMethodAttribute.referencedClass.findMethod(name, type);
287
288                if (enclosingMethodAttribute.referencedMethod == null)
289                {
290                    // We couldn't find the enclosing method.
291                    missingProgramMemberWarningPrinter.print(className,
292                                                             enclosingClassName,
293                                                             "Warning: " +
294                                                             ClassUtil.externalClassName(className) +
295                                                             ": can't find enclosing method '" +
296                                                             ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) +
297                                                             "' in program class " +
298                                                             ClassUtil.externalClassName(enclosingClassName));
299                }
300            }
301        }
302    }
303
304
305    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
306    {
307        // Initialize the nested attributes.
308        codeAttribute.attributesAccept(clazz, method, this);
309    }
310
311
312    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
313    {
314        // Initialize the local variables.
315        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
316    }
317
318
319    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
320    {
321        // Initialize the local variable types.
322        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
323    }
324
325
326    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
327    {
328        signatureAttribute.referencedClasses =
329            findReferencedClasses(clazz.getName(),
330                                  clazz.getString(signatureAttribute.u2signatureIndex));
331    }
332
333
334    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
335    {
336        // Initialize the annotations.
337        annotationsAttribute.annotationsAccept(clazz, this);
338    }
339
340
341    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
342    {
343        // Initialize the annotations.
344        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
345    }
346
347
348    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
349    {
350        // Initialize the annotation.
351        annotationDefaultAttribute.defaultValueAccept(clazz, this);
352    }
353
354
355    // Implementations for LocalVariableInfoVisitor.
356
357    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
358    {
359        localVariableInfo.referencedClass =
360            findReferencedClass(clazz.getName(),
361                                clazz.getString(localVariableInfo.u2descriptorIndex));
362    }
363
364
365    // Implementations for LocalVariableTypeInfoVisitor.
366
367    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
368    {
369        localVariableTypeInfo.referencedClasses =
370            findReferencedClasses(clazz.getName(),
371                                  clazz.getString(localVariableTypeInfo.u2signatureIndex));
372    }
373
374
375    // Implementations for AnnotationVisitor.
376
377    public void visitAnnotation(Clazz clazz, Annotation annotation)
378    {
379        annotation.referencedClasses =
380            findReferencedClasses(clazz.getName(),
381                                  clazz.getString(annotation.u2typeIndex));
382
383        // Initialize the element values.
384        annotation.elementValuesAccept(clazz, this);
385    }
386
387
388    // Implementations for ElementValueVisitor.
389
390    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
391    {
392        initializeElementValue(clazz, annotation, constantElementValue);
393    }
394
395
396    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
397    {
398        initializeElementValue(clazz, annotation, enumConstantElementValue);
399
400        enumConstantElementValue.referencedClasses =
401            findReferencedClasses(clazz.getName(),
402                                  clazz.getString(enumConstantElementValue.u2typeNameIndex));
403    }
404
405
406    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
407    {
408        initializeElementValue(clazz, annotation, classElementValue);
409
410        classElementValue.referencedClasses =
411            findReferencedClasses(clazz.getName(),
412                                  clazz.getString(classElementValue.u2classInfoIndex));
413    }
414
415
416    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
417    {
418        initializeElementValue(clazz, annotation, annotationElementValue);
419
420        // Initialize the annotation.
421        annotationElementValue.annotationAccept(clazz, this);
422    }
423
424
425    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
426    {
427        initializeElementValue(clazz, annotation, arrayElementValue);
428
429        // Initialize the element values.
430        arrayElementValue.elementValuesAccept(clazz, annotation, this);
431    }
432
433
434    /**
435     * Initializes the referenced method of an element value, if any.
436     */
437    private void initializeElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
438    {
439        // See if we have a referenced class.
440        if (annotation                      != null &&
441            annotation.referencedClasses    != null &&
442            elementValue.u2elementNameIndex != 0)
443        {
444            // See if we can find the method in the referenced class
445            // (ignoring the descriptor).
446            String name = clazz.getString(elementValue.u2elementNameIndex);
447
448            Clazz referencedClass = annotation.referencedClasses[0];
449            elementValue.referencedClass  = referencedClass;
450            elementValue.referencedMethod = referencedClass.findMethod(name, null);
451        }
452    }
453
454
455    // Small utility methods.
456
457    /**
458     * Returns the single class referenced by the given descriptor, or
459     * <code>null</code> if there isn't any useful reference.
460     */
461    private Clazz findReferencedClass(String referencingClassName,
462                                      String descriptor)
463    {
464        DescriptorClassEnumeration enumeration =
465            new DescriptorClassEnumeration(descriptor);
466
467        enumeration.nextFluff();
468
469        if (enumeration.hasMoreClassNames())
470        {
471            return findClass(referencingClassName, enumeration.nextClassName());
472        }
473
474        return null;
475    }
476
477
478    /**
479     * Returns an array of classes referenced by the given descriptor, or
480     * <code>null</code> if there aren't any useful references.
481     */
482    private Clazz[] findReferencedClasses(String referencingClassName,
483                                          String descriptor)
484    {
485        DescriptorClassEnumeration enumeration =
486            new DescriptorClassEnumeration(descriptor);
487
488        int classCount = enumeration.classCount();
489        if (classCount > 0)
490        {
491            Clazz[] referencedClasses = new Clazz[classCount];
492
493            boolean foundReferencedClasses = false;
494
495            for (int index = 0; index < classCount; index++)
496            {
497                String fluff = enumeration.nextFluff();
498                String name  = enumeration.nextClassName();
499
500                Clazz referencedClass = findClass(referencingClassName, name);
501
502                if (referencedClass != null)
503                {
504                    referencedClasses[index] = referencedClass;
505                    foundReferencedClasses = true;
506                }
507            }
508
509            if (foundReferencedClasses)
510            {
511                return referencedClasses;
512            }
513        }
514
515        return null;
516    }
517
518
519    /**
520     * Returns the class with the given name, either for the program class pool
521     * or from the library class pool, or <code>null</code> if it can't be found.
522     */
523    private Clazz findClass(String referencingClassName, String name)
524    {
525        // Is it an array type?
526        if (ClassUtil.isInternalArrayType(name))
527        {
528            // Ignore any primitive array types.
529            if (!ClassUtil.isInternalClassType(name))
530            {
531                return null;
532            }
533
534            // Strip the array part.
535            name = ClassUtil.internalClassNameFromClassType(name);
536        }
537
538        // First look for the class in the program class pool.
539        Clazz clazz = programClassPool.getClass(name);
540
541        // Otherwise look for the class in the library class pool.
542        if (clazz == null)
543        {
544            clazz = libraryClassPool.getClass(name);
545
546            if (clazz == null &&
547                missingClassWarningPrinter != null)
548            {
549                // We didn't find the superclass or interface. Print a warning.
550                missingClassWarningPrinter.print(referencingClassName,
551                                                 name,
552                                                 "Warning: " +
553                                                 ClassUtil.externalClassName(referencingClassName) +
554                                                 ": can't find referenced class " +
555                                                 ClassUtil.externalClassName(name));
556            }
557        }
558        else if (dependencyWarningPrinter != null)
559        {
560            // The superclass or interface was found in the program class pool.
561            // Print a warning.
562            dependencyWarningPrinter.print(referencingClassName,
563                                           name,
564                                           "Warning: library class " +
565                                           ClassUtil.externalClassName(referencingClassName) +
566                                           " depends on program class " +
567                                           ClassUtil.externalClassName(name));
568        }
569
570        return clazz;
571    }
572}
573