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.io;
22
23import proguard.classfile.*;
24import proguard.classfile.constant.*;
25import proguard.classfile.constant.visitor.ConstantVisitor;
26import proguard.classfile.util.*;
27import proguard.classfile.visitor.*;
28
29import java.io.DataInput;
30
31/**
32 * This ClassVisitor fills out the LibraryClass objects that it visits with data
33 * from the given DataInput object.
34 *
35 * @author Eric Lafortune
36 */
37public class LibraryClassReader
38extends      SimplifiedVisitor
39implements   ClassVisitor,
40             MemberVisitor,
41             ConstantVisitor
42{
43    private static final LibraryField[]  EMPTY_LIBRARY_FIELDS  = new LibraryField[0];
44    private static final LibraryMethod[] EMPTY_LIBRARY_METHODS = new LibraryMethod[0];
45
46
47    private final RuntimeDataInput dataInput;
48    private final boolean          skipNonPublicClasses;
49    private final boolean          skipNonPublicClassMembers;
50
51    // A global array that acts as a parameter for the visitor methods.
52    private Constant[]      constantPool;
53
54
55    /**
56     * Creates a new ProgramClassReader for reading from the given DataInput.
57     */
58    public LibraryClassReader(DataInput dataInput,
59                              boolean   skipNonPublicClasses,
60                              boolean   skipNonPublicClassMembers)
61    {
62        this.dataInput                 = new RuntimeDataInput(dataInput);
63        this.skipNonPublicClasses      = skipNonPublicClasses;
64        this.skipNonPublicClassMembers = skipNonPublicClassMembers;
65    }
66
67
68    // Implementations for ClassVisitor.
69
70    public void visitProgramClass(ProgramClass libraryClass)
71    {
72    }
73
74
75    public void visitLibraryClass(LibraryClass libraryClass)
76    {
77        // Read and check the magic number.
78        int u4magic = dataInput.readInt();
79
80        ClassUtil.checkMagicNumber(u4magic);
81
82        // Read and check the version numbers.
83        int u2minorVersion = dataInput.readUnsignedShort();
84        int u2majorVersion = dataInput.readUnsignedShort();
85
86        int u4version = ClassUtil.internalClassVersion(u2majorVersion,
87                                                       u2minorVersion);
88
89        ClassUtil.checkVersionNumbers(u4version);
90
91        // Read the constant pool. Note that the first entry is not used.
92        int u2constantPoolCount = dataInput.readUnsignedShort();
93
94        // Create the constant pool array.
95        constantPool = new Constant[u2constantPoolCount];
96
97        for (int index = 1; index < u2constantPoolCount; index++)
98        {
99            Constant constant = createConstant();
100            constant.accept(libraryClass, this);
101
102            int tag = constant.getTag();
103            if (tag == ClassConstants.CONSTANT_Class ||
104                tag == ClassConstants.CONSTANT_Utf8)
105            {
106                constantPool[index] = constant;
107            }
108
109            // Long constants and double constants take up two entries in the
110            // constant pool.
111            if (tag == ClassConstants.CONSTANT_Long ||
112                tag == ClassConstants.CONSTANT_Double)
113            {
114                index++;
115            }
116        }
117
118        // Read the general class information.
119        libraryClass.u2accessFlags = dataInput.readUnsignedShort();
120
121        // We may stop parsing this library class if it's not public anyway.
122        // E.g. only about 60% of all rt.jar classes need to be parsed.
123        if (skipNonPublicClasses &&
124            AccessUtil.accessLevel(libraryClass.getAccessFlags()) < AccessUtil.PUBLIC)
125        {
126            return;
127        }
128
129        // Read the class and super class indices.
130        int u2thisClass  = dataInput.readUnsignedShort();
131        int u2superClass = dataInput.readUnsignedShort();
132
133        // Store their actual names.
134        libraryClass.thisClassName  = getClassName(u2thisClass);
135        libraryClass.superClassName = (u2superClass == 0) ? null :
136                                      getClassName(u2superClass);
137
138        // Read the interfaces
139        int u2interfacesCount = dataInput.readUnsignedShort();
140
141        libraryClass.interfaceNames = new String[u2interfacesCount];
142        for (int index = 0; index < u2interfacesCount; index++)
143        {
144            // Store the actual interface name.
145            int u2interface = dataInput.readUnsignedShort();
146            libraryClass.interfaceNames[index] = getClassName(u2interface);
147        }
148
149        // Read the fields.
150        int u2fieldsCount = dataInput.readUnsignedShort();
151
152        // Create the fields array.
153        LibraryField[] reusableFields = new LibraryField[u2fieldsCount];
154
155        int visibleFieldsCount = 0;
156        for (int index = 0; index < u2fieldsCount; index++)
157        {
158            LibraryField field = new LibraryField();
159            this.visitLibraryMember(libraryClass, field);
160
161            // Only store fields that are visible.
162            if (AccessUtil.accessLevel(field.getAccessFlags()) >=
163                (skipNonPublicClassMembers ? AccessUtil.PROTECTED :
164                                             AccessUtil.PACKAGE_VISIBLE))
165            {
166                reusableFields[visibleFieldsCount++] = field;
167            }
168        }
169
170        // Copy the visible fields (if any) into a fields array of the right size.
171        if (visibleFieldsCount == 0)
172        {
173            libraryClass.fields = EMPTY_LIBRARY_FIELDS;
174        }
175        else
176        {
177            libraryClass.fields = new LibraryField[visibleFieldsCount];
178            System.arraycopy(reusableFields, 0, libraryClass.fields, 0, visibleFieldsCount);
179        }
180
181        // Read the methods.
182        int u2methodsCount = dataInput.readUnsignedShort();
183
184        // Create the methods array.
185        LibraryMethod[] reusableMethods = new LibraryMethod[u2methodsCount];
186
187        int visibleMethodsCount = 0;
188        for (int index = 0; index < u2methodsCount; index++)
189        {
190            LibraryMethod method = new LibraryMethod();
191            this.visitLibraryMember(libraryClass, method);
192
193            // Only store methods that are visible.
194            if (AccessUtil.accessLevel(method.getAccessFlags()) >=
195                (skipNonPublicClassMembers ? AccessUtil.PROTECTED :
196                                             AccessUtil.PACKAGE_VISIBLE))
197            {
198                reusableMethods[visibleMethodsCount++] = method;
199            }
200        }
201
202        // Copy the visible methods (if any) into a methods array of the right size.
203        if (visibleMethodsCount == 0)
204        {
205            libraryClass.methods = EMPTY_LIBRARY_METHODS;
206        }
207        else
208        {
209            libraryClass.methods = new LibraryMethod[visibleMethodsCount];
210            System.arraycopy(reusableMethods, 0, libraryClass.methods, 0, visibleMethodsCount);
211        }
212
213        // Skip the class attributes.
214        skipAttributes();
215    }
216
217
218    // Implementations for MemberVisitor.
219
220    public void visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember)
221    {
222    }
223
224
225    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
226    {
227        // Read the general field information.
228        libraryMember.u2accessFlags = dataInput.readUnsignedShort();
229        libraryMember.name          = getString(dataInput.readUnsignedShort());
230        libraryMember.descriptor    = getString(dataInput.readUnsignedShort());
231
232        // Skip the field attributes.
233        skipAttributes();
234    }
235
236
237    // Implementations for ConstantVisitor.
238
239    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
240    {
241        dataInput.skipBytes(4);
242    }
243
244
245    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
246    {
247        dataInput.skipBytes(8);
248    }
249
250
251    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
252    {
253        dataInput.skipBytes(4);
254    }
255
256
257    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
258    {
259        dataInput.skipBytes(8);
260    }
261
262
263    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
264    {
265        dataInput.skipBytes(2);
266    }
267
268
269    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
270    {
271        int u2length = dataInput.readUnsignedShort();
272
273        // Read the UTF-8 bytes.
274        byte[] bytes = new byte[u2length];
275        dataInput.readFully(bytes);
276        utf8Constant.setBytes(bytes);
277    }
278
279
280    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
281    {
282        dataInput.skipBytes(4);
283    }
284
285
286    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
287    {
288        classConstant.u2nameIndex = dataInput.readUnsignedShort();
289    }
290
291
292    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
293    {
294        dataInput.skipBytes(4);
295    }
296
297
298    // Small utility methods.
299
300    /**
301     * Returns the class name of the ClassConstant at the specified index in the
302     * reusable constant pool.
303     */
304    private String getClassName(int constantIndex)
305    {
306        ClassConstant classEntry = (ClassConstant)constantPool[constantIndex];
307
308        return getString(classEntry.u2nameIndex);
309    }
310
311
312    /**
313     * Returns the string of the Utf8Constant at the specified index in the
314     * reusable constant pool.
315     */
316    private String getString(int constantIndex)
317    {
318        return ((Utf8Constant)constantPool[constantIndex]).getString();
319    }
320
321
322    private Constant createConstant()
323    {
324        int u1tag = dataInput.readUnsignedByte();
325
326        switch (u1tag)
327        {
328            case ClassConstants.CONSTANT_Utf8:               return new Utf8Constant();
329            case ClassConstants.CONSTANT_Integer:            return new IntegerConstant();
330            case ClassConstants.CONSTANT_Float:              return new FloatConstant();
331            case ClassConstants.CONSTANT_Long:               return new LongConstant();
332            case ClassConstants.CONSTANT_Double:             return new DoubleConstant();
333            case ClassConstants.CONSTANT_String:             return new StringConstant();
334            case ClassConstants.CONSTANT_Fieldref:           return new FieldrefConstant();
335            case ClassConstants.CONSTANT_Methodref:          return new MethodrefConstant();
336            case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant();
337            case ClassConstants.CONSTANT_Class:              return new ClassConstant();
338            case ClassConstants.CONSTANT_NameAndType:        return new NameAndTypeConstant();
339
340            default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool");
341        }
342    }
343
344
345    private void skipAttributes()
346    {
347        int u2attributesCount = dataInput.readUnsignedShort();
348
349        for (int index = 0; index < u2attributesCount; index++)
350        {
351            skipAttribute();
352        }
353    }
354
355
356    private void skipAttribute()
357    {
358        dataInput.skipBytes(2);
359        int u4attributeLength = dataInput.readInt();
360        dataInput.skipBytes(u4attributeLength);
361    }
362}
363