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