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.InvocationTargetException;
  33 import java.lang.reflect.Method;
  34 import java.lang.reflect.Modifier;
  35 import java.lang.reflect.Type;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 
  39 import jdk.vm.ci.common.JVMCIError;
  40 import jdk.vm.ci.meta.Constant;
  41 import jdk.vm.ci.meta.ConstantPool;
  42 import jdk.vm.ci.meta.DefaultProfilingInfo;
  43 import jdk.vm.ci.meta.ExceptionHandler;
  44 import jdk.vm.ci.meta.JavaConstant;
  45 import jdk.vm.ci.meta.JavaMethod;
  46 import jdk.vm.ci.meta.JavaType;
  47 import jdk.vm.ci.meta.LineNumberTable;
  48 import jdk.vm.ci.meta.LineNumberTableImpl;
  49 import jdk.vm.ci.meta.Local;
  50 import jdk.vm.ci.meta.LocalImpl;
  51 import jdk.vm.ci.meta.LocalVariableTable;
  52 import jdk.vm.ci.meta.LocalVariableTableImpl;
  53 import jdk.vm.ci.meta.ModifiersProvider;
  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, HotSpotProxified, 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(null, constMethod + config.constMethodConstantsOffset);
 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() & ModifiersProvider.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      * Manually adds a DontInline annotation to this method.
 312      */
 313     public void setNotInlineable() {
 314         compilerToVM().doNotInlineOrCompile(this);
 315     }
 316 
 317     /**
 318      * Returns true if this method is one of the special methods that is ignored by security stack
 319      * walks.
 320      *
 321      * @return true if special method ignored by security stack walks, false otherwise
 322      */
 323     public boolean ignoredBySecurityStackWalk() {
 324         return compilerToVM().methodIsIgnoredBySecurityStackWalk(this);
 325     }
 326 
 327     @Override
 328     public boolean isClassInitializer() {
 329         return "<clinit>".equals(name) && isStatic();
 330     }
 331 
 332     @Override
 333     public boolean isConstructor() {
 334         return "<init>".equals(name) && !isStatic();
 335     }
 336 
 337     @Override
 338     public int getMaxLocals() {
 339         if (isAbstract() || isNative()) {
 340             return 0;
 341         }
 342         HotSpotVMConfig config = config();
 343         return UNSAFE.getChar(getConstMethod() + config.methodMaxLocalsOffset);
 344     }
 345 
 346     @Override
 347     public int getMaxStackSize() {
 348         if (isAbstract() || isNative()) {
 349             return 0;
 350         }
 351         HotSpotVMConfig config = config();
 352         return config.extraStackEntries + UNSAFE.getChar(getConstMethod() + config.constMethodMaxStackOffset);
 353     }
 354 
 355     @Override
 356     public StackTraceElement asStackTraceElement(int bci) {
 357         if (bci < 0 || bci >= getCodeSize()) {
 358             // HotSpot code can only construct stack trace elements for valid bcis
 359             StackTraceElement ste = compilerToVM().getStackTraceElement(this, 0);
 360             return new StackTraceElement(ste.getClassName(), ste.getMethodName(), ste.getFileName(), -1);
 361         }
 362         return compilerToVM().getStackTraceElement(this, bci);
 363     }
 364 
 365     public ResolvedJavaMethod uniqueConcreteMethod(HotSpotResolvedObjectType receiver) {
 366         if (receiver.isInterface()) {
 367             // Cannot trust interfaces. Because of:
 368             // interface I { void foo(); }
 369             // class A { public void foo() {} }
 370             // class B extends A implements I { }
 371             // class C extends B { public void foo() { } }
 372             // class D extends B { }
 373             // Would lead to identify C.foo() as the unique concrete method for I.foo() without
 374             // seeing A.foo().
 375             return null;
 376         }
 377         if (this.isDefault()) {
 378             // CHA for default methods doesn't work and may crash the VM
 379             return null;
 380         }
 381         return compilerToVM().findUniqueConcreteMethod(((HotSpotResolvedObjectTypeImpl) receiver), this);
 382     }
 383 
 384     @Override
 385     public HotSpotSignature getSignature() {
 386         return signature;
 387     }
 388 
 389     /**
 390      * Gets the value of {@code Method::_code}.
 391      *
 392      * @return the value of {@code Method::_code}
 393      */
 394     private long getCompiledCode() {
 395         HotSpotVMConfig config = config();
 396         return UNSAFE.getAddress(metaspaceMethod + config.methodCodeOffset);
 397     }
 398 
 399     /**
 400      * Returns whether this method has compiled code.
 401      *
 402      * @return true if this method has compiled code, false otherwise
 403      */
 404     public boolean hasCompiledCode() {
 405         return getCompiledCode() != 0L;
 406     }
 407 
 408     /**
 409      * @param level
 410      * @return true if the currently installed code was generated at {@code level}.
 411      */
 412     public boolean hasCompiledCodeAtLevel(int level) {
 413         long compiledCode = getCompiledCode();
 414         if (compiledCode != 0) {
 415             return UNSAFE.getInt(compiledCode + config().nmethodCompLevelOffset) == level;
 416         }
 417         return false;
 418     }
 419 
 420     private static final String TraceMethodDataFilter = System.getProperty("jvmci.traceMethodDataFilter");
 421 
 422     @Override
 423     public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
 424         ProfilingInfo info;
 425 
 426         if (methodData == null) {
 427             long metaspaceMethodData = UNSAFE.getAddress(metaspaceMethod + config().methodDataOffset);
 428             if (metaspaceMethodData != 0) {
 429                 methodData = new HotSpotMethodData(metaspaceMethodData, this);
 430                 if (TraceMethodDataFilter != null && this.format("%H.%n").contains(TraceMethodDataFilter)) {
 431                     System.out.println("Raw method data for " + this.format("%H.%n(%p)") + ":");
 432                     System.out.println(methodData.toString());
 433                 }
 434             }
 435         }
 436 
 437         if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) {
 438             // Be optimistic and return false for exceptionSeen. A methodDataOop is allocated in
 439             // case of a deoptimization.
 440             info = DefaultProfilingInfo.get(TriState.FALSE);
 441         } else {
 442             info = new HotSpotProfilingInfo(methodData, this, includeNormal, includeOSR);
 443         }
 444         return info;
 445     }
 446 
 447     @Override
 448     public void reprofile() {
 449         compilerToVM().reprofile(this);
 450     }
 451 
 452     @Override
 453     public ConstantPool getConstantPool() {
 454         return constantPool;
 455     }
 456 
 457     @Override
 458     public Annotation[][] getParameterAnnotations() {
 459         Executable javaMethod = toJava();
 460         return javaMethod == null ? null : javaMethod.getParameterAnnotations();
 461     }
 462 
 463     @Override
 464     public Annotation[] getAnnotations() {
 465         Executable javaMethod = toJava();
 466         return javaMethod == null ? new Annotation[0] : javaMethod.getAnnotations();
 467     }
 468 
 469     @Override
 470     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
 471         Executable javaMethod = toJava();
 472         return javaMethod == null ? null : javaMethod.getAnnotation(annotationClass);
 473     }
 474 
 475     public boolean isDefault() {
 476         if (isConstructor()) {
 477             return false;
 478         }
 479         // Copied from java.lang.Method.isDefault()
 480         int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
 481         return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
 482     }
 483 
 484     @Override
 485     public Type[] getGenericParameterTypes() {
 486         Executable javaMethod = toJava();
 487         return javaMethod == null ? null : javaMethod.getGenericParameterTypes();
 488     }
 489 
 490     public Class<?>[] signatureToTypes() {
 491         Signature sig = getSignature();
 492         int count = sig.getParameterCount(false);
 493         Class<?>[] result = new Class<?>[count];
 494         for (int i = 0; i < result.length; ++i) {
 495             JavaType parameterType = sig.getParameterType(i, holder);
 496             HotSpotResolvedJavaType resolvedParameterType = (HotSpotResolvedJavaType) parameterType.resolve(holder);
 497             result[i] = resolvedParameterType.mirror();
 498         }
 499         return result;
 500     }
 501 
 502     private Executable toJava() {
 503         if (toJavaCache != null) {
 504             return toJavaCache;
 505         }
 506         try {
 507             Class<?>[] parameterTypes = signatureToTypes();
 508             Executable result = isConstructor() ? holder.mirror().getDeclaredConstructor(parameterTypes) : holder.mirror().getDeclaredMethod(name, parameterTypes);
 509             toJavaCache = result;
 510             return result;
 511         } catch (NoSuchMethodException | NoClassDefFoundError e) {
 512             return null;
 513         }
 514     }
 515 
 516     @Override
 517     public boolean canBeInlined() {
 518         if (isDontInline()) {
 519             return false;
 520         }
 521         return compilerToVM().canInlineMethod(this);
 522     }
 523 
 524     @Override
 525     public boolean shouldBeInlined() {
 526         if (isForceInline()) {
 527             return true;
 528         }
 529         return compilerToVM().shouldInlineMethod(this);
 530     }
 531 
 532     @Override
 533     public LineNumberTable getLineNumberTable() {
 534         final boolean hasLineNumberTable = (getConstMethodFlags() & config().constMethodHasLineNumberTable) != 0;
 535         if (!hasLineNumberTable) {
 536             return null;
 537         }
 538 
 539         long[] values = compilerToVM().getLineNumberTable(this);
 540         if (values == null || values.length == 0) {
 541             // Empty table so treat is as non-existent
 542             return null;
 543         }
 544         assert values.length % 2 == 0;
 545         int[] bci = new int[values.length / 2];
 546         int[] line = new int[values.length / 2];
 547 
 548         for (int i = 0; i < values.length / 2; i++) {
 549             bci[i] = (int) values[i * 2];
 550             line[i] = (int) values[i * 2 + 1];
 551         }
 552 
 553         return new LineNumberTableImpl(line, bci);
 554     }
 555 
 556     @Override
 557     public LocalVariableTable getLocalVariableTable() {
 558         final boolean hasLocalVariableTable = (getConstMethodFlags() & config().constMethodHasLocalVariableTable) != 0;
 559         if (!hasLocalVariableTable) {
 560             return null;
 561         }
 562 
 563         HotSpotVMConfig config = config();
 564         long localVariableTableElement = compilerToVM().getLocalVariableTableStart(this);
 565         final int localVariableTableLength = compilerToVM().getLocalVariableTableLength(this);
 566         Local[] locals = new Local[localVariableTableLength];
 567 
 568         for (int i = 0; i < localVariableTableLength; i++) {
 569             final int startBci = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset);
 570             final int endBci = startBci + UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset);
 571             final int nameCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset);
 572             final int typeCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset);
 573             final int slot = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset);
 574 
 575             String localName = getConstantPool().lookupUtf8(nameCpIndex);
 576             String localType = getConstantPool().lookupUtf8(typeCpIndex);
 577 
 578             locals[i] = new LocalImpl(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot);
 579 
 580             // Go to the next LocalVariableTableElement
 581             localVariableTableElement += config.localVariableTableElementSize;
 582         }
 583 
 584         return new LocalVariableTableImpl(locals);
 585     }
 586 
 587     /**
 588      * Returns the offset of this method into the v-table. The method must have a v-table entry as
 589      * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is
 590      * thrown.
 591      *
 592      * @return the offset of this method into the v-table
 593      */
 594     public int vtableEntryOffset(ResolvedJavaType resolved) {
 595         if (!isInVirtualMethodTable(resolved)) {
 596             throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved);
 597         }
 598         HotSpotVMConfig config = config();
 599         final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved);
 600         return config.instanceKlassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
 601     }
 602 
 603     @Override
 604     public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
 605         if (resolved instanceof HotSpotResolvedObjectTypeImpl) {
 606             HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved;
 607             int vtableIndex = getVtableIndex(hotspotResolved);
 608             return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength();
 609         }
 610         return false;
 611     }
 612 
 613     private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) {
 614         if (!holder.isLinked()) {
 615             return config().invalidVtableIndex;
 616         }
 617         if (holder.isInterface()) {
 618             if (resolved.isInterface()) {
 619                 return config().invalidVtableIndex;
 620             }
 621             return getVtableIndexForInterfaceMethod(resolved);
 622         }
 623         return getVtableIndex();
 624     }
 625 
 626     /**
 627      * Returns this method's virtual table index.
 628      *
 629      * @return virtual table index
 630      */
 631     private int getVtableIndex() {
 632         assert !holder.isInterface();
 633         HotSpotVMConfig config = config();
 634         int result = UNSAFE.getInt(metaspaceMethod + config.methodVtableIndexOffset);
 635         assert result >= config.nonvirtualVtableIndex : "must be linked";
 636         return result;
 637     }
 638 
 639     private int getVtableIndexForInterfaceMethod(ResolvedJavaType resolved) {
 640         HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved;
 641         return compilerToVM().getVtableIndexForInterfaceMethod(hotspotType, this);
 642     }
 643 
 644     /**
 645      * The {@link SpeculationLog} for methods compiled by JVMCI hang off this per-declaring-type
 646      * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is
 647      * never moves and b) we never read from it.
 648      * <p>
 649      * One implication is that we will preserve {@link SpeculationLog}s for methods that have been
 650      * redefined via class redefinition. It's tempting to periodically flush such logs but we cannot
 651      * read the JVM_ACC_IS_OBSOLETE bit (or anything else) via the raw pointer as obsoleted methods
 652      * are subject to clean up and deletion (see InstanceKlass::purge_previous_versions_internal).
 653      */
 654     private static final ClassValue<Map<Long, SpeculationLog>> SpeculationLogs = new ClassValue<Map<Long, SpeculationLog>>() {
 655         @Override
 656         protected Map<Long, SpeculationLog> computeValue(java.lang.Class<?> type) {
 657             return new HashMap<>(4);
 658         }
 659     };
 660 
 661     public SpeculationLog getSpeculationLog() {
 662         Map<Long, SpeculationLog> map = SpeculationLogs.get(holder.mirror());
 663         synchronized (map) {
 664             SpeculationLog log = map.get(this.metaspaceMethod);
 665             if (log == null) {
 666                 log = new HotSpotSpeculationLog();
 667                 map.put(metaspaceMethod, log);
 668             }
 669             return log;
 670         }
 671     }
 672 
 673     public int intrinsicId() {
 674         HotSpotVMConfig config = config();
 675         return UNSAFE.getChar(metaspaceMethod + config.methodIntrinsicIdOffset);
 676     }
 677 
 678     @Override
 679     public JavaConstant invoke(JavaConstant receiver, JavaConstant[] arguments) {
 680         assert !isConstructor();
 681         Method javaMethod = (Method) toJava();
 682         javaMethod.setAccessible(true);
 683 
 684         Object[] objArguments = new Object[arguments.length];
 685         for (int i = 0; i < arguments.length; i++) {
 686             objArguments[i] = HotSpotObjectConstantImpl.asBoxedValue(arguments[i]);
 687         }
 688         Object objReceiver = receiver != null && !receiver.isNull() ? ((HotSpotObjectConstantImpl) receiver).object() : null;
 689 
 690         try {
 691             Object objResult = javaMethod.invoke(objReceiver, objArguments);
 692             return javaMethod.getReturnType() == void.class ? null : HotSpotObjectConstantImpl.forBoxedValue(getSignature().getReturnKind(), objResult);
 693 
 694         } catch (IllegalAccessException | InvocationTargetException ex) {
 695             throw new IllegalArgumentException(ex);
 696         }
 697     }
 698 
 699     /**
 700      * Allocates a compile id for this method by asking the VM for one.
 701      *
 702      * @param entryBCI entry bci
 703      * @return compile id
 704      */
 705     public int allocateCompileId(int entryBCI) {
 706         return compilerToVM().allocateCompileId(this, entryBCI);
 707     }
 708 
 709     public boolean hasCodeAtLevel(int entryBCI, int level) {
 710         if (entryBCI == config().invocationEntryBci) {
 711             return hasCompiledCodeAtLevel(level);
 712         }
 713         return compilerToVM().hasCompiledCodeForOSR(this, entryBCI, level);
 714     }
 715 }