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 }