blob: 585581fefe4c2b700f7a7b224b3343bafc026050 [file] [log] [blame]
/*
* Copyright (c) 2003, 2013, 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 sun.jvm.hotspot.oops;
import java.io.*;
import java.util.*;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.interpreter.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.utilities.*;
public class ConstMethod extends VMObject {
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
initialize(VM.getVM().getTypeDataBase());
}
});
}
// anon-enum constants for _flags.
private static int HAS_LINENUMBER_TABLE;
private static int HAS_CHECKED_EXCEPTIONS;
private static int HAS_LOCALVARIABLE_TABLE;
private static int HAS_EXCEPTION_TABLE;
private static int HAS_GENERIC_SIGNATURE;
private static int HAS_METHOD_ANNOTATIONS;
private static int HAS_PARAMETER_ANNOTATIONS;
private static int HAS_METHOD_PARAMETERS;
private static int HAS_DEFAULT_ANNOTATIONS;
private static int HAS_TYPE_ANNOTATIONS;
private static final int sizeofShort = 2;
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("ConstMethod");
constants = new MetadataField(type.getAddressField("_constants"), 0);
constMethodSize = new CIntField(type.getCIntegerField("_constMethod_size"), 0);
flags = new CIntField(type.getCIntegerField("_flags"), 0);
// enum constants for flags
HAS_LINENUMBER_TABLE = db.lookupIntConstant("ConstMethod::_has_linenumber_table").intValue();
HAS_CHECKED_EXCEPTIONS = db.lookupIntConstant("ConstMethod::_has_checked_exceptions").intValue();
HAS_LOCALVARIABLE_TABLE = db.lookupIntConstant("ConstMethod::_has_localvariable_table").intValue();
HAS_EXCEPTION_TABLE = db.lookupIntConstant("ConstMethod::_has_exception_table").intValue();
HAS_GENERIC_SIGNATURE = db.lookupIntConstant("ConstMethod::_has_generic_signature").intValue();
HAS_METHOD_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_method_annotations").intValue();
HAS_PARAMETER_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_parameter_annotations").intValue();
HAS_METHOD_PARAMETERS = db.lookupIntConstant("ConstMethod::_has_method_parameters").intValue();
HAS_DEFAULT_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_default_annotations").intValue();
HAS_TYPE_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_type_annotations").intValue();
// Size of Java bytecodes allocated immediately after ConstMethod*.
codeSize = new CIntField(type.getCIntegerField("_code_size"), 0);
nameIndex = new CIntField(type.getCIntegerField("_name_index"), 0);
signatureIndex = new CIntField(type.getCIntegerField("_signature_index"), 0);
idnum = new CIntField(type.getCIntegerField("_method_idnum"), 0);
maxStack = new CIntField(type.getCIntegerField("_max_stack"), 0);
maxLocals = new CIntField(type.getCIntegerField("_max_locals"), 0);
sizeOfParameters = new CIntField(type.getCIntegerField("_size_of_parameters"), 0);
// start of byte code
bytecodeOffset = type.getSize();
type = db.lookupType("MethodParametersElement");
methodParametersElementSize = type.getSize();
type = db.lookupType("CheckedExceptionElement");
checkedExceptionElementSize = type.getSize();
type = db.lookupType("LocalVariableTableElement");
localVariableTableElementSize = type.getSize();
type = db.lookupType("ExceptionTableElement");
exceptionTableElementSize = type.getSize();
}
public ConstMethod(Address addr) {
super(addr);
}
// Fields
private static MetadataField constants;
private static CIntField constMethodSize;
private static CIntField flags;
private static CIntField codeSize;
private static CIntField nameIndex;
private static CIntField signatureIndex;
private static CIntField idnum;
private static CIntField maxStack;
private static CIntField maxLocals;
private static CIntField sizeOfParameters;
// start of bytecode
private static long bytecodeOffset;
private static long methodParametersElementSize;
private static long checkedExceptionElementSize;
private static long localVariableTableElementSize;
private static long exceptionTableElementSize;
public Method getMethod() {
InstanceKlass ik = (InstanceKlass)getConstants().getPoolHolder();
MethodArray methods = ik.getMethods();
return methods.at((int)getIdNum());
}
// Accessors for declared fields
public ConstantPool getConstants() {
return (ConstantPool) constants.getValue(this);
}
public long getConstMethodSize() {
return constMethodSize.getValue(this);
}
public long getFlags() {
return flags.getValue(this);
}
public long getCodeSize() {
return codeSize.getValue(this);
}
public long getNameIndex() {
return nameIndex.getValue(this);
}
public long getSignatureIndex() {
return signatureIndex.getValue(this);
}
public long getGenericSignatureIndex() {
if (hasGenericSignature()) {
return getAddress().getCIntegerAt(offsetOfGenericSignatureIndex(), 2, true);
} else {
return 0;
}
}
public long getIdNum() {
return idnum.getValue(this);
}
public long getMaxStack() {
return maxStack.getValue(this);
}
public long getMaxLocals() {
return maxLocals.getValue(this);
}
public long getSizeOfParameters() {
return sizeOfParameters.getValue(this);
}
public Symbol getName() {
return getMethod().getName();
}
public Symbol getSignature() {
return getMethod().getSignature();
}
public Symbol getGenericSignature() {
return getMethod().getGenericSignature();
}
// bytecode accessors
/** Get a bytecode or breakpoint at the given bci */
public int getBytecodeOrBPAt(int bci) {
return getAddress().getJByteAt(bytecodeOffset + bci) & 0xFF;
}
public byte getBytecodeByteArg(int bci) {
return (byte) getBytecodeOrBPAt(bci);
}
/** Fetches a 16-bit big-endian ("Java ordered") value from the
bytecode stream */
public short getBytecodeShortArg(int bci) {
int hi = getBytecodeOrBPAt(bci);
int lo = getBytecodeOrBPAt(bci + 1);
return (short) ((hi << 8) | lo);
}
/** Fetches a 16-bit native ordered value from the
bytecode stream */
public short getNativeShortArg(int bci) {
int hi = getBytecodeOrBPAt(bci);
int lo = getBytecodeOrBPAt(bci + 1);
if (VM.getVM().isBigEndian()) {
return (short) ((hi << 8) | lo);
} else {
return (short) ((lo << 8) | hi);
}
}
/** Fetches a 32-bit big-endian ("Java ordered") value from the
bytecode stream */
public int getBytecodeIntArg(int bci) {
int b4 = getBytecodeOrBPAt(bci);
int b3 = getBytecodeOrBPAt(bci + 1);
int b2 = getBytecodeOrBPAt(bci + 2);
int b1 = getBytecodeOrBPAt(bci + 3);
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
/** Fetches a 32-bit native ordered value from the
bytecode stream */
public int getNativeIntArg(int bci) {
int b4 = getBytecodeOrBPAt(bci);
int b3 = getBytecodeOrBPAt(bci + 1);
int b2 = getBytecodeOrBPAt(bci + 2);
int b1 = getBytecodeOrBPAt(bci + 3);
if (VM.getVM().isBigEndian()) {
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
} else {
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
}
}
public byte[] getByteCode() {
byte[] bc = new byte[ (int) getCodeSize() ];
for( int i=0; i < bc.length; i++ )
{
long offs = bytecodeOffset + i;
bc[i] = getAddress().getJByteAt( offs );
}
return bc;
}
public long getSize() {
return getConstMethodSize();
}
public void printValueOn(PrintStream tty) {
tty.print("ConstMethod " + getName().asString() + getSignature().asString() + "@" + getAddress());
}
public void iterateFields(MetadataVisitor visitor) {
visitor.doMetadata(constants, true);
visitor.doCInt(constMethodSize, true);
visitor.doCInt(flags, true);
visitor.doCInt(codeSize, true);
visitor.doCInt(nameIndex, true);
visitor.doCInt(signatureIndex, true);
visitor.doCInt(codeSize, true);
visitor.doCInt(maxStack, true);
visitor.doCInt(maxLocals, true);
visitor.doCInt(sizeOfParameters, true);
}
// Accessors
public boolean hasLineNumberTable() {
return (getFlags() & HAS_LINENUMBER_TABLE) != 0;
}
public int getLineNumberFromBCI(int bci) {
if (!VM.getVM().isCore()) {
if (bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) bci = 0;
}
if (isNative()) {
return -1;
}
if (Assert.ASSERTS_ENABLED) {
Assert.that(bci == 0 || 0 <= bci && bci < getCodeSize(), "illegal bci");
}
int bestBCI = 0;
int bestLine = -1;
if (hasLineNumberTable()) {
// The line numbers are a short array of 2-tuples [start_pc, line_number].
// Not necessarily sorted and not necessarily one-to-one.
CompressedLineNumberReadStream stream =
new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
while (stream.readPair()) {
if (stream.bci() == bci) {
// perfect match
return stream.line();
} else {
// update best_bci/line
if (stream.bci() < bci && stream.bci() >= bestBCI) {
bestBCI = stream.bci();
bestLine = stream.line();
}
}
}
}
return bestLine;
}
public LineNumberTableElement[] getLineNumberTable() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasLineNumberTable(),
"should only be called if table is present");
}
int len = getLineNumberTableLength();
CompressedLineNumberReadStream stream =
new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
LineNumberTableElement[] ret = new LineNumberTableElement[len];
for (int idx = 0; idx < len; idx++) {
stream.readPair();
ret[idx] = new LineNumberTableElement(stream.bci(), stream.line());
}
return ret;
}
public boolean hasLocalVariableTable() {
return (getFlags() & HAS_LOCALVARIABLE_TABLE) != 0;
}
public Symbol getLocalVariableName(int bci, int slot) {
return getMethod().getLocalVariableName(bci, slot);
}
/** Should only be called if table is present */
public LocalVariableTableElement[] getLocalVariableTable() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasLocalVariableTable(), "should only be called if table is present");
}
LocalVariableTableElement[] ret = new LocalVariableTableElement[getLocalVariableTableLength()];
long offset = offsetOfLocalVariableTable();
for (int i = 0; i < ret.length; i++) {
ret[i] = new LocalVariableTableElement(getAddress(), offset);
offset += localVariableTableElementSize;
}
return ret;
}
public boolean hasExceptionTable() {
return (getFlags() & HAS_EXCEPTION_TABLE) != 0;
}
public ExceptionTableElement[] getExceptionTable() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasExceptionTable(), "should only be called if table is present");
}
ExceptionTableElement[] ret = new ExceptionTableElement[getExceptionTableLength()];
long offset = offsetOfExceptionTable();
for (int i = 0; i < ret.length; i++) {
ret[i] = new ExceptionTableElement(getAddress(), offset);
offset += exceptionTableElementSize;
}
return ret;
}
public boolean hasCheckedExceptions() {
return (getFlags() & HAS_CHECKED_EXCEPTIONS) != 0;
}
public CheckedExceptionElement[] getCheckedExceptions() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasCheckedExceptions(), "should only be called if table is present");
}
CheckedExceptionElement[] ret = new CheckedExceptionElement[getCheckedExceptionsLength()];
long offset = offsetOfCheckedExceptions();
for (int i = 0; i < ret.length; i++) {
ret[i] = new CheckedExceptionElement(getAddress(), offset);
offset += checkedExceptionElementSize;
}
return ret;
}
private boolean hasMethodParameters() {
return (getFlags() & HAS_METHOD_PARAMETERS) != 0;
}
private boolean hasGenericSignature() {
return (getFlags() & HAS_GENERIC_SIGNATURE) != 0;
}
private boolean hasMethodAnnotations() {
return (getFlags() & HAS_METHOD_ANNOTATIONS) != 0;
}
private boolean hasParameterAnnotations() {
return (getFlags() & HAS_PARAMETER_ANNOTATIONS) != 0;
}
private boolean hasDefaultAnnotations() {
return (getFlags() & HAS_DEFAULT_ANNOTATIONS) != 0;
}
private boolean hasTypeAnnotations() {
return (getFlags() & HAS_TYPE_ANNOTATIONS) != 0;
}
//---------------------------------------------------------------------------
// Internals only below this point
//
private boolean isNative() {
return getMethod().isNative();
}
// Offset of end of code
private long offsetOfCodeEnd() {
return bytecodeOffset + getCodeSize();
}
// Offset of start of compressed line number table (see method.hpp)
private long offsetOfCompressedLineNumberTable() {
return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0);
}
// Offset of last short in Method* before annotations, if present
private long offsetOfLastU2Element() {
int offset = 0;
if (hasMethodAnnotations()) offset++;
if (hasParameterAnnotations()) offset++;
if (hasTypeAnnotations()) offset++;
if (hasDefaultAnnotations()) offset++;
long wordSize = VM.getVM().getObjectHeap().getOopSize();
return (getSize() * wordSize) - (offset * wordSize) - sizeofShort;
}
// Offset of the generic signature index
private long offsetOfGenericSignatureIndex() {
return offsetOfLastU2Element();
}
private long offsetOfMethodParametersLength() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasMethodParameters(), "should only be called if table is present");
}
return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
offsetOfLastU2Element();
}
private int getMethodParametersLength() {
if (hasMethodParameters())
return (int) getAddress().getCIntegerAt(offsetOfMethodParametersLength(), 2, true);
else
return 0;
}
// Offset of start of checked exceptions
private long offsetOfMethodParameters() {
long offset = offsetOfMethodParametersLength();
long length = getMethodParametersLength();
if (Assert.ASSERTS_ENABLED) {
Assert.that(length > 0, "should only be called if method parameter information is present");
}
offset -= length * methodParametersElementSize;
return offset;
}
private long offsetOfCheckedExceptionsLength() {
if (hasMethodParameters())
return offsetOfMethodParameters() - sizeofShort;
else {
return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
offsetOfLastU2Element();
}
}
private int getCheckedExceptionsLength() {
if (hasCheckedExceptions()) {
return (int) getAddress().getCIntegerAt(offsetOfCheckedExceptionsLength(), 2, true);
} else {
return 0;
}
}
// Offset of start of checked exceptions
private long offsetOfCheckedExceptions() {
long offset = offsetOfCheckedExceptionsLength();
long length = getCheckedExceptionsLength();
if (Assert.ASSERTS_ENABLED) {
Assert.that(length > 0, "should only be called if table is present");
}
offset -= length * checkedExceptionElementSize;
return offset;
}
private int getLineNumberTableLength() {
int len = 0;
if (hasLineNumberTable()) {
CompressedLineNumberReadStream stream =
new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
while (stream.readPair()) {
len += 1;
}
}
return len;
}
private int getLocalVariableTableLength() {
if (hasLocalVariableTable()) {
return (int) getAddress().getCIntegerAt(offsetOfLocalVariableTableLength(), 2, true);
} else {
return 0;
}
}
// Offset of local variable table length
private long offsetOfLocalVariableTableLength() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasLocalVariableTable(), "should only be called if table is present");
}
if (hasExceptionTable()) {
return offsetOfExceptionTable() - sizeofShort;
} else if (hasCheckedExceptions()) {
return offsetOfCheckedExceptions() - sizeofShort;
} else if (hasMethodParameters()) {
return offsetOfMethodParameters() - sizeofShort;
} else {
return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
offsetOfLastU2Element();
}
}
private long offsetOfLocalVariableTable() {
long offset = offsetOfLocalVariableTableLength();
long length = getLocalVariableTableLength();
if (Assert.ASSERTS_ENABLED) {
Assert.that(length > 0, "should only be called if table is present");
}
offset -= length * localVariableTableElementSize;
return offset;
}
private int getExceptionTableLength() {
if (hasExceptionTable()) {
return (int) getAddress().getCIntegerAt(offsetOfExceptionTableLength(), 2, true);
} else {
return 0;
}
}
private long offsetOfExceptionTableLength() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasExceptionTable(), "should only be called if table is present");
}
if (hasCheckedExceptions()) {
return offsetOfCheckedExceptions() - sizeofShort;
} else if (hasMethodParameters()) {
return offsetOfMethodParameters() - sizeofShort;
} else {
return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
offsetOfLastU2Element();
}
}
private long offsetOfExceptionTable() {
long offset = offsetOfExceptionTableLength();
long length = getExceptionTableLength();
if (Assert.ASSERTS_ENABLED) {
Assert.that(length > 0, "should only be called if table is present");
}
offset -= length * exceptionTableElementSize;
return offset;
}
}