1 /* 2 * Copyright (c) 2011, 2016, 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 package jdk.vm.ci.hotspot; 24 25 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM; 26 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime; 27 import static jdk.vm.ci.hotspot.HotSpotModifiers.BRIDGE; 28 import static jdk.vm.ci.hotspot.HotSpotModifiers.SYNTHETIC; 29 import static jdk.vm.ci.hotspot.HotSpotModifiers.VARARGS; 30 import static jdk.vm.ci.hotspot.HotSpotModifiers.jvmMethodModifiers; 31 import static jdk.vm.ci.hotspot.HotSpotVMConfig.config; 32 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE; 33 34 import java.lang.annotation.Annotation; 35 import java.lang.reflect.Executable; 36 import java.lang.reflect.Method; 37 import java.lang.reflect.Modifier; 38 import java.lang.reflect.Type; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.Map; 42 43 import jdk.vm.ci.common.JVMCIError; 44 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option; 45 import jdk.vm.ci.meta.Constant; 46 import jdk.vm.ci.meta.ConstantPool; 47 import jdk.vm.ci.meta.DefaultProfilingInfo; 48 import jdk.vm.ci.meta.ExceptionHandler; 49 import jdk.vm.ci.meta.JavaMethod; 50 import jdk.vm.ci.meta.JavaType; 51 import jdk.vm.ci.meta.LineNumberTable; 52 import jdk.vm.ci.meta.Local; 53 import jdk.vm.ci.meta.LocalVariableTable; 54 import jdk.vm.ci.meta.ProfilingInfo; 55 import jdk.vm.ci.meta.ResolvedJavaMethod; 56 import jdk.vm.ci.meta.ResolvedJavaType; 57 import jdk.vm.ci.meta.Signature; 58 import jdk.vm.ci.meta.SpeculationLog; 59 import jdk.vm.ci.meta.TriState; 60 61 /** 62 * Implementation of {@link JavaMethod} for resolved HotSpot methods. 63 */ 64 final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, MetaspaceWrapperObject { 65 66 /** 67 * Reference to metaspace Method object. 68 */ 69 private final long metaspaceMethod; 70 71 private final HotSpotResolvedObjectTypeImpl holder; 72 private final HotSpotConstantPool constantPool; 73 private final HotSpotSignature signature; 74 private HotSpotMethodData methodData; 75 private byte[] code; 76 private Executable toJavaCache; 77 78 /** 79 * Gets the holder of a HotSpot metaspace method native object. 80 * 81 * @param metaspaceMethod a metaspace Method object 82 * @return the {@link ResolvedJavaType} corresponding to the holder of the 83 * {@code metaspaceMethod} 84 */ 85 private static HotSpotResolvedObjectTypeImpl getHolder(long metaspaceMethod) { 86 HotSpotVMConfig config = config(); 87 final long metaspaceConstMethod = UNSAFE.getAddress(metaspaceMethod + config.methodConstMethodOffset); 88 final long metaspaceConstantPool = UNSAFE.getAddress(metaspaceConstMethod + config.constMethodConstantsOffset); 89 return compilerToVM().getResolvedJavaType(null, metaspaceConstantPool + config.constantPoolHolderOffset, false); 90 } 91 92 /** 93 * Gets the JVMCI mirror from a HotSpot method. The VM is responsible for ensuring that the 94 * Method* is kept alive for the duration of this call and the 95 * {@link HotSpotJVMCIMetaAccessContext} keeps it alive after that. 96 * 97 * Called from the VM. 98 * 99 * @param metaspaceMethod a metaspace Method object 100 * @return the {@link ResolvedJavaMethod} corresponding to {@code metaspaceMethod} 101 */ 102 @SuppressWarnings("unused") 103 private static HotSpotResolvedJavaMethod fromMetaspace(long metaspaceMethod) { 104 HotSpotResolvedObjectTypeImpl holder = getHolder(metaspaceMethod); 105 return holder.createMethod(metaspaceMethod); 106 } 107 108 HotSpotResolvedJavaMethodImpl(HotSpotResolvedObjectTypeImpl holder, long metaspaceMethod) { 109 // It would be too much work to get the method name here so we fill it in later. 110 super(null); 111 this.metaspaceMethod = metaspaceMethod; 112 this.holder = holder; 113 114 HotSpotVMConfig config = config(); 115 final long constMethod = getConstMethod(); 116 117 /* 118 * Get the constant pool from the metaspace method. Some methods (e.g. intrinsics for 119 * signature-polymorphic method handle methods) have their own constant pool instead of the 120 * one from their holder. 121 */ 122 final long metaspaceConstantPool = UNSAFE.getAddress(constMethod + config.constMethodConstantsOffset); 123 if (metaspaceConstantPool == holder.getConstantPool().getMetaspaceConstantPool()) { 124 this.constantPool = holder.getConstantPool(); 125 } else { 126 this.constantPool = compilerToVM().getConstantPool(this); 127 } 128 129 final int nameIndex = UNSAFE.getChar(constMethod + config.constMethodNameIndexOffset); 130 this.name = constantPool.lookupUtf8(nameIndex); 131 132 final int signatureIndex = UNSAFE.getChar(constMethod + config.constMethodSignatureIndexOffset); 133 this.signature = (HotSpotSignature) constantPool.lookupSignature(signatureIndex); 134 } 135 136 /** 137 * Returns a pointer to this method's constant method data structure ( 138 * {@code Method::_constMethod}). This pointer isn't wrapped since it should be safe to use it 139 * within the context of this HotSpotResolvedJavaMethodImpl since the Method* and ConstMethod* 140 * are kept alive as a pair. 141 * 142 * @return pointer to this method's ConstMethod 143 */ 144 private long getConstMethod() { 145 assert metaspaceMethod != 0; 146 return UNSAFE.getAddress(metaspaceMethod + config().methodConstMethodOffset); 147 } 148 149 @Override 150 public boolean equals(Object obj) { 151 if (this == obj) { 152 return true; 153 } 154 if (obj instanceof HotSpotResolvedJavaMethodImpl) { 155 HotSpotResolvedJavaMethodImpl that = (HotSpotResolvedJavaMethodImpl) obj; 156 return that.metaspaceMethod == metaspaceMethod; 157 } 158 return false; 159 } 160 161 @Override 162 public int hashCode() { 163 return (int) metaspaceMethod; 164 } 165 166 /** 167 * Returns this method's flags ({@code Method::_flags}). 168 * 169 * @return flags of this method 170 */ 171 private int getFlags() { 172 return UNSAFE.getShort(metaspaceMethod + config().methodFlagsOffset); 173 } 174 175 /** 176 * Returns this method's constant method flags ({@code ConstMethod::_flags}). 177 * 178 * @return flags of this method's ConstMethod 179 */ 180 private int getConstMethodFlags() { 181 return UNSAFE.getChar(getConstMethod() + config().constMethodFlagsOffset); 182 } 183 184 @Override 185 public HotSpotResolvedObjectTypeImpl getDeclaringClass() { 186 return holder; 187 } 188 189 /** 190 * Gets the address of the C++ Method object for this method. 191 */ 192 public Constant getMetaspaceMethodConstant() { 193 return HotSpotMetaspaceConstantImpl.forMetaspaceObject(this, false); 194 } 195 196 public long getMetaspacePointer() { 197 return metaspaceMethod; 198 } 199 200 @Override 201 public Constant getEncoding() { 202 return getMetaspaceMethodConstant(); 203 } 204 205 /** 206 * Gets the complete set of modifiers for this method which includes the JVM specification 207 * modifiers as well as the HotSpot internal modifiers. 208 */ 209 public int getAllModifiers() { 210 return UNSAFE.getInt(metaspaceMethod + config().methodAccessFlagsOffset); 211 } 212 213 @Override 214 public int getModifiers() { 215 return getAllModifiers() & jvmMethodModifiers(); 216 } 217 218 @Override 219 public boolean canBeStaticallyBound() { 220 return (isFinal() || isPrivate() || isStatic() || holder.isLeaf()) && isConcrete(); 221 } 222 223 @Override 224 public byte[] getCode() { 225 if (getCodeSize() == 0) { 226 return null; 227 } 228 if (code == null && holder.isLinked()) { 229 code = compilerToVM().getBytecode(this); 230 assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length; 231 } 232 return code; 233 } 234 235 @Override 236 public int getCodeSize() { 237 return UNSAFE.getChar(getConstMethod() + config().constMethodCodeSizeOffset); 238 } 239 240 @Override 241 public ExceptionHandler[] getExceptionHandlers() { 242 final boolean hasExceptionTable = (getConstMethodFlags() & config().constMethodHasExceptionTable) != 0; 243 if (!hasExceptionTable) { 244 return new ExceptionHandler[0]; 245 } 246 247 HotSpotVMConfig config = config(); 248 final int exceptionTableLength = compilerToVM().getExceptionTableLength(this); 249 ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength]; 250 long exceptionTableElement = compilerToVM().getExceptionTableStart(this); 251 252 for (int i = 0; i < exceptionTableLength; i++) { 253 final int startPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementStartPcOffset); 254 final int endPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementEndPcOffset); 255 final int handlerPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementHandlerPcOffset); 256 int catchTypeIndex = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementCatchTypeIndexOffset); 257 258 JavaType catchType; 259 if (catchTypeIndex == 0) { 260 catchType = null; 261 } else { 262 final int opcode = -1; // opcode is not used 263 catchType = constantPool.lookupType(catchTypeIndex, opcode); 264 265 // Check for Throwable which catches everything. 266 if (catchType instanceof HotSpotResolvedObjectTypeImpl) { 267 HotSpotResolvedObjectTypeImpl resolvedType = (HotSpotResolvedObjectTypeImpl) catchType; 268 if (resolvedType.mirror() == Throwable.class) { 269 catchTypeIndex = 0; 270 catchType = null; 271 } 272 } 273 } 274 handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType); 275 276 // Go to the next ExceptionTableElement 277 exceptionTableElement += config.exceptionTableElementSize; 278 } 279 280 return handlers; 281 } 282 283 /** 284 * Returns true if this method has a {@code CallerSensitive} annotation. 285 * 286 * @return true if CallerSensitive annotation present, false otherwise 287 */ 288 public boolean isCallerSensitive() { 289 return (getFlags() & config().methodFlagsCallerSensitive) != 0; 290 } 291 292 /** 293 * Returns true if this method has a {@code ForceInline} annotation. 294 * 295 * @return true if ForceInline annotation present, false otherwise 296 */ 297 public boolean isForceInline() { 298 return (getFlags() & config().methodFlagsForceInline) != 0; 299 } 300 301 /** 302 * Returns true if this method has a {@code DontInline} annotation. 303 * 304 * @return true if DontInline annotation present, false otherwise 305 */ 306 public boolean isDontInline() { 307 return (getFlags() & config().methodFlagsDontInline) != 0; 308 } 309 310 /** 311 * Returns true if this method has a {@code ReservedStackAccess} annotation. 312 * 313 * @return true if ReservedStackAccess annotation present, false otherwise 314 */ 315 public boolean hasReservedStackAccess() { 316 return (getFlags() & config().methodFlagsReservedStackAccess) != 0; 317 } 318 319 /** 320 * Manually adds a DontInline annotation to this method. 321 */ 322 public void setNotInlineable() { 323 compilerToVM().doNotInlineOrCompile(this); 324 } 325 326 /** 327 * Returns true if this method is one of the special methods that is ignored by security stack 328 * walks. 329 * 330 * @return true if special method ignored by security stack walks, false otherwise 331 */ 332 public boolean ignoredBySecurityStackWalk() { 333 return compilerToVM().methodIsIgnoredBySecurityStackWalk(this); 334 } 335 336 @Override 337 public boolean isClassInitializer() { 338 return "<clinit>".equals(name) && isStatic(); 339 } 340 341 @Override 342 public boolean isConstructor() { 343 return "<init>".equals(name) && !isStatic(); 344 } 345 346 @Override 347 public int getMaxLocals() { 348 if (isAbstract() || isNative()) { 349 return 0; 350 } 351 HotSpotVMConfig config = config(); 352 return UNSAFE.getChar(getConstMethod() + config.methodMaxLocalsOffset); 353 } 354 355 @Override 356 public int getMaxStackSize() { 357 if (isAbstract() || isNative()) { 358 return 0; 359 } 360 HotSpotVMConfig config = config(); 361 return config.extraStackEntries + UNSAFE.getChar(getConstMethod() + config.constMethodMaxStackOffset); 362 } 363 364 @Override 365 public StackTraceElement asStackTraceElement(int bci) { 366 if (bci < 0 || bci >= getCodeSize()) { 367 // HotSpot code can only construct stack trace elements for valid bcis 368 StackTraceElement ste = compilerToVM().getStackTraceElement(this, 0); 369 return new StackTraceElement(ste.getClassName(), ste.getMethodName(), ste.getFileName(), -1); 370 } 371 return compilerToVM().getStackTraceElement(this, bci); 372 } 373 374 public ResolvedJavaMethod uniqueConcreteMethod(HotSpotResolvedObjectType receiver) { 375 if (receiver.isInterface()) { 376 // Cannot trust interfaces. Because of: 377 // interface I { void foo(); } 378 // class A { public void foo() {} } 379 // class B extends A implements I { } 380 // class C extends B { public void foo() { } } 381 // class D extends B { } 382 // Would lead to identify C.foo() as the unique concrete method for I.foo() without 383 // seeing A.foo(). 384 return null; 385 } 386 if (this.isDefault()) { 387 // CHA for default methods doesn't work and may crash the VM 388 return null; 389 } 390 return compilerToVM().findUniqueConcreteMethod(((HotSpotResolvedObjectTypeImpl) receiver), this); 391 } 392 393 @Override 394 public HotSpotSignature getSignature() { 395 return signature; 396 } 397 398 /** 399 * Gets the value of {@code Method::_code}. 400 * 401 * @return the value of {@code Method::_code} 402 */ 403 private long getCompiledCode() { 404 HotSpotVMConfig config = config(); 405 return UNSAFE.getAddress(metaspaceMethod + config.methodCodeOffset); 406 } 407 408 /** 409 * Returns whether this method has compiled code. 410 * 411 * @return true if this method has compiled code, false otherwise 412 */ 413 public boolean hasCompiledCode() { 414 return getCompiledCode() != 0L; 415 } 416 417 /** 418 * @param level 419 * @return true if the currently installed code was generated at {@code level}. 420 */ 421 public boolean hasCompiledCodeAtLevel(int level) { 422 long compiledCode = getCompiledCode(); 423 if (compiledCode != 0) { 424 return UNSAFE.getInt(compiledCode + config().nmethodCompLevelOffset) == level; 425 } 426 return false; 427 } 428 429 @Override 430 public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) { 431 ProfilingInfo info; 432 433 if (methodData == null) { 434 long metaspaceMethodData = UNSAFE.getAddress(metaspaceMethod + config().methodDataOffset); 435 if (metaspaceMethodData != 0) { 436 methodData = new HotSpotMethodData(metaspaceMethodData, this); 437 String methodDataFilter = Option.TraceMethodDataFilter.getString(); 438 if (methodDataFilter != null && this.format("%H.%n").contains(methodDataFilter)) { 439 System.out.println(methodData.toString()); 440 } 441 } 442 } 443 444 if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) { 445 // Be optimistic and return false for exceptionSeen. A methodDataOop is allocated in 446 // case of a deoptimization. 447 info = DefaultProfilingInfo.get(TriState.FALSE); 448 } else { 449 info = new HotSpotProfilingInfo(methodData, this, includeNormal, includeOSR); 450 } 451 return info; 452 } 453 454 @Override 455 public void reprofile() { 456 compilerToVM().reprofile(this); 457 } 458 459 @Override 460 public ConstantPool getConstantPool() { 461 return constantPool; 462 } 463 464 @Override 465 public Parameter[] getParameters() { 466 Executable javaMethod = toJava(); 467 if (javaMethod == null) { 468 return null; 469 } 470 471 java.lang.reflect.Parameter[] javaParameters = javaMethod.getParameters(); 472 Parameter[] res = new Parameter[javaParameters.length]; 473 for (int i = 0; i < res.length; i++) { 474 java.lang.reflect.Parameter src = javaParameters[i]; 475 String paramName = src.isNamePresent() ? src.getName() : null; 476 res[i] = new Parameter(paramName, src.getModifiers(), this, i); 477 } 478 return res; 479 } 480 481 @Override 482 public Annotation[][] getParameterAnnotations() { 483 Executable javaMethod = toJava(); 484 return javaMethod == null ? null : javaMethod.getParameterAnnotations(); 485 } 486 487 @Override 488 public Annotation[] getAnnotations() { 489 Executable javaMethod = toJava(); 490 if (javaMethod != null) { 491 return javaMethod.getAnnotations(); 492 } 493 return new Annotation[0]; 494 } 495 496 @Override 497 public Annotation[] getDeclaredAnnotations() { 498 Executable javaMethod = toJava(); 499 if (javaMethod != null) { 500 return javaMethod.getDeclaredAnnotations(); 501 } 502 return new Annotation[0]; 503 } 504 505 @Override 506 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { 507 Executable javaMethod = toJava(); 508 return javaMethod == null ? null : javaMethod.getAnnotation(annotationClass); 509 } 510 511 public boolean isBridge() { 512 return (BRIDGE & getModifiers()) != 0; 513 } 514 515 @Override 516 public boolean isSynthetic() { 517 return (SYNTHETIC & getModifiers()) != 0; 518 } 519 520 public boolean isVarArgs() { 521 return (VARARGS & getModifiers()) != 0; 522 } 523 524 public boolean isDefault() { 525 if (isConstructor()) { 526 return false; 527 } 528 // Copied from java.lang.Method.isDefault() 529 int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC; 530 return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface(); 531 } 532 533 @Override 534 public Type[] getGenericParameterTypes() { 535 Executable javaMethod = toJava(); 536 return javaMethod == null ? null : javaMethod.getGenericParameterTypes(); 537 } 538 539 public Class<?>[] signatureToTypes() { 540 Signature sig = getSignature(); 541 int count = sig.getParameterCount(false); 542 Class<?>[] result = new Class<?>[count]; 543 for (int i = 0; i < result.length; ++i) { 544 JavaType parameterType = sig.getParameterType(i, holder); 545 HotSpotResolvedJavaType resolvedParameterType = (HotSpotResolvedJavaType) parameterType.resolve(holder); 546 result[i] = resolvedParameterType.mirror(); 547 } 548 return result; 549 } 550 551 private static Method searchMethods(Method[] methods, String name, Class<?> returnType, Class<?>[] parameterTypes) { 552 for (Method m : methods) { 553 if (m.getName().equals(name) && returnType.equals(m.getReturnType()) && Arrays.equals(m.getParameterTypes(), parameterTypes)) { 554 return m; 555 } 556 } 557 return null; 558 } 559 560 private Executable toJava() { 561 if (toJavaCache != null) { 562 return toJavaCache; 563 } 564 try { 565 Class<?>[] parameterTypes = signatureToTypes(); 566 Class<?> returnType = ((HotSpotResolvedJavaType) getSignature().getReturnType(holder).resolve(holder)).mirror(); 567 568 Executable result; 569 if (isConstructor()) { 570 result = holder.mirror().getDeclaredConstructor(parameterTypes); 571 } else { 572 // Do not use Method.getDeclaredMethod() as it can return a bridge method 573 // when this.isBridge() is false and vice versa. 574 result = searchMethods(holder.mirror().getDeclaredMethods(), name, returnType, parameterTypes); 575 } 576 toJavaCache = result; 577 return result; 578 } catch (NoSuchMethodException | NoClassDefFoundError e) { 579 return null; 580 } 581 } 582 583 @Override 584 public boolean canBeInlined() { 585 if (isDontInline()) { 586 return false; 587 } 588 return compilerToVM().canInlineMethod(this); 589 } 590 591 @Override 592 public boolean shouldBeInlined() { 593 if (isForceInline()) { 594 return true; 595 } 596 return compilerToVM().shouldInlineMethod(this); 597 } 598 599 @Override 600 public LineNumberTable getLineNumberTable() { 601 final boolean hasLineNumberTable = (getConstMethodFlags() & config().constMethodHasLineNumberTable) != 0; 602 if (!hasLineNumberTable) { 603 return null; 604 } 605 606 long[] values = compilerToVM().getLineNumberTable(this); 607 if (values == null || values.length == 0) { 608 // Empty table so treat is as non-existent 609 return null; 610 } 611 assert values.length % 2 == 0; 612 int[] bci = new int[values.length / 2]; 613 int[] line = new int[values.length / 2]; 614 615 for (int i = 0; i < values.length / 2; i++) { 616 bci[i] = (int) values[i * 2]; 617 line[i] = (int) values[i * 2 + 1]; 618 } 619 620 return new LineNumberTable(line, bci); 621 } 622 623 @Override 624 public LocalVariableTable getLocalVariableTable() { 625 final boolean hasLocalVariableTable = (getConstMethodFlags() & config().constMethodHasLocalVariableTable) != 0; 626 if (!hasLocalVariableTable) { 627 return null; 628 } 629 630 HotSpotVMConfig config = config(); 631 long localVariableTableElement = compilerToVM().getLocalVariableTableStart(this); 632 final int localVariableTableLength = compilerToVM().getLocalVariableTableLength(this); 633 Local[] locals = new Local[localVariableTableLength]; 634 635 for (int i = 0; i < localVariableTableLength; i++) { 636 final int startBci = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset); 637 final int endBci = startBci + UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset); 638 final int nameCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset); 639 final int typeCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset); 640 final int slot = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset); 641 642 String localName = getConstantPool().lookupUtf8(nameCpIndex); 643 String localType = getConstantPool().lookupUtf8(typeCpIndex); 644 645 locals[i] = new Local(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot); 646 647 // Go to the next LocalVariableTableElement 648 localVariableTableElement += config.localVariableTableElementSize; 649 } 650 651 return new LocalVariableTable(locals); 652 } 653 654 /** 655 * Returns the offset of this method into the v-table. The method must have a v-table entry as 656 * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is 657 * thrown. 658 * 659 * @return the offset of this method into the v-table 660 */ 661 public int vtableEntryOffset(ResolvedJavaType resolved) { 662 if (!isInVirtualMethodTable(resolved)) { 663 throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved); 664 } 665 HotSpotVMConfig config = config(); 666 final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved); 667 return config.klassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset; 668 } 669 670 @Override 671 public boolean isInVirtualMethodTable(ResolvedJavaType resolved) { 672 if (resolved instanceof HotSpotResolvedObjectTypeImpl) { 673 HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved; 674 int vtableIndex = getVtableIndex(hotspotResolved); 675 return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength(); 676 } 677 return false; 678 } 679 680 private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) { 681 if (!holder.isLinked()) { 682 return config().invalidVtableIndex; 683 } 684 if (holder.isInterface()) { 685 if (resolved.isInterface()) { 686 return config().invalidVtableIndex; 687 } 688 return getVtableIndexForInterfaceMethod(resolved); 689 } 690 return getVtableIndex(); 691 } 692 693 /** 694 * Returns this method's virtual table index. 695 * 696 * @return virtual table index 697 */ 698 private int getVtableIndex() { 699 assert !holder.isInterface(); 700 HotSpotVMConfig config = config(); 701 int result = UNSAFE.getInt(metaspaceMethod + config.methodVtableIndexOffset); 702 assert result >= config.nonvirtualVtableIndex : "must be linked"; 703 return result; 704 } 705 706 private int getVtableIndexForInterfaceMethod(ResolvedJavaType resolved) { 707 HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved; 708 return compilerToVM().getVtableIndexForInterfaceMethod(hotspotType, this); 709 } 710 711 /** 712 * The {@link SpeculationLog} for methods compiled by JVMCI hang off this per-declaring-type 713 * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is 714 * never moves and b) we never read from it. 715 * <p> 716 * One implication is that we will preserve {@link SpeculationLog}s for methods that have been 717 * redefined via class redefinition. It's tempting to periodically flush such logs but we cannot 718 * read the JVM_ACC_IS_OBSOLETE bit (or anything else) via the raw pointer as obsoleted methods 719 * are subject to clean up and deletion (see InstanceKlass::purge_previous_versions_internal). 720 */ 721 private static final ClassValue<Map<Long, SpeculationLog>> SpeculationLogs = new ClassValue<Map<Long, SpeculationLog>>() { 722 @Override 723 protected Map<Long, SpeculationLog> computeValue(java.lang.Class<?> type) { 724 return new HashMap<>(4); 725 } 726 }; 727 728 public SpeculationLog getSpeculationLog() { 729 Map<Long, SpeculationLog> map = SpeculationLogs.get(holder.mirror()); 730 synchronized (map) { 731 SpeculationLog log = map.get(this.metaspaceMethod); 732 if (log == null) { 733 log = new HotSpotSpeculationLog(); 734 map.put(metaspaceMethod, log); 735 } 736 return log; 737 } 738 } 739 740 public int intrinsicId() { 741 HotSpotVMConfig config = config(); 742 return UNSAFE.getChar(metaspaceMethod + config.methodIntrinsicIdOffset); 743 } 744 745 public boolean isIntrinsicCandidate() { 746 return (getFlags() & config().methodFlagsIntrinsicCandidate) != 0; 747 } 748 749 /** 750 * Allocates a compile id for this method by asking the VM for one. 751 * 752 * @param entryBCI entry bci 753 * @return compile id 754 */ 755 public int allocateCompileId(int entryBCI) { 756 return compilerToVM().allocateCompileId(this, entryBCI); 757 } 758 759 public boolean hasCodeAtLevel(int entryBCI, int level) { 760 if (entryBCI == config().invocationEntryBci) { 761 return hasCompiledCodeAtLevel(level); 762 } 763 return compilerToVM().hasCompiledCodeForOSR(this, entryBCI, level); 764 } 765 }