/* * Copyright 2008-2010 Brian S O'Neill * * 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 org.cojen.classfile.attribute; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.cojen.classfile.Attribute; import org.cojen.classfile.ConstantPool; import org.cojen.classfile.MethodInfo; import org.cojen.classfile.TypeDesc; import org.cojen.classfile.constant.ConstantClassInfo; /** * * * @author Brian S O'Neill */ public class StackMapTableAttr extends Attribute { private final InitialFrame mInitialFrame; private int mSize; private int mLength; public StackMapTableAttr(ConstantPool cp, String name, int length, DataInput din) throws IOException { super(cp, name); int size = din.readUnsignedShort(); List<StackMapFrame> frames = new ArrayList<StackMapFrame>(size); InitialFrame first = new InitialFrame(); StackMapFrame last = first; for (int i=0; i<size; i++) { StackMapFrame frame = StackMapFrame.read(last, cp, din); if (frame != null) { last = frame; } } mInitialFrame = first; mSize = size; mLength = length; } public int getLength() { if (mLength < 0) { if (mInitialFrame.getNext() == null) { mLength = 0; } else { int length = 2; StackMapFrame frame = mInitialFrame; while (frame != null) { length += frame.getLength(); frame = frame.getNext(); } mLength = length; } } return mLength; } @Override public void writeTo(DataOutput dout) throws IOException { if (mSize == 0) { return; } super.writeTo(dout); } @Override public void writeDataTo(DataOutput dout) throws IOException { dout.writeShort(mSize); StackMapFrame frame = mInitialFrame; while (frame != null) { frame.writeTo(dout); frame = frame.getNext(); } } public StackMapFrame getInitialFrame() { return mInitialFrame; } public void initialStackMapFrame(MethodInfo method) { mInitialFrame.set(getConstantPool(), method); } public static abstract class StackMapFrame { static StackMapFrame read(StackMapFrame prev, ConstantPool cp, DataInput din) throws IOException { int frameType = din.readUnsignedByte(); if (frameType <= 63) { return new SameFrame(prev, frameType); } else if (frameType <= 127) { return new SameLocalsOneStackItemFrame(prev, frameType - 64, cp, din); } else if (frameType <= 246) { // reserved frame type range return null; } else if (frameType == 247) { return new SameLocalsOneStackItemFrameExtended(prev, cp, din); } else if (frameType <= 250) { return new ChopFrame(prev, 251 - frameType, din); } else if (frameType == 251) { return new SameFrameExtended(prev, din); } else if (frameType <= 254) { return new AppendFrame(prev, frameType - 251, cp, din); } else { return new FullFrame(prev, cp, din); } } private final StackMapFrame mPrev; private StackMapFrame mNext; int mOffset = -1; StackMapFrame(StackMapFrame prev) { if ((mPrev = prev) != null) { prev.mNext = this; } } public abstract int getLength(); public abstract int getOffsetDelta(); public int getOffset() { if (mOffset < 0) { if (mPrev == null || mPrev instanceof InitialFrame) { mOffset = getOffsetDelta(); } else { mOffset = mPrev.getOffset() + 1 + getOffsetDelta(); } } return mOffset; } public abstract VerificationTypeInfo[] getLocalInfos(); public abstract VerificationTypeInfo[] getStackItemInfos(); public StackMapFrame getPrevious() { return mPrev; } public StackMapFrame getNext() { return mNext; } public abstract void writeTo(DataOutput dout) throws IOException; } private static class InitialFrame extends StackMapFrame { private VerificationTypeInfo[] mLocalInfos; InitialFrame() { super(null); mOffset = 0; } public int getLength() { return 0; } public int getOffsetDelta() { return 0; } public VerificationTypeInfo[] getLocalInfos() { if (mLocalInfos == null) { return VerificationTypeInfo.EMPTY_ARRAY; } return mLocalInfos.clone(); } public VerificationTypeInfo[] getStackItemInfos() { return VerificationTypeInfo.EMPTY_ARRAY; } public void writeTo(DataOutput dout) { } void set(ConstantPool cp, MethodInfo info) { TypeDesc[] paramTypes = info.getMethodDescriptor().getParameterTypes(); VerificationTypeInfo[] infos; int offset; if (info.getModifiers().isStatic()) { infos = new VerificationTypeInfo[paramTypes.length]; offset = 0; } else { infos = new VerificationTypeInfo[1 + paramTypes.length]; if (info.getName().equals("<init>")) { infos[0] = UninitThisVariableInfo.THE; } else { infos[0] = VerificationTypeInfo.forType(cp, info.getClassFile().getType()); } offset = 1; } for (int i=0; i<paramTypes.length; i++) { infos[offset + i] = VerificationTypeInfo.forType(cp, paramTypes[i]); } mLocalInfos = infos; } } private static class SameFrame extends StackMapFrame { private final int mOffsetDelta; SameFrame(StackMapFrame prev, int offsetDelta) { super(prev); mOffsetDelta = offsetDelta; } public int getLength() { return 1; } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { return getPrevious().getLocalInfos(); } public VerificationTypeInfo[] getStackItemInfos() { return VerificationTypeInfo.EMPTY_ARRAY; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(mOffsetDelta); } } private static class SameLocalsOneStackItemFrame extends StackMapFrame { private final int mOffsetDelta; private final VerificationTypeInfo mStackItemInfo; SameLocalsOneStackItemFrame(StackMapFrame prev, int offsetDelta, ConstantPool cp, DataInput din) throws IOException { super(prev); mOffsetDelta = offsetDelta; mStackItemInfo = VerificationTypeInfo.read(cp, din); } public int getLength() { return 1 + mStackItemInfo.getLength(); } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { return getPrevious().getLocalInfos(); } public VerificationTypeInfo[] getStackItemInfos() { return new VerificationTypeInfo[]{mStackItemInfo}; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(64 + mOffsetDelta); mStackItemInfo.writeTo(dout); } } private static class SameLocalsOneStackItemFrameExtended extends StackMapFrame { private final int mOffsetDelta; private final VerificationTypeInfo mStackItemInfo; SameLocalsOneStackItemFrameExtended(StackMapFrame prev, ConstantPool cp, DataInput din) throws IOException { super(prev); mOffsetDelta = din.readUnsignedShort(); mStackItemInfo = VerificationTypeInfo.read(cp, din); } public int getLength() { return 3 + mStackItemInfo.getLength(); } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { return getPrevious().getLocalInfos(); } public VerificationTypeInfo[] getStackItemInfos() { return new VerificationTypeInfo[]{mStackItemInfo}; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(257); dout.writeShort(mOffsetDelta); mStackItemInfo.writeTo(dout); } } private static class ChopFrame extends StackMapFrame { private final int mOffsetDelta; private final int mChop; private transient VerificationTypeInfo[] mLocalInfos; ChopFrame(StackMapFrame prev, int chop, DataInput din) throws IOException { super(prev); mOffsetDelta = din.readUnsignedShort(); mChop = chop; } public int getLength() { return 3; } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { if (mLocalInfos == null) { VerificationTypeInfo[] prevInfos = getPrevious().getLocalInfos(); VerificationTypeInfo[] infos = new VerificationTypeInfo[prevInfos.length - mChop]; System.arraycopy(prevInfos, 0, infos, 0, infos.length); mLocalInfos = infos; } return mLocalInfos.clone(); } public VerificationTypeInfo[] getStackItemInfos() { return VerificationTypeInfo.EMPTY_ARRAY; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(251 - mChop); dout.writeShort(mOffsetDelta); } } private static class SameFrameExtended extends StackMapFrame { private final int mOffsetDelta; SameFrameExtended(StackMapFrame prev, DataInput din) throws IOException { super(prev); mOffsetDelta = din.readUnsignedShort(); } public int getLength() { return 3; } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { return getPrevious().getLocalInfos(); } public VerificationTypeInfo[] getStackItemInfos() { return VerificationTypeInfo.EMPTY_ARRAY; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(251); dout.writeShort(mOffsetDelta); } } private static class AppendFrame extends StackMapFrame { private final int mOffsetDelta; private final VerificationTypeInfo[] mAppendInfos; private transient VerificationTypeInfo[] mLocalInfos; AppendFrame(StackMapFrame prev, int numLocals, ConstantPool cp, DataInput din) throws IOException { super(prev); mOffsetDelta = din.readUnsignedShort(); mAppendInfos = VerificationTypeInfo.read(cp, din, numLocals); } public int getLength() { int length = 3; for (VerificationTypeInfo info : mAppendInfos) { length += info.getLength(); } return length; } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { if (mLocalInfos == null) { VerificationTypeInfo[] prevInfos = getPrevious().getLocalInfos(); VerificationTypeInfo[] infos = new VerificationTypeInfo[prevInfos.length + mAppendInfos.length]; System.arraycopy(prevInfos, 0, infos, 0, prevInfos.length); System.arraycopy(mAppendInfos, 0, infos, prevInfos.length, mAppendInfos.length); mLocalInfos = infos; } return mLocalInfos.clone(); } public VerificationTypeInfo[] getStackItemInfos() { return VerificationTypeInfo.EMPTY_ARRAY; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(251 + mAppendInfos.length); dout.writeShort(mOffsetDelta); for (VerificationTypeInfo info : mAppendInfos) { info.writeTo(dout); } } } private static class FullFrame extends StackMapFrame { private final int mOffsetDelta; private final VerificationTypeInfo[] mLocalInfos; private final VerificationTypeInfo[] mStackItemInfos; FullFrame(StackMapFrame prev, ConstantPool cp, DataInput din) throws IOException { super(prev); mOffsetDelta = din.readUnsignedShort(); int numLocals = din.readUnsignedShort(); mLocalInfos = VerificationTypeInfo.read(cp, din, numLocals); int numStackItems = din.readUnsignedShort(); mStackItemInfos = VerificationTypeInfo.read(cp, din, numStackItems); } public int getLength() { int length = 7; for (VerificationTypeInfo info : mLocalInfos) { length += info.getLength(); } for (VerificationTypeInfo info : mStackItemInfos) { length += info.getLength(); } return length; } public int getOffsetDelta() { return mOffsetDelta; } public VerificationTypeInfo[] getLocalInfos() { return mLocalInfos.clone(); } public VerificationTypeInfo[] getStackItemInfos() { return mStackItemInfos.clone(); } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(255); dout.writeShort(mOffsetDelta); dout.writeShort(mLocalInfos.length); for (VerificationTypeInfo info : mLocalInfos) { info.writeTo(dout); } dout.writeShort(mStackItemInfos.length); for (VerificationTypeInfo info : mStackItemInfos) { info.writeTo(dout); } } } public static abstract class VerificationTypeInfo { static final VerificationTypeInfo[] EMPTY_ARRAY = new VerificationTypeInfo[0]; static VerificationTypeInfo read(ConstantPool cp, DataInput din) throws IOException { int type = din.readUnsignedByte(); switch (type) { case 0: return TopVariableInfo.THE; case 1: return IntegerVariableInfo.THE; case 2: return FloatVariableInfo.THE; case 3: return DoubleVariableInfo.THE; case 4: return LongVariableInfo.THE; case 5: return NullVariableInfo.THE; case 6: return UninitThisVariableInfo.THE; case 7: return new ObjectVariableInfo(cp, din); case 8: return new UninitVariableInfo(cp, din); } return null; } static VerificationTypeInfo forType(ConstantPool cp, TypeDesc type) { switch (type.getTypeCode()) { default: return TopVariableInfo.THE; case TypeDesc.OBJECT_CODE: return new ObjectVariableInfo(cp, type); case TypeDesc.BOOLEAN_CODE: case TypeDesc.BYTE_CODE: case TypeDesc.CHAR_CODE: case TypeDesc.SHORT_CODE: case TypeDesc.INT_CODE: return IntegerVariableInfo.THE; case TypeDesc.LONG_CODE: return LongVariableInfo.THE; case TypeDesc.FLOAT_CODE: return FloatVariableInfo.THE; case TypeDesc.DOUBLE_CODE: return DoubleVariableInfo.THE; } } private static VerificationTypeInfo[] read(ConstantPool cp, DataInput din, int num) throws IOException { VerificationTypeInfo[] infos = new VerificationTypeInfo[num]; for (int i=0; i<num; i++) { infos[i] = read(cp, din); } return infos; } VerificationTypeInfo() { } public int getLength() { return 1; } public abstract TypeDesc getType(); /** * When true, type is unassigned or unused. */ public boolean isTop() { return false; } /** * When true, type is the "this" reference. */ public boolean isThis() { return false; } /** * When true, type is an object whose constructor has not been called. */ public boolean isUninitialized() { return false; } public abstract void writeTo(DataOutput dout) throws IOException; @Override public String toString() { return getType().getFullName(); } } private static class TopVariableInfo extends VerificationTypeInfo { static final TopVariableInfo THE = new TopVariableInfo(); private TopVariableInfo() { } public TypeDesc getType() { return null; } @Override public boolean isTop() { return true; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(0); } @Override public String toString() { return "<top>"; } } private static class IntegerVariableInfo extends VerificationTypeInfo { static final IntegerVariableInfo THE = new IntegerVariableInfo(); private IntegerVariableInfo() { } public TypeDesc getType() { return TypeDesc.INT; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(1); } } private static class FloatVariableInfo extends VerificationTypeInfo { static final FloatVariableInfo THE = new FloatVariableInfo(); private FloatVariableInfo() { } public TypeDesc getType() { return TypeDesc.FLOAT; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(2); } } private static class LongVariableInfo extends VerificationTypeInfo { static final LongVariableInfo THE = new LongVariableInfo(); private LongVariableInfo() { } public TypeDesc getType() { return TypeDesc.LONG; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(4); } } private static class DoubleVariableInfo extends VerificationTypeInfo { static final DoubleVariableInfo THE = new DoubleVariableInfo(); private DoubleVariableInfo() { } public TypeDesc getType() { return TypeDesc.DOUBLE; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(3); } } private static class NullVariableInfo extends VerificationTypeInfo { static final NullVariableInfo THE = new NullVariableInfo(); private NullVariableInfo() { } public TypeDesc getType() { return null; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(5); } @Override public String toString() { return "<null>"; } } private static class UninitThisVariableInfo extends VerificationTypeInfo { static final UninitThisVariableInfo THE = new UninitThisVariableInfo(); private UninitThisVariableInfo() { } public TypeDesc getType() { return null; } @Override public boolean isThis() { return true; } @Override public boolean isUninitialized() { return true; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(6); } @Override public String toString() { return "<uninitialized this>"; } } private static class ObjectVariableInfo extends VerificationTypeInfo { private final ConstantClassInfo mClassInfo; ObjectVariableInfo(ConstantPool cp, DataInput din) throws IOException { mClassInfo = (ConstantClassInfo) cp.getConstant(din.readUnsignedShort()); } ObjectVariableInfo(ConstantPool cp, TypeDesc desc) { mClassInfo = cp.addConstantClass(desc); } @Override public int getLength() { return 3; } public TypeDesc getType() { return mClassInfo.getType(); } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(7); dout.writeShort(mClassInfo.getIndex()); } } private static class UninitVariableInfo extends VerificationTypeInfo { private final int mOffset; UninitVariableInfo(ConstantPool cp, DataInput din) throws IOException { mOffset = din.readUnsignedShort(); } @Override public int getLength() { return 3; } public TypeDesc getType() { return null; } @Override public boolean isUninitialized() { return true; } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(8); dout.writeShort(mOffset); } @Override public String toString() { return "<uninitialized>"; } } }