/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.classfile;
import static com.sun.max.vm.classfile.ErrorContext.*;
import java.io.*;
import com.sun.max.program.*;
import com.sun.max.vm.classfile.constant.*;
import com.sun.max.vm.classfile.stackmap.*;
import com.sun.max.vm.verifier.*;
import com.sun.max.vm.verifier.types.*;
/**
* From <a href="http://jcp.org/en/jsr/detail?id=202">JSR 202</a>:
* <p>
* The stack map attribute is a variable-length attribute in the attributes table of a Code attribute. The name of the
* attribute is StackMapTable. This attribute is used during the process of
* {@linkplain TypeCheckingVerifier verification by typechecking}.
* <p>
* A stack map attribute consists of zero or more stack map frames. Each stack map frame specifies (either explicitly or
* implicitly) a BCI, the {@linkplain VerificationType verification types} for the local variables, and
* the verification types for the operand stack.
* <p>
* The type checker deals with and manipulates the expected types of a method's local variables and operand stack.
* Throughout this section, a location refers to either a single local variable or to a single operand stack entry. We
* will use the terms stack map frame and type state interchangeably to describe a mapping from locations in the operand
* stack and local variables of a method to verification types. We will usually use the term stack map frame when such a
* mapping is provided in the class file, and the term type state when the mapping is inferred by the type checker.
* <p>
* If a method's Code attribute does not have a StackMapTable attribute, it has an implicit stack map attribute. This
* implicit stack map attribute is equivalent to a StackMapTable attribute with number_of_entries equal to zero. A
* method's Code attribute may have at most one StackMapTable attribute, otherwise a ClassFormatError is thrown.
* <p>
* The format of the stack map in the class file is given below.
* <p>
*
* <pre>
* stack_map {
* u2 attribute_name_index;
* u4 attribute_length
* u2 number_of_entries;
* stack_map_frame entries[number_of_entries];
* }
* </pre>
*
* <p>
* Each stack_map_frame structure specifies the type state at a particular BCI. Each frame type specifies
* (explicitly or implicitly) a {@link StackMapFrame#bciDelta() delta} that is used to calculate the actual
* BCI at which it applies. The BCI at which the frame applies is given by adding
* {@code 1 + delta} to the BCI of the previous frame, unless the previous frame is the initial frame of the
* method, in which case the BCI is {@code delta}.
*/
public class StackMapTable {
public static final int SAME_FRAME_BOUND = 64;
public static final int SAME_LOCALS_1_STACK_ITEM_BOUND = 128;
public static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
public static final int SAME_FRAME_EXTENDED = 251;
public static final int FULL_FRAME = 255;
public static final StackMapFrame[] NO_STACK_MAP_FRAMES = {};
private final byte[] attributeData;
/**
* Gets the stack map frames defined by this attribute.
* <p>
* This implementation parses the bytes of the attribute each time this method is called and so the result should be
* cached if it is to be read more than once.
*
* @param registry
* used to create specific {@linkplain ReferenceType reference types}. Can be null in which case
* {@link VerificationType#REFERENCE} is used in the returned frames
*/
public final StackMapFrame[] getFrames(VerificationRegistry registry) {
return readFrames(registry, attributeData);
}
private static StackMapFrame[] readFrames(VerificationRegistry registry, byte[] attributeData) {
final ClassfileStream classfileStream = new ClassfileStream(attributeData);
final int numberOfEntries = classfileStream.readUnsigned2();
if (numberOfEntries == 0) {
return NO_STACK_MAP_FRAMES;
}
final StackMapFrame[] entries = new StackMapFrame[numberOfEntries];
for (int i = 0; i < numberOfEntries; i++) {
entries[i] = readStackMapFrame(classfileStream, registry);
}
return entries;
}
/**
*
* @param classfileStream
* @param registry
* used to create specific {@linkplain ReferenceType reference types}. If {@code null}, then
* {@link VerificationType#OBJECT} is return for any object type and
* {@link VerificationType#UNINITIALIZED} is returned for any uninitialized type
*/
public static StackMapFrame readStackMapFrame(ClassfileStream classfileStream, VerificationRegistry registry) {
final int type = classfileStream.readUnsigned1();
StackMapFrame returnFrame = null;
if (type < SAME_FRAME_BOUND) {
returnFrame = new SameFrame(type, classfileStream);
} else if (type < SAME_LOCALS_1_STACK_ITEM_BOUND) {
returnFrame = new SameLocalsOneStack(type, classfileStream, registry);
} else if (type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
/* throw an error: these are reserved for future use */
throw classFormatError("Invalid StackFrame type id " + type);
} else if (type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
returnFrame = new SameLocalsOneStackExtended(type, classfileStream, registry);
} else if (type < SAME_FRAME_EXTENDED) {
returnFrame = new ChopFrame(type, classfileStream);
} else if (type == SAME_FRAME_EXTENDED) {
returnFrame = new SameFrameExtended(type, classfileStream);
} else if (type < FULL_FRAME) {
returnFrame = new AppendFrame(type, classfileStream, registry);
} else if (type == FULL_FRAME) {
returnFrame = new FullFrame(type, classfileStream, registry);
} else {
/* throw error, out of bound _type */
throw classFormatError("Out of range StackFrame type id " + type);
}
return returnFrame;
}
public StackMapTable(StackMapFrame[] frames, ConstantPoolEditor constantPoolEditor) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
final DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
try {
dataOutputStream.writeShort(frames.length);
for (StackMapFrame frame : frames) {
frame.write(dataOutputStream, constantPoolEditor);
}
} catch (IOException e) {
// This will never occur
throw ProgramError.unexpected(e);
}
attributeData = byteArrayOutputStream.toByteArray();
}
public StackMapTable(ClassfileStream classfileStream, final ConstantPool constantPool, int attributeSize) {
attributeData = classfileStream.readByteArray(attributeSize);
}
/**
* Writes this stack map table to a given stream as a StackMapTable class file attribute.
*
* @param stream
* a data output stream that has just written the 'attribute_name_index' and 'attribute_length'
* fields of a class file attribute
* @param constantPoolEditor
*/
public void writeAttributeInfo(DataOutputStream stream, ConstantPoolEditor constantPoolEditor) throws IOException {
stream.write(attributeData);
}
}