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