/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.classfile.io; import proguard.classfile.*; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.*; import java.io.DataInput; /** * This ClassVisitor fills out the LibraryClass objects that it visits with data * from the given DataInput object. * * @author Eric Lafortune */ public class LibraryClassReader extends SimplifiedVisitor implements ClassVisitor, MemberVisitor, ConstantVisitor { private static final LibraryField[] EMPTY_LIBRARY_FIELDS = new LibraryField[0]; private static final LibraryMethod[] EMPTY_LIBRARY_METHODS = new LibraryMethod[0]; private final RuntimeDataInput dataInput; private final boolean skipNonPublicClasses; private final boolean skipNonPublicClassMembers; // A global array that acts as a parameter for the visitor methods. private Constant[] constantPool; /** * Creates a new ProgramClassReader for reading from the given DataInput. */ public LibraryClassReader(DataInput dataInput, boolean skipNonPublicClasses, boolean skipNonPublicClassMembers) { this.dataInput = new RuntimeDataInput(dataInput); this.skipNonPublicClasses = skipNonPublicClasses; this.skipNonPublicClassMembers = skipNonPublicClassMembers; } // Implementations for ClassVisitor. public void visitProgramClass(ProgramClass libraryClass) { } public void visitLibraryClass(LibraryClass libraryClass) { // Read and check the magic number. int u4magic = dataInput.readInt(); ClassUtil.checkMagicNumber(u4magic); // Read and check the version numbers. int u2minorVersion = dataInput.readUnsignedShort(); int u2majorVersion = dataInput.readUnsignedShort(); int u4version = ClassUtil.internalClassVersion(u2majorVersion, u2minorVersion); ClassUtil.checkVersionNumbers(u4version); // Read the constant pool. Note that the first entry is not used. int u2constantPoolCount = dataInput.readUnsignedShort(); // Create the constant pool array. constantPool = new Constant[u2constantPoolCount]; for (int index = 1; index < u2constantPoolCount; index++) { Constant constant = createConstant(); constant.accept(libraryClass, this); int tag = constant.getTag(); if (tag == ClassConstants.CONSTANT_Class || tag == ClassConstants.CONSTANT_Utf8) { constantPool[index] = constant; } // Long constants and double constants take up two entries in the // constant pool. if (tag == ClassConstants.CONSTANT_Long || tag == ClassConstants.CONSTANT_Double) { index++; } } // Read the general class information. libraryClass.u2accessFlags = dataInput.readUnsignedShort(); // We may stop parsing this library class if it's not public anyway. // E.g. only about 60% of all rt.jar classes need to be parsed. if (skipNonPublicClasses && AccessUtil.accessLevel(libraryClass.getAccessFlags()) < AccessUtil.PUBLIC) { return; } // Read the class and super class indices. int u2thisClass = dataInput.readUnsignedShort(); int u2superClass = dataInput.readUnsignedShort(); // Store their actual names. libraryClass.thisClassName = getClassName(u2thisClass); libraryClass.superClassName = (u2superClass == 0) ? null : getClassName(u2superClass); // Read the interfaces int u2interfacesCount = dataInput.readUnsignedShort(); libraryClass.interfaceNames = new String[u2interfacesCount]; for (int index = 0; index < u2interfacesCount; index++) { // Store the actual interface name. int u2interface = dataInput.readUnsignedShort(); libraryClass.interfaceNames[index] = getClassName(u2interface); } // Read the fields. int u2fieldsCount = dataInput.readUnsignedShort(); // Create the fields array. LibraryField[] reusableFields = new LibraryField[u2fieldsCount]; int visibleFieldsCount = 0; for (int index = 0; index < u2fieldsCount; index++) { LibraryField field = new LibraryField(); this.visitLibraryMember(libraryClass, field); // Only store fields that are visible. if (AccessUtil.accessLevel(field.getAccessFlags()) >= (skipNonPublicClassMembers ? AccessUtil.PROTECTED : AccessUtil.PACKAGE_VISIBLE)) { reusableFields[visibleFieldsCount++] = field; } } // Copy the visible fields (if any) into a fields array of the right size. if (visibleFieldsCount == 0) { libraryClass.fields = EMPTY_LIBRARY_FIELDS; } else { libraryClass.fields = new LibraryField[visibleFieldsCount]; System.arraycopy(reusableFields, 0, libraryClass.fields, 0, visibleFieldsCount); } // Read the methods. int u2methodsCount = dataInput.readUnsignedShort(); // Create the methods array. LibraryMethod[] reusableMethods = new LibraryMethod[u2methodsCount]; int visibleMethodsCount = 0; for (int index = 0; index < u2methodsCount; index++) { LibraryMethod method = new LibraryMethod(); this.visitLibraryMember(libraryClass, method); // Only store methods that are visible. if (AccessUtil.accessLevel(method.getAccessFlags()) >= (skipNonPublicClassMembers ? AccessUtil.PROTECTED : AccessUtil.PACKAGE_VISIBLE)) { reusableMethods[visibleMethodsCount++] = method; } } // Copy the visible methods (if any) into a methods array of the right size. if (visibleMethodsCount == 0) { libraryClass.methods = EMPTY_LIBRARY_METHODS; } else { libraryClass.methods = new LibraryMethod[visibleMethodsCount]; System.arraycopy(reusableMethods, 0, libraryClass.methods, 0, visibleMethodsCount); } // Skip the class attributes. skipAttributes(); } // Implementations for MemberVisitor. public void visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember) { } public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) { // Read the general field information. libraryMember.u2accessFlags = dataInput.readUnsignedShort(); libraryMember.name = getString(dataInput.readUnsignedShort()); libraryMember.descriptor = getString(dataInput.readUnsignedShort()); // Skip the field attributes. skipAttributes(); } // Implementations for ConstantVisitor. public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) { dataInput.skipBytes(4); } public void visitLongConstant(Clazz clazz, LongConstant longConstant) { dataInput.skipBytes(8); } public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) { dataInput.skipBytes(4); } public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) { dataInput.skipBytes(8); } public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { dataInput.skipBytes(2); } public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) { int u2length = dataInput.readUnsignedShort(); // Read the UTF-8 bytes. byte[] bytes = new byte[u2length]; dataInput.readFully(bytes); utf8Constant.setBytes(bytes); } public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { dataInput.skipBytes(4); } public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) { dataInput.skipBytes(3); } public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) { dataInput.skipBytes(4); } public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { classConstant.u2nameIndex = dataInput.readUnsignedShort(); } public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) { dataInput.skipBytes(2); } public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) { dataInput.skipBytes(4); } // Small utility methods. /** * Returns the class name of the ClassConstant at the specified index in the * reusable constant pool. */ private String getClassName(int constantIndex) { ClassConstant classEntry = (ClassConstant)constantPool[constantIndex]; return getString(classEntry.u2nameIndex); } /** * Returns the string of the Utf8Constant at the specified index in the * reusable constant pool. */ private String getString(int constantIndex) { return ((Utf8Constant)constantPool[constantIndex]).getString(); } private Constant createConstant() { int u1tag = dataInput.readUnsignedByte(); switch (u1tag) { case ClassConstants.CONSTANT_Integer: return new IntegerConstant(); case ClassConstants.CONSTANT_Float: return new FloatConstant(); case ClassConstants.CONSTANT_Long: return new LongConstant(); case ClassConstants.CONSTANT_Double: return new DoubleConstant(); case ClassConstants.CONSTANT_String: return new StringConstant(); case ClassConstants.CONSTANT_Utf8: return new Utf8Constant(); case ClassConstants.CONSTANT_InvokeDynamic: return new InvokeDynamicConstant(); case ClassConstants.CONSTANT_MethodHandle: return new MethodHandleConstant(); case ClassConstants.CONSTANT_Fieldref: return new FieldrefConstant(); case ClassConstants.CONSTANT_Methodref: return new MethodrefConstant(); case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant(); case ClassConstants.CONSTANT_Class: return new ClassConstant(); case ClassConstants.CONSTANT_MethodType : return new MethodTypeConstant(); case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); } } private void skipAttributes() { int u2attributesCount = dataInput.readUnsignedShort(); for (int index = 0; index < u2attributesCount; index++) { skipAttribute(); } } private void skipAttribute() { dataInput.skipBytes(2); int u4attributeLength = dataInput.readInt(); dataInput.skipBytes(u4attributeLength); } }