blob: a9a4e8e9783f4495e103bc73767460145dad88a8 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2014 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);
}
}