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