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 }