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