/* * 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; } }