1 /*
   2  * Copyright (c) 2003, 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 
  51   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
  52     Type type                  = db.lookupType("constMethodOopDesc");
  53     // Backpointer to non-const methodOop
  54     method                     = new OopField(type.getOopField("_method"), 0);
  55     // The exception handler table. 4-tuples of ints [start_pc, end_pc,
  56     // handler_pc, catch_type index] For methods with no exceptions the
  57     // table is pointing to Universe::the_empty_int_array
  58     exceptionTable             = new OopField(type.getOopField("_exception_table"), 0);
  59     constMethodSize            = new CIntField(type.getCIntegerField("_constMethod_size"), 0);
  60     flags                      = new ByteField(type.getJByteField("_flags"), 0);
  61 
  62     // enum constants for flags
  63     HAS_LINENUMBER_TABLE      = db.lookupIntConstant("constMethodOopDesc::_has_linenumber_table").intValue();
  64     HAS_CHECKED_EXCEPTIONS     = db.lookupIntConstant("constMethodOopDesc::_has_checked_exceptions").intValue();
  65     HAS_LOCALVARIABLE_TABLE   = db.lookupIntConstant("constMethodOopDesc::_has_localvariable_table").intValue();
  66 
  67     // Size of Java bytecodes allocated immediately after constMethodOop.
  68     codeSize                   = new CIntField(type.getCIntegerField("_code_size"), 0);
  69     nameIndex                  = new CIntField(type.getCIntegerField("_name_index"), 0);
  70     signatureIndex             = new CIntField(type.getCIntegerField("_signature_index"), 0);
  71     genericSignatureIndex      = new CIntField(type.getCIntegerField("_generic_signature_index"),0);
  72 
  73     // start of byte code
  74     bytecodeOffset = type.getSize();
  75 
  76     type                       = db.lookupType("CheckedExceptionElement");
  77     checkedExceptionElementSize = type.getSize();
  78 
  79     type                       = db.lookupType("LocalVariableTableElement");
  80     localVariableTableElementSize = type.getSize();
  81   }
  82 
  83   ConstMethod(OopHandle handle, ObjectHeap heap) {
  84     super(handle, heap);
  85   }
  86 
  87   // Fields
  88   private static OopField  method;
  89   private static OopField  exceptionTable;
  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 
 103   // Accessors for declared fields
 104   public Method getMethod() {
 105     return (Method) method.getValue(this);
 106   }
 107 
 108   public TypeArray getExceptionTable() {
 109     return (TypeArray) exceptionTable.getValue(this);
 110   }
 111 
 112   public long getConstMethodSize() {
 113     return constMethodSize.getValue(this);
 114   }
 115 
 116   public byte getFlags() {
 117     return flags.getValue(this);
 118   }
 119 
 120   public long getCodeSize() {
 121     return codeSize.getValue(this);
 122   }
 123 
 124   public long getNameIndex() {
 125     return nameIndex.getValue(this);
 126   }
 127 
 128   public long getSignatureIndex() {
 129     return signatureIndex.getValue(this);
 130   }
 131 
 132   public long getGenericSignatureIndex() {
 133     return genericSignatureIndex.getValue(this);
 134   }
 135 
 136   public Symbol getName() {
 137     return getMethod().getName();
 138   }
 139 
 140   public Symbol getSignature() {
 141     return getMethod().getSignature();
 142   }
 143 
 144   public Symbol getGenericSignature() {
 145     return getMethod().getGenericSignature();
 146   }
 147 
 148   // bytecode accessors
 149 
 150   /** Get a bytecode or breakpoint at the given bci */
 151   public int getBytecodeOrBPAt(int bci) {
 152     return getHandle().getJByteAt(bytecodeOffset + bci) & 0xFF;
 153   }
 154 
 155   public byte getBytecodeByteArg(int bci) {
 156     return (byte) getBytecodeOrBPAt(bci);
 157   }
 158 
 159   /** Fetches a 16-bit big-endian ("Java ordered") value from the
 160       bytecode stream */
 161   public short getBytecodeShortArg(int bci) {
 162     int hi = getBytecodeOrBPAt(bci);
 163     int lo = getBytecodeOrBPAt(bci + 1);
 164     return (short) ((hi << 8) | lo);
 165   }
 166 
 167   /** Fetches a 16-bit native ordered value from the
 168       bytecode stream */
 169   public short getNativeShortArg(int bci) {
 170     int hi = getBytecodeOrBPAt(bci);
 171     int lo = getBytecodeOrBPAt(bci + 1);
 172     if (VM.getVM().isBigEndian()) {
 173         return (short) ((hi << 8) | lo);
 174     } else {
 175         return (short) ((lo << 8) | hi);
 176     }
 177   }
 178 
 179   /** Fetches a 32-bit big-endian ("Java ordered") value from the
 180       bytecode stream */
 181   public int getBytecodeIntArg(int bci) {
 182     int b4 = getBytecodeOrBPAt(bci);
 183     int b3 = getBytecodeOrBPAt(bci + 1);
 184     int b2 = getBytecodeOrBPAt(bci + 2);
 185     int b1 = getBytecodeOrBPAt(bci + 3);
 186 
 187     return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
 188   }
 189 
 190   /** Fetches a 32-bit native ordered value from the
 191       bytecode stream */
 192   public int getNativeIntArg(int bci) {
 193     int b4 = getBytecodeOrBPAt(bci);
 194     int b3 = getBytecodeOrBPAt(bci + 1);
 195     int b2 = getBytecodeOrBPAt(bci + 2);
 196     int b1 = getBytecodeOrBPAt(bci + 3);
 197 
 198     if (VM.getVM().isBigEndian()) {
 199         return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
 200     } else {
 201         return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
 202     }
 203   }
 204 
 205   public byte[] getByteCode() {
 206      byte[] bc = new byte[ (int) getCodeSize() ];
 207      for( int i=0; i < bc.length; i++ )
 208      {
 209         long offs = bytecodeOffset + i;
 210         bc[i] = getHandle().getJByteAt( offs );
 211      }
 212      return bc;
 213   }
 214 
 215   public long getObjectSize() {
 216     return getConstMethodSize() * getHeap().getOopSize();
 217   }
 218 
 219   public void printValueOn(PrintStream tty) {
 220     tty.print("ConstMethod " + getName().asString() + getSignature().asString() + "@" + getHandle());
 221   }
 222 
 223   public void iterateFields(OopVisitor visitor, boolean doVMFields) {
 224     super.iterateFields(visitor, doVMFields);
 225     if (doVMFields) {
 226       visitor.doOop(method, true);
 227       visitor.doOop(exceptionTable, true);
 228       visitor.doCInt(constMethodSize, true);
 229       visitor.doByte(flags, true);
 230       visitor.doCInt(codeSize, true);
 231       visitor.doCInt(nameIndex, true);
 232       visitor.doCInt(signatureIndex, true);
 233       visitor.doCInt(genericSignatureIndex, true);
 234       visitor.doCInt(codeSize, true);
 235     }
 236   }
 237 
 238   // Accessors
 239 
 240   public boolean hasLineNumberTable() {
 241     return (getFlags() & HAS_LINENUMBER_TABLE) != 0;
 242   }
 243 
 244   public int getLineNumberFromBCI(int bci) {
 245     if (!VM.getVM().isCore()) {
 246       if (bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) bci = 0;
 247     }
 248 
 249     if (isNative()) {
 250       return -1;
 251     }
 252 
 253     if (Assert.ASSERTS_ENABLED) {
 254       Assert.that(bci == 0 || 0 <= bci && bci < getCodeSize(), "illegal bci");
 255     }
 256     int bestBCI  =  0;
 257     int bestLine = -1;
 258     if (hasLineNumberTable()) {
 259       // The line numbers are a short array of 2-tuples [start_pc, line_number].
 260       // Not necessarily sorted and not necessarily one-to-one.
 261       CompressedLineNumberReadStream stream =
 262         new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 263       while (stream.readPair()) {
 264         if (stream.bci() == bci) {
 265           // perfect match
 266           return stream.line();
 267         } else {
 268           // update best_bci/line
 269           if (stream.bci() < bci && stream.bci() >= bestBCI) {
 270             bestBCI  = stream.bci();
 271             bestLine = stream.line();
 272           }
 273         }
 274       }
 275     }
 276     return bestLine;
 277   }
 278 
 279   public LineNumberTableElement[] getLineNumberTable() {
 280     if (Assert.ASSERTS_ENABLED) {
 281       Assert.that(hasLineNumberTable(),
 282                   "should only be called if table is present");
 283     }
 284     int len = getLineNumberTableLength();
 285     CompressedLineNumberReadStream stream =
 286       new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 287     LineNumberTableElement[] ret = new LineNumberTableElement[len];
 288 
 289     for (int idx = 0; idx < len; idx++) {
 290       stream.readPair();
 291       ret[idx] = new LineNumberTableElement(stream.bci(), stream.line());
 292     }
 293     return ret;
 294   }
 295 
 296   public boolean hasLocalVariableTable() {
 297     return (getFlags() & HAS_LOCALVARIABLE_TABLE) != 0;
 298   }
 299 
 300   public Symbol getLocalVariableName(int bci, int slot) {
 301     return getMethod().getLocalVariableName(bci, slot);
 302   }
 303 
 304   /** Should only be called if table is present */
 305   public LocalVariableTableElement[] getLocalVariableTable() {
 306     if (Assert.ASSERTS_ENABLED) {
 307       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
 308     }
 309     LocalVariableTableElement[] ret = new LocalVariableTableElement[getLocalVariableTableLength()];
 310     long offset = offsetOfLocalVariableTable();
 311     for (int i = 0; i < ret.length; i++) {
 312       ret[i] = new LocalVariableTableElement(getHandle(), offset);
 313       offset += localVariableTableElementSize;
 314     }
 315     return ret;
 316   }
 317 
 318   public boolean hasCheckedExceptions() {
 319     return (getFlags() & HAS_CHECKED_EXCEPTIONS) != 0;
 320   }
 321 
 322   public CheckedExceptionElement[] getCheckedExceptions() {
 323     if (Assert.ASSERTS_ENABLED) {
 324       Assert.that(hasCheckedExceptions(), "should only be called if table is present");
 325     }
 326     CheckedExceptionElement[] ret = new CheckedExceptionElement[getCheckedExceptionsLength()];
 327     long offset = offsetOfCheckedExceptions();
 328     for (int i = 0; i < ret.length; i++) {
 329       ret[i] = new CheckedExceptionElement(getHandle(), offset);
 330       offset += checkedExceptionElementSize;
 331     }
 332     return ret;
 333   }
 334 
 335 
 336   //---------------------------------------------------------------------------
 337   // Internals only below this point
 338   //
 339 
 340   private boolean isNative() {
 341     return getMethod().isNative();
 342   }
 343 
 344   // Offset of end of code
 345   private long offsetOfCodeEnd() {
 346     return bytecodeOffset + getCodeSize();
 347   }
 348 
 349   // Offset of start of compressed line number table (see methodOop.hpp)
 350   private long offsetOfCompressedLineNumberTable() {
 351     return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0);
 352   }
 353 
 354   // Offset of last short in methodOop
 355   private long offsetOfLastU2Element() {
 356     return getObjectSize() - 2;
 357   }
 358 
 359   private long offsetOfCheckedExceptionsLength() {
 360     return offsetOfLastU2Element();
 361   }
 362 
 363   private int getCheckedExceptionsLength() {
 364     if (hasCheckedExceptions()) {
 365       return (int) getHandle().getCIntegerAt(offsetOfCheckedExceptionsLength(), 2, true);
 366     } else {
 367       return 0;
 368     }
 369   }
 370 
 371   // Offset of start of checked exceptions
 372   private long offsetOfCheckedExceptions() {
 373     long offset = offsetOfCheckedExceptionsLength();
 374     long length = getCheckedExceptionsLength();
 375     if (Assert.ASSERTS_ENABLED) {
 376       Assert.that(length > 0, "should only be called if table is present");
 377     }
 378     offset -= length * checkedExceptionElementSize;
 379     return offset;
 380   }
 381 
 382   private int getLineNumberTableLength() {
 383     int len = 0;
 384     if (hasLineNumberTable()) {
 385       CompressedLineNumberReadStream stream =
 386         new CompressedLineNumberReadStream(getHandle(), (int) offsetOfCompressedLineNumberTable());
 387       while (stream.readPair()) {
 388         len += 1;
 389       }
 390     }
 391     return len;
 392   }
 393 
 394   private int getLocalVariableTableLength() {
 395     if (hasLocalVariableTable()) {
 396       return (int) getHandle().getCIntegerAt(offsetOfLocalVariableTableLength(), 2, true);
 397     } else {
 398       return 0;
 399     }
 400   }
 401 
 402   // Offset of local variable table length
 403   private long offsetOfLocalVariableTableLength() {
 404     if (Assert.ASSERTS_ENABLED) {
 405       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
 406     }
 407     if (hasCheckedExceptions()) {
 408       return offsetOfCheckedExceptions() - 2;
 409     } else {
 410       return offsetOfLastU2Element();
 411     }
 412   }
 413 
 414   private long offsetOfLocalVariableTable() {
 415     long offset = offsetOfLocalVariableTableLength();
 416     long length = getLocalVariableTableLength();
 417     if (Assert.ASSERTS_ENABLED) {
 418       Assert.that(length > 0, "should only be called if table is present");
 419     }
 420     offset -= length * localVariableTableElementSize;
 421     return offset;
 422   }
 423 
 424 }