/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mereflect.io;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import mereflect.MEClass;
import mereflect.MEField;
import mereflect.MEMethod;
import mereflect.info.AiCode;
import mereflect.info.AiConstantValue;
import mereflect.info.AiExceptions;
import mereflect.info.AiInnerClasses;
import mereflect.info.AiLineNumberTable;
import mereflect.info.AiSourceFile;
import mereflect.info.AttributeInfo;
import mereflect.info.CiClass;
import mereflect.info.CiDouble;
import mereflect.info.CiFieldRef;
import mereflect.info.CiFloat;
import mereflect.info.CiInteger;
import mereflect.info.CiInterfaceMethodRef;
import mereflect.info.CiLong;
import mereflect.info.CiMethodRef;
import mereflect.info.CiNameAndType;
import mereflect.info.CiString;
import mereflect.info.CiUtf8;
import mereflect.info.ClassInfo;
public class DefaultClassReader implements ClassReader
{
@Override
public MEClass readClassFile(DataInputStream dis) throws IOException
{
MEClass c = new MEClass();
int magic = dis.readInt();
if (magic != 0xcafebabe) {
throw new IOException("Not a class file");
}
// Version
int majMin = dis.readInt();
c.setMajorVersion((majMin & 0xff00) >> 8);
c.setMinorVersion(majMin & 0xff);
// Constant pool
boolean corruptPool = false;
int prereadByte = 0;
int cpLen = dis.readUnsignedShort();
ClassInfo[] infos = new ClassInfo[cpLen];
for (int i = 1; i < cpLen; i++)
{
try
{
infos[i] = readClassInfo(c, dis);
if (infos[i].getTag() == ClassInfo.CONSTANT_Long ||
infos[i].getTag() == ClassInfo.CONSTANT_Double)
{
i++;
}
} catch (MissingClassInfoException mcie)
{
corruptPool = true;
prereadByte = mcie.getTag();
break;
}
}
c.setConstantPool(infos);
// Class data
int accessFlags = 0;
if (corruptPool) {
accessFlags = (prereadByte << 8) | dis.readUnsignedByte(); // Pool corrupt, we've already read a zero byte
} else {
accessFlags = dis.readUnsignedShort();
}
int thisClass = dis.readUnsignedShort();
int superClass = dis.readUnsignedShort();
c.setAccessFlags(accessFlags);
c.setThisClassIndex(thisClass);
c.setSuperClassIndex(superClass);
// Interfaces
int ifcLen = dis.readUnsignedShort();
int[] ifcs = new int[ifcLen];
for (int i = 0; i < ifcLen; i++)
{
ifcs[i] = dis.readUnsignedShort();
}
c.setInterfaceIndices(ifcs);
// Fields
int fLen = dis.readUnsignedShort();
MEField[] fields = new MEField[fLen];
for (int i = 0; i < fLen; i++)
{
fields[i] = readFieldInfo(c, dis);
}
c.setFields(fields);
// Methods
int mLen = dis.readUnsignedShort();
MEMethod[] methods = new MEMethod[mLen];
for (int i = 0; i < mLen; i++)
{
methods[i] = readMethodInfo(c, dis);
}
c.setMethods(methods);
// Attributes
AttributeInfo[] attrs = readAttributeInfos(c, dis);
c.setAttributes(attrs);
//System.out.println("[ClassParser] class:"+c.getName());
return c;
}
protected ClassInfo readClassInfo(MEClass c, DataInputStream dis) throws IOException
{
ClassInfo res = null;
int tag = dis.readUnsignedByte();
if (tag == 0 || tag == 2)
{
throw new MissingClassInfoException(tag);
}
else
{
switch (tag)
{
case ClassInfo.CONSTANT_Class:
int nameIndex = dis.readUnsignedShort();
res = new CiClass(nameIndex);
break;
case ClassInfo.CONSTANT_Fieldref:
int classIndex = dis.readUnsignedShort();
int nameAndTypeIndex = dis.readUnsignedShort();
res = new CiFieldRef(classIndex, nameAndTypeIndex);
break;
case ClassInfo.CONSTANT_Methodref:
classIndex = dis.readUnsignedShort();
nameAndTypeIndex = dis.readUnsignedShort();
res = new CiMethodRef(classIndex, nameAndTypeIndex);
break;
case ClassInfo.CONSTANT_InterfaceMethodref:
classIndex = dis.readUnsignedShort();
nameAndTypeIndex = dis.readUnsignedShort();
res = new CiInterfaceMethodRef(classIndex, nameAndTypeIndex);
break;
case ClassInfo.CONSTANT_String:
int stringIndex = dis.readUnsignedShort();
res = new CiString(stringIndex);
break;
case ClassInfo.CONSTANT_Integer:
int i = dis.readInt();
res = new CiInteger(i);
break;
case ClassInfo.CONSTANT_Float:
float f = dis.readFloat();
res = new CiFloat(f);
break;
case ClassInfo.CONSTANT_Long:
long l = dis.readLong();
res = new CiLong(l);
break;
case ClassInfo.CONSTANT_Double:
double d = dis.readDouble();
res = new CiDouble(d);
break;
case ClassInfo.CONSTANT_NameAndType:
nameIndex = dis.readUnsignedShort();
int descIndex = dis.readUnsignedShort();
res = new CiNameAndType(nameIndex, descIndex);
break;
case ClassInfo.CONSTANT_Utf8:
String utf8 = dis.readUTF();
res = new CiUtf8(utf8);
break;
default:
throw new ClassReaderException("Unknown class info tag [" + tag + "]");
}
}
return res;
}
protected MEMethod readMethodInfo(MEClass c, DataInputStream dis) throws IOException
{
MEMethod res = null;
int accessFlags = dis.readUnsignedShort();
int nameIndex = dis.readUnsignedShort();
int descIndex = dis.readUnsignedShort();
AttributeInfo[] attrs = readAttributeInfos(c, dis);
res = new MEMethod(c);
res.setAccessFlags(accessFlags);
res.setNameIndex(nameIndex);
res.setDescriptorIndex(descIndex);
res.setAttributes(attrs);
//System.out.println("[ClassParser] method:"+res.getName());
return res;
}
protected MEField readFieldInfo(MEClass c, DataInputStream dis) throws IOException
{
MEField res = null;
int accessFlags = dis.readUnsignedShort();
int nameIndex = dis.readUnsignedShort();
int descIndex = dis.readUnsignedShort();
AttributeInfo[] attrs = readAttributeInfos(c, dis);
res = new MEField(c);
res.setAccessFlags(accessFlags);
res.setNameIndex(nameIndex);
res.setDescriptorIndex(descIndex);
res.setAttributes(attrs);
//System.out.println("[ClassParser] field:"+res.getName());
return res;
}
protected AttributeInfo[] readAttributeInfos(MEClass c, DataInputStream dis) throws IOException
{
int attrLen = dis.readUnsignedShort();
AttributeInfo[] attrs = new AttributeInfo[attrLen];
for (int i = 0; i < attrLen; i++)
{
attrs[i] = readAttributeInfo(c, dis);
}
return attrs;
}
protected AttributeInfo readAttributeInfo(MEClass c, DataInputStream dis) throws IOException
{
AttributeInfo res = null;
int nameIndex = dis.readUnsignedShort();
long length = readUnsigned4(dis);
long read = 0;
boolean def = true;
if (nameIndex <= 0 || nameIndex >= c.getConstantPool().length)
{
throw new ClassReaderException("Attribute name index out of range [0 <= " +
nameIndex + " <= " + c.getConstantPool().length + "]");
}
ClassInfo ci = c.getConstantPool()[nameIndex];
if (ci != null && ci.getTag() == ClassInfo.CONSTANT_Utf8)
{
String name = ((CiUtf8) ci).getUtf8();
if (name.equals(AttributeInfo.EXCEPTIONS))
{
res = new AiExceptions();
int exLen = dis.readUnsignedShort();
read += 2;
int[] exs = new int[exLen];
for (int i = 0; i < exLen; i++)
{
exs[i] = dis.readUnsignedShort();
read += 2;
}
((AiExceptions) res).setExceptionIndices(exs);
def = false;
}
else if (name.equals(AttributeInfo.CONSTANT_VALUE))
{
res = new AiConstantValue();
int cvIndex = dis.readUnsignedShort();
read += 2;
((AiConstantValue) res).setConstantValueIndex(cvIndex);
def = false;
}
else if (name.equals(AttributeInfo.SOURCE_FILE))
{
res = new AiSourceFile();
int srcFileIndex = dis.readUnsignedShort();
read += 2;
((AiSourceFile) res).setSourceFileIndex(srcFileIndex);
def = false;
}
else if (name.equals(AttributeInfo.INNER_CLASSES))
{
res = new AiInnerClasses();
int icLen = dis.readUnsignedShort();
read += 2;
AiInnerClasses.InnerClass[] inner = new AiInnerClasses.InnerClass[icLen];
for (int i = 0; i < icLen; i++)
{
int iciIdx = dis.readUnsignedShort();
read += 2;
int ociIdx = dis.readUnsignedShort();
read += 2;
int inIdx = dis.readUnsignedShort();
read += 2;
int accessFlags = dis.readUnsignedShort();
read += 2;
inner[i] = new AiInnerClasses.InnerClass();
inner[i].setInnerClassInfoIndex(iciIdx);
inner[i].setOuterClassInfoIndex(ociIdx);
inner[i].setInnerNameIndex(inIdx);
inner[i].setAccessFlags(accessFlags);
}
((AiInnerClasses) res).setInnerClasses(inner);
def = false;
}
else if (name.equals(AttributeInfo.CODE))
{
res = new AiCode();
int maxStack = dis.readUnsignedShort();
read += 2;
int maxLocals = dis.readUnsignedShort();
read += 2;
long codeLength = readUnsigned4(dis);
read += 4;
byte[] code = new byte[(int) codeLength];
for (long i = 0; i < codeLength; i++)
{
code[(int) i] = dis.readByte();
read++;
}
int exceptionTableLen = dis.readUnsignedShort();
read += 2;
AiCode.ExceptionSpec[] exs = new AiCode.ExceptionSpec[exceptionTableLen];
for (int i = 0; i < exceptionTableLen; i++)
{
int startPc = dis.readUnsignedShort();
read += 2;
int endPc = dis.readUnsignedShort();
read += 2;
int handlerPc = dis.readUnsignedShort();
read += 2;
int catchType = dis.readUnsignedShort();
read += 2;
exs[i] = new AiCode.ExceptionSpec();
exs[i].setStartPc(startPc);
exs[i].setEndPc(endPc);
exs[i].setHandlerPc(handlerPc);
exs[i].setCatchType(catchType);
}
AttributeInfo attrs[] = readAttributeInfos(c, dis);
read += 2; // attr len
for (int i = 0; i < attrs.length; i++)
{
read += 6 + // initial 6 bytes of attribute
attrs[i].getLength(); // rest of attribute
}
((AiCode) res).setMaxStack(maxStack);
((AiCode) res).setMaxLocals(maxLocals);
((AiCode) res).setCode(code);
((AiCode) res).setExceptions(exs);
((AiCode) res).setAttributes(attrs);
def = false;
}
else if (name.equals(AttributeInfo.LINE_NUMBER_TABLE))
{
res = new AiLineNumberTable();
int lnbrLen = dis.readUnsignedShort();
read += 2;
int[][] lookup = new int[lnbrLen][2];
for (int i = 0; i < lnbrLen; i++)
{
lookup[i][0] = dis.readUnsignedShort();
read += 2;
lookup[i][1] = dis.readUnsignedShort();
read += 2;
}
((AiLineNumberTable) res).set(lookup);
def = false;
}
}
if (def)
{
byte[] info = new byte[(int) length];
for (long i = 0; i < length; i++)
{
info[(int) i] = dis.readByte();
}
res = new AttributeInfo();
res.setInfo(info);
}
else
{
long left = length - read;
if (left > 0)
{
while (left-- > 0)
{
dis.read();
}
}
}
res.setNameIndex(nameIndex);
res.setLength(length);
return res;
}
protected long readUnsigned4(InputStream is) throws IOException
{
long l = 0;
for (int i = 0; i < 4; i++)
{
l |= (is.read() & 0xff);
if (i < 3) {
l <<= 8;
}
}
return l;
}
}