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