1 /*
   2  * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.oops;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import sun.jvm.hotspot.code.*;
  30 import sun.jvm.hotspot.debugger.*;
  31 import sun.jvm.hotspot.interpreter.*;
  32 import sun.jvm.hotspot.memory.*;
  33 import sun.jvm.hotspot.runtime.*;
  34 import sun.jvm.hotspot.types.*;
  35 import sun.jvm.hotspot.utilities.*;
  36 
  37 public class ConstMethod extends Oop {
  38   static {
  39     VM.registerVMInitializedObserver(new Observer() {
  40         public void update(Observable o, Object data) {
  41           initialize(VM.getVM().getTypeDataBase());
  42         }
  43       });
  44   }
  45 
  46   // anon-enum constants for _flags.
  47   private static int HAS_LINENUMBER_TABLE;
  48   private static int HAS_CHECKED_EXCEPTIONS;
  49   private static int HAS_LOCALVARIABLE_TABLE;
  50   private static int HAS_EXCEPTION_TABLE;
  51 
  52   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
  53     Type type                  = db.lookupType("constMethodOopDesc");
  54     // Backpointer to non-const methodOop
  55     method                     = new OopField(type.getOopField("_method"), 0);
  56     constMethodSize            = new CIntField(type.getCIntegerField("_constMethod_size"), 0);
  57     flags                      = new ByteField(type.getJByteField("_flags"), 0);
  58 
  59     // enum constants for flags
  60     HAS_LINENUMBER_TABLE      = db.lookupIntConstant("constMethodOopDesc::_has_linenumber_table").intValue();
  61     HAS_CHECKED_EXCEPTIONS     = db.lookupIntConstant("constMethodOopDesc::_has_checked_exceptions").intValue();
  62     HAS_LOCALVARIABLE_TABLE   = db.lookupIntConstant("constMethodOopDesc::_has_localvariable_table").intValue();
  63     HAS_EXCEPTION_TABLE       = db.lookupIntConstant("constMethodOopDesc::_has_exception_table").intValue();
  64 
  65     // Size of Java bytecodes allocated immediately after constMethodOop.
  66     codeSize                   = new CIntField(type.getCIntegerField("_code_size"), 0);
  67     nameIndex                  = new CIntField(type.getCIntegerField("_name_index"), 0);
  68     signatureIndex             = new CIntField(type.getCIntegerField("_signature_index"), 0);
  69     genericSignatureIndex      = new CIntField(type.getCIntegerField("_generic_signature_index"),0);
  70 
  71     // start of byte code
  72     bytecodeOffset = type.getSize();
  73 
  74     type                       = db.lookupType("CheckedExceptionElement");
  75     checkedExceptionElementSize = type.getSize();
  76 
  77     type                       = db.lookupType("LocalVariableTableElement");
  78     localVariableTableElementSize = type.getSize();
  79 
  80     type                       = db.lookupType("ExceptionTableElement");
  81     exceptionTableElementSize = type.getSize();
  82   }
  83 
  84   ConstMethod(OopHandle handle, ObjectHeap heap) {
  85     super(handle, heap);
  86   }
  87 
  88   // Fields
  89   private static OopField  method;
  90   private static CIntField constMethodSize;
  91   private static ByteField flags;
  92   private static CIntField codeSize;
  93   private static CIntField nameIndex;
  94   private static CIntField signatureIndex;
  95   private static CIntField genericSignatureIndex;
  96 
  97   // start of bytecode
  98   private static long bytecodeOffset;
  99 
 100   private static long checkedExceptionElementSize;
 101   private static long localVariableTableElementSize;
 102   private static long exceptionTableElementSize;
 103 
 104   // Accessors for declared fields
 105   public Method getMethod() {
 106     return (Method) method.getValue(this);
 107   }
 108 
 109   public long getConstMethodSize() {
 110     return constMethodSize.getValue(this);
 111   }
 112 
 113   public byte getFlags() {
 114     return flags.getValue(this);
 115   }
 116 
 117   public long getCodeSize() {
 118     return codeSize.getValue(this);
 119   }
 120 
 121   public long getNameIndex() {
 122     return nameIndex.getValue(this);
 123   }
 124 
 125   public long getSignatureIndex() {
 126     return signatureIndex.getValue(this);
 127   }
 128 
 129   public long getGenericSignatureIndex() {
 130     return genericSignatureIndex.getValue(this);
 131   }
 132 
 133   public Symbol getName() {
 134     return getMethod().getName();
 135   }
 136 
 137   public Symbol getSignature() {
 138     return getMethod().getSignature();
 139   }
 140 
 141   public Symbol getGenericSignature() {
 142     return getMethod().getGenericSignature();
 143   }
 144 
 145   // bytecode accessors
 146 
 147   /** Get a bytecode or breakpoint at the given bci */
 148   public int getBytecodeOrBPAt(int bci) {
 149     return getHandle().getJByteAt(bytecodeOffset + bci) & 0xFF;
 150   }
 151 
 152   public byte getBytecodeByteArg(int bci) {
 153     return (byte) getBytecodeOrBPAt(bci);
 154   }
 155 
 156   /** Fetches a 16-bit big-endian ("Java ordered") value from the
 157       bytecode stream */
 158   public short getBytecodeShortArg(int bci) {
 159     int hi = getBytecodeOrBPAt(bci);
 160     int lo = getBytecodeOrBPAt(bci + 1);
 161     return (short) ((hi << 8) | lo);
 162   }
 163 
 164   /** Fetches a 16-bit native ordered value from the
 165       bytecode stream */
 166   public short getNativeShortArg(int bci) {
 167     int hi = getBytecodeOrBPAt(bci);
 168     int lo = getBytecodeOrBPAt(bci + 1);
 169     if (VM.getVM().isBigEndian()) {
 170         return (short) ((hi << 8) | lo);
 171     } else {
 172         return (short) ((lo << 8) | hi);
 173     }
 174   }
 175 
 176   /** Fetches a 32-bit big-endian ("Java ordered") value from the
 177       bytecode stream */
 178   public int getBytecodeIntArg(int bci) {
 179     int b4 = getBytecodeOrBPAt(bci);
 180     int b3 = getBytecodeOrBPAt(bci + 1);
 181     int b2 = getBytecodeOrBPAt(bci + 2);
 182     int b1 = getBytecodeOrBPAt(bci + 3);
 183 
 184     return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
 185   }
 186 
 187   /** Fetches a 32-bit native ordered value from the
 188       bytecode stream */
 189   public int getNativeIntArg(int bci) {
 190     int b4 = getBytecodeOrBPAt(bci);
 191     int b3 = getBytecodeOrBPAt(bci + 1);
 192     int b2 = getBytecodeOrBPAt(bci + 2);
 193     int b1 = getBytecodeOrBPAt(bci + 3);
 194 
 195     if (VM.getVM().isBigEndian()) {
 196         return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
 197     } else {
 198         return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
 199     }
 200   }
 201 
 202   public byte[] getByteCode() {
 203      byte[] bc = new byte[ (int) getCodeSize() ];
 204      for( int i=0; i < bc.length; i++ )
 205      {
 206         long offs = bytecodeOffset + i;
 207         bc[i] = getHandle().getJByteAt( offs );
 208      }
 209      return bc;
 210   }
 211 
 212   public long getObjectSize() {
 213     return getConstMethodSize() * getHeap().getOopSize();
 214   }
 215 
 216   public void printValueOn(PrintStream tty) {
 217     tty.print("ConstMethod " + getName().asString() + getSignature().asString() + "@" + getHandle());
 218   }
 219 
 220   public void iterateFields(OopVisitor visitor, boolean doVMFields) {
 221     super.iterateFields(visitor, doVMFields);
 222     if (doVMFields) {
 223       visitor.doOop(method, true);
 224       visitor.doCInt(constMethodSize, true);
 225       visitor.doByte(flags, true);
 226       visitor.doCInt(codeSize, true);
 227       visitor.doCInt(nameIndex, true);
 228       visitor.doCInt(signatureIndex, true);
 229       visitor.doCInt(genericSignatureIndex, true);
 230       visitor.doCInt(codeSize, true);
 231     }
 232   }
 233 
 234   // Accessors
 235 
 236   public boolean hasLineNumberTable() {
 237     return (getFlags() & HAS_LINENUMBER_TABLE) != 0;
 238   }
 239 
 240   public int getLineNumberFromBCI(int bci) {
 241     if (!VM.getVM().isCore()) {
 242       if (bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) bci = 0;
 243     }
 244 
 245     if (isNative()) {
 246       return -1;
 247     }
 248 
 249     if (Assert.ASSERTS_ENABLED) {
 250       Assert.that(bci == 0 || 0 <= bci && bci < getCodeSize(), "illegal bci");
 251     }
 252     int bestBCI  =  0;
 253     int bestLine = -1;
 254     if (hasLineNumberTable()) {
 255       // The line numbers are a short array of 2-tuples [start_pc, line_number].
 256       // Not necessarily sorted and not necessarily one-to-one.
 257       CompressedLineNumberReadStream stream =
 258         new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 259       while (stream.readPair()) {
 260         if (stream.bci() == bci) {
 261           // perfect match
 262           return stream.line();
 263         } else {
 264           // update best_bci/line
 265           if (stream.bci() < bci && stream.bci() >= bestBCI) {
 266             bestBCI  = stream.bci();
 267             bestLine = stream.line();
 268           }
 269         }
 270       }
 271     }
 272     return bestLine;
 273   }
 274 
 275   public LineNumberTableElement[] getLineNumberTable() {
 276     if (Assert.ASSERTS_ENABLED) {
 277       Assert.that(hasLineNumberTable(),
 278                   "should only be called if table is present");
 279     }
 280     int len = getLineNumberTableLength();
 281     CompressedLineNumberReadStream stream =
 282       new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 283     LineNumberTableElement[] ret = new LineNumberTableElement[len];
 284 
 285     for (int idx = 0; idx < len; idx++) {
 286       stream.readPair();
 287       ret[idx] = new LineNumberTableElement(stream.bci(), stream.line());
 288     }
 289     return ret;
 290   }
 291 
 292   public boolean hasLocalVariableTable() {
 293     return (getFlags() & HAS_LOCALVARIABLE_TABLE) != 0;
 294   }
 295 
 296   public Symbol getLocalVariableName(int bci, int slot) {
 297     return getMethod().getLocalVariableName(bci, slot);
 298   }
 299 
 300   /** Should only be called if table is present */
 301   public LocalVariableTableElement[] getLocalVariableTable() {
 302     if (Assert.ASSERTS_ENABLED) {
 303       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
 304     }
 305     LocalVariableTableElement[] ret = new LocalVariableTableElement[getLocalVariableTableLength()];
 306     long offset = offsetOfLocalVariableTable();
 307     for (int i = 0; i < ret.length; i++) {
 308       ret[i] = new LocalVariableTableElement(getHandle(), offset);
 309       offset += localVariableTableElementSize;
 310     }
 311     return ret;
 312   }
 313 
 314   public boolean hasExceptionTable() {
 315     return (getFlags() & HAS_EXCEPTION_TABLE) != 0;
 316   }
 317 
 318   public ExceptionTableElement[] getExceptionTable() {
 319     if (Assert.ASSERTS_ENABLED) {
 320       Assert.that(hasExceptionTable(), "should only be called if table is present");
 321     }
 322     ExceptionTableElement[] ret = new ExceptionTableElement[getExceptionTableLength()];
 323     long offset = offsetOfExceptionTable();
 324     for (int i = 0; i < ret.length; i++) {
 325       ret[i] = new ExceptionTableElement(getHandle(), offset);
 326       offset += exceptionTableElementSize;
 327     }
 328     return ret;
 329   }
 330 
 331   public boolean hasCheckedExceptions() {
 332     return (getFlags() & HAS_CHECKED_EXCEPTIONS) != 0;
 333   }
 334 
 335   public CheckedExceptionElement[] getCheckedExceptions() {
 336     if (Assert.ASSERTS_ENABLED) {
 337       Assert.that(hasCheckedExceptions(), "should only be called if table is present");
 338     }
 339     CheckedExceptionElement[] ret = new CheckedExceptionElement[getCheckedExceptionsLength()];
 340     long offset = offsetOfCheckedExceptions();
 341     for (int i = 0; i < ret.length; i++) {
 342       ret[i] = new CheckedExceptionElement(getHandle(), offset);
 343       offset += checkedExceptionElementSize;
 344     }
 345     return ret;
 346   }
 347 
 348 
 349   //---------------------------------------------------------------------------
 350   // Internals only below this point
 351   //
 352 
 353   private boolean isNative() {
 354     return getMethod().isNative();
 355   }
 356 
 357   // Offset of end of code
 358   private long offsetOfCodeEnd() {
 359     return bytecodeOffset + getCodeSize();
 360   }
 361 
 362   // Offset of start of compressed line number table (see methodOop.hpp)
 363   private long offsetOfCompressedLineNumberTable() {
 364     return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0);
 365   }
 366 
 367   // Offset of last short in methodOop
 368   private long offsetOfLastU2Element() {
 369     return getObjectSize() - 2;
 370   }
 371 
 372   private long offsetOfCheckedExceptionsLength() {
 373     return offsetOfLastU2Element();
 374   }
 375 
 376   private int getCheckedExceptionsLength() {
 377     if (hasCheckedExceptions()) {
 378       return (int) getHandle().getCIntegerAt(offsetOfCheckedExceptionsLength(), 2, true);
 379     } else {
 380       return 0;
 381     }
 382   }
 383 
 384   // Offset of start of checked exceptions
 385   private long offsetOfCheckedExceptions() {
 386     long offset = offsetOfCheckedExceptionsLength();
 387     long length = getCheckedExceptionsLength();
 388     if (Assert.ASSERTS_ENABLED) {
 389       Assert.that(length > 0, "should only be called if table is present");
 390     }
 391     offset -= length * checkedExceptionElementSize;
 392     return offset;
 393   }
 394 
 395   private int getLineNumberTableLength() {
 396     int len = 0;
 397     if (hasLineNumberTable()) {
 398       CompressedLineNumberReadStream stream =
 399         new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 400       while (stream.readPair()) {
 401         len += 1;
 402       }
 403     }
 404     return len;
 405   }
 406 
 407   private int getLocalVariableTableLength() {
 408     if (hasLocalVariableTable()) {
 409       return (int) getHandle().getCIntegerAt(offsetOfLocalVariableTableLength(), 2, true);
 410     } else {
 411       return 0;
 412     }
 413   }
 414 
 415   // Offset of local variable table length
 416   private long offsetOfLocalVariableTableLength() {
 417     if (Assert.ASSERTS_ENABLED) {
 418       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
 419     }
 420     
 421     if (hasExceptionTable()) {
 422       return offsetOfExceptionTable() - 2;
 423     } else if (hasCheckedExceptions()) {
 424       return offsetOfCheckedExceptions() - 2;
 425     } else {
 426       return offsetOfLastU2Element();
 427     }
 428   }
 429 
 430   private long offsetOfLocalVariableTable() {
 431     long offset = offsetOfLocalVariableTableLength();
 432     long length = getLocalVariableTableLength();
 433     if (Assert.ASSERTS_ENABLED) {
 434       Assert.that(length > 0, "should only be called if table is present");
 435     }
 436     offset -= length * localVariableTableElementSize;
 437     return offset;
 438   }
 439 
 440   private int getExceptionTableLength() {
 441     if (hasExceptionTable()) {
 442       return (int) getHandle().getCIntegerAt(offsetOfExceptionTableLength(), 2, true);
 443     } else {
 444       return 0;
 445     }
 446   }
 447 
 448   private long offsetOfExceptionTableLength() {
 449     if (Assert.ASSERTS_ENABLED) {
 450       Assert.that(hasExceptionTable(), "should only be called if table is present");
 451     }
 452     if (hasCheckedExceptions()) {
 453       return offsetOfCheckedExceptions() - 2;
 454     } else {
 455       return offsetOfLastU2Element();
 456     }
 457   }
 458 
 459   private long offsetOfExceptionTable() {
 460     long offset = offsetOfExceptionTableLength();
 461     long length = getExceptionTableLength();
 462     if (Assert.ASSERTS_ENABLED) {
 463       Assert.that(length > 0, "should only be called if table is present");
 464     }
 465     offset -= length * exceptionTableElementSize;
 466     return offset;
 467   }
 468 
 469 }