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.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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
281    {
282        dataInput.skipBytes(4);
283    }
284
285
286    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
287    {
288        dataInput.skipBytes(3);
289    }
290
291
292    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
293    {
294        dataInput.skipBytes(4);
295    }
296
297
298    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
299    {
300        classConstant.u2nameIndex = dataInput.readUnsignedShort();
301    }
302
303
304    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
305    {
306        dataInput.skipBytes(2);
307    }
308
309
310    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
311    {
312        dataInput.skipBytes(4);
313    }
314
315
316    // Small utility methods.
317
318    /**
319     * Returns the class name of the ClassConstant at the specified index in the
320     * reusable constant pool.
321     */
322    private String getClassName(int constantIndex)
323    {
324        ClassConstant classEntry = (ClassConstant)constantPool[constantIndex];
325
326        return getString(classEntry.u2nameIndex);
327    }
328
329
330    /**
331     * Returns the string of the Utf8Constant at the specified index in the
332     * reusable constant pool.
333     */
334    private String getString(int constantIndex)
335    {
336        return ((Utf8Constant)constantPool[constantIndex]).getString();
337    }
338
339
340    private Constant createConstant()
341    {
342        int u1tag = dataInput.readUnsignedByte();
343
344        switch (u1tag)
345        {
346            case ClassConstants.CONSTANT_Integer:            return new IntegerConstant();
347            case ClassConstants.CONSTANT_Float:              return new FloatConstant();
348            case ClassConstants.CONSTANT_Long:               return new LongConstant();
349            case ClassConstants.CONSTANT_Double:             return new DoubleConstant();
350            case ClassConstants.CONSTANT_String:             return new StringConstant();
351            case ClassConstants.CONSTANT_Utf8:               return new Utf8Constant();
352            case ClassConstants.CONSTANT_InvokeDynamic:      return new InvokeDynamicConstant();
353            case ClassConstants.CONSTANT_MethodHandle:       return new MethodHandleConstant();
354            case ClassConstants.CONSTANT_Fieldref:           return new FieldrefConstant();
355            case ClassConstants.CONSTANT_Methodref:          return new MethodrefConstant();
356            case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant();
357            case ClassConstants.CONSTANT_Class:              return new ClassConstant();
358            case ClassConstants.CONSTANT_MethodType:         return new MethodTypeConstant();
359            case ClassConstants.CONSTANT_NameAndType:        return new NameAndTypeConstant();
360
361            default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool");
362        }
363    }
364
365
366    private void skipAttributes()
367    {
368        int u2attributesCount = dataInput.readUnsignedShort();
369
370        for (int index = 0; index < u2attributesCount; index++)
371        {
372            skipAttribute();
373        }
374    }
375
376
377    private void skipAttribute()
378    {
379        dataInput.skipBytes(2);
380        int u4attributeLength = dataInput.readInt();
381        dataInput.skipBytes(u4attributeLength);
382    }
383}
384