/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2011, Stefan Hepp (stefan@stefant.org).
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common.bcel;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ConstantPool;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Attribute defined in JSR202
*
* @author Stefan Hepp (stefan@stefant.org)
*/
public class StackMapTable extends CustomAttribute {
public static class VerificationTypeInfo {
private int tag;
private int cp_index;
private int offset;
public VerificationTypeInfo(DataInputStream file) throws IOException {
tag = file.readUnsignedByte();
if (isObject()) {
cp_index = file.readUnsignedShort();
}
if (isUninitialized()) {
offset = file.readUnsignedShort();
}
}
public void dump(DataOutputStream file) throws IOException {
file.writeByte(tag);
if (isObject()) {
file.writeShort(cp_index);
}
if (isUninitialized()) {
file.writeShort(offset);
}
}
public int getLength() {
int length = 1;
if (isObject() || isUninitialized()) {
length += 2;
}
return length;
}
public int getCPIndex() { return cp_index; }
public int getOffset() { return offset; }
public boolean isTop() { return tag == 0; }
public boolean isInteger() { return tag == 1; }
public boolean isFloat() { return tag == 2; }
public boolean isLong() { return tag == 4; }
public boolean isDouble() { return tag == 3; }
public boolean isNull() { return tag == 5; }
public boolean isUninitializedThis() { return tag == 6; }
public boolean isObject() { return tag == 7; }
public boolean isUninitialized() { return tag == 8; }
}
public static class StackMapFrame {
private int tag;
private int offset_delta;
private List<VerificationTypeInfo> stack, locals;
public StackMapFrame(DataInputStream file, ConstantPool constant_pool) throws IOException {
tag = file.readUnsignedByte();
if (isSameLocalsOneStackItemFrame()) {
stack = new ArrayList<VerificationTypeInfo>(1);
stack.add(new VerificationTypeInfo(file));
}
if (isSameLocalsOneStackItemFrameExtended()) {
offset_delta = file.readUnsignedShort();
stack = new ArrayList<VerificationTypeInfo>(1);
stack.add(new VerificationTypeInfo(file));
}
if (isChopFrame() || isSameFrameExtended()) {
offset_delta = file.readUnsignedShort();
}
if (isAppendFrame()) {
offset_delta = file.readUnsignedShort();
int len = tag - 251;
locals = new ArrayList<VerificationTypeInfo>(len);
for (int i = 0; i < len; i++) {
locals.add(new VerificationTypeInfo(file));
}
}
if (isFullFrame()) {
offset_delta = file.readUnsignedShort();
int len;
len = file.readUnsignedShort();
locals = new ArrayList<VerificationTypeInfo>(len);
for (int i = 0; i < len; i++) {
locals.add(new VerificationTypeInfo(file));
}
len = file.readUnsignedShort();
stack = new ArrayList<VerificationTypeInfo>(len);
for (int i = 0; i < len; i++) {
stack.add(new VerificationTypeInfo(file));
}
}
}
public int getLength() {
int length = 1;
if (isSameLocalsOneStackItemFrame()) {
length += stack.get(0).getLength();
}
if (isSameLocalsOneStackItemFrameExtended()) {
length += 2;
length += stack.get(0).getLength();
}
if (isChopFrame() || isSameFrameExtended()) {
length += 2;
}
if (isAppendFrame()) {
length += 2;
for (VerificationTypeInfo t : locals) {
length += t.getLength();
}
}
if (isFullFrame()) {
length += 6;
for (VerificationTypeInfo t : locals) {
length += t.getLength();
}
for (VerificationTypeInfo t : stack) {
length += t.getLength();
}
}
return length;
}
public void dump(DataOutputStream file) throws IOException {
file.writeByte(tag);
if (isSameLocalsOneStackItemFrame()) {
stack.get(0).dump(file);
}
if (isSameLocalsOneStackItemFrameExtended()) {
file.writeShort(offset_delta);
stack.get(0).dump(file);
}
if (isChopFrame() || isSameFrameExtended()) {
file.writeShort(offset_delta);
}
if (isAppendFrame()) {
file.writeShort(offset_delta);
for (VerificationTypeInfo t : locals) {
t.dump(file);
}
}
if (isFullFrame()) {
file.writeShort(offset_delta);
file.writeShort(locals.size());
for (VerificationTypeInfo t : locals) {
t.dump(file);
}
file.writeShort(stack.size());
for (VerificationTypeInfo t : stack) {
t.dump(file);
}
}
}
public int getOffsetDelta() {
return offset_delta;
}
public List<VerificationTypeInfo> getStack() {
return stack;
}
public List<VerificationTypeInfo> getLocals() {
return locals;
}
public boolean isSameFrame() { return tag >= 0 && tag <= 63; }
public boolean isSameLocalsOneStackItemFrame() { return tag >= 64 && tag <=127; }
public boolean isSameLocalsOneStackItemFrameExtended() { return tag == 247; }
public boolean isChopFrame() { return tag >= 248 && tag <= 250; }
public boolean isSameFrameExtended() { return tag == 251; }
public boolean isAppendFrame() { return tag >= 252 && tag <= 254; }
public boolean isFullFrame() { return tag == 255; }
}
private final List<StackMapFrame> entries;
public StackMapTable(int name_index, int length, DataInputStream file, ConstantPool constant_pool) throws IOException {
super(Constants.ATTR_UNKNOWN, name_index, length, constant_pool);
int num = file.readUnsignedShort();
entries = new ArrayList<StackMapFrame>(num);
for (int i = 0; i < num; i++) {
entries.add(new StackMapFrame(file, constant_pool));
}
}
public StackMapTable(int name_index, int length, List<StackMapFrame> entries, ConstantPool constant_pool) {
super(Constants.ATTR_UNKNOWN, name_index, length, constant_pool);
this.entries = new ArrayList<StackMapFrame>(entries);
}
public List<StackMapFrame> getEntries() {
return entries;
}
@Override
public void dump(DataOutputStream file) throws IOException {
updateLength();
super.dump(file);
file.writeShort(entries.size());
for (StackMapFrame f : entries) {
f.dump(file);
}
}
@Override
public String toString() {
return "StackMapTable";
}
@Override
public Attribute copy(ConstantPool _constant_pool) {
// TODO deep copy
List<StackMapFrame> newEntries = new ArrayList<StackMapFrame>(entries);
return new StackMapTable(name_index, length, newEntries, _constant_pool);
}
@Override
public Collection<Integer> getConstantPoolIDs() {
Set<Integer> ids = new HashSet<Integer>();
for (StackMapFrame f : entries) {
if (f.getLocals() != null) {
for (VerificationTypeInfo t : f.getLocals()) {
if (t.isObject()) {
ids.add(t.getCPIndex());
}
}
}
if (f.getStack() != null) {
for (VerificationTypeInfo t : f.getStack()) {
if (t.isObject()) {
ids.add(t.getCPIndex());
}
}
}
}
return ids;
}
public void updateLength() {
int length = 2;
for (StackMapFrame f : entries) {
length += f.getLength();
}
setLength(length);
}
}