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