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