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.HotSpotModifiers.BRIDGE;
  28 import static jdk.vm.ci.hotspot.HotSpotModifiers.SYNTHETIC;
  29 import static jdk.vm.ci.hotspot.HotSpotModifiers.VARARGS;
  30 import static jdk.vm.ci.hotspot.HotSpotModifiers.jvmMethodModifiers;
  31 import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
  32 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
  33 
  34 import java.lang.annotation.Annotation;
  35 import java.lang.reflect.Executable;
  36 import java.lang.reflect.Modifier;
  37 import java.lang.reflect.Type;
  38 import java.util.HashMap;
  39 import java.util.Map;
  40 
  41 import jdk.vm.ci.common.JVMCIError;
  42 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
  43 import jdk.vm.ci.meta.Constant;
  44 import jdk.vm.ci.meta.ConstantPool;
  45 import jdk.vm.ci.meta.DefaultProfilingInfo;
  46 import jdk.vm.ci.meta.ExceptionHandler;
  47 import jdk.vm.ci.meta.JavaMethod;
  48 import jdk.vm.ci.meta.JavaType;
  49 import jdk.vm.ci.meta.LineNumberTable;
  50 import jdk.vm.ci.meta.Local;
  51 import jdk.vm.ci.meta.LocalVariableTable;
  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() & 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 isBridge() {
 494         return (BRIDGE & getModifiers()) != 0;
 495     }
 496 
 497     @Override
 498     public boolean isSynthetic() {
 499         return (SYNTHETIC & getModifiers()) != 0;
 500     }
 501 
 502     public boolean isVarArgs() {
 503         return (VARARGS & getModifiers()) != 0;
 504     }
 505 
 506     public boolean isDefault() {
 507         if (isConstructor()) {
 508             return false;
 509         }
 510         // Copied from java.lang.Method.isDefault()
 511         int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
 512         return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
 513     }
 514 
 515     @Override
 516     public Type[] getGenericParameterTypes() {
 517         Executable javaMethod = toJava();
 518         return javaMethod == null ? null : javaMethod.getGenericParameterTypes();
 519     }
 520 
 521     public Class<?>[] signatureToTypes() {
 522         Signature sig = getSignature();
 523         int count = sig.getParameterCount(false);
 524         Class<?>[] result = new Class<?>[count];
 525         for (int i = 0; i < result.length; ++i) {
 526             JavaType parameterType = sig.getParameterType(i, holder);
 527             HotSpotResolvedJavaType resolvedParameterType = (HotSpotResolvedJavaType) parameterType.resolve(holder);
 528             result[i] = resolvedParameterType.mirror();
 529         }
 530         return result;
 531     }
 532 
 533     private Executable toJava() {
 534         if (toJavaCache != null) {
 535             return toJavaCache;
 536         }
 537         try {
 538             Class<?>[] parameterTypes = signatureToTypes();
 539             Executable result = isConstructor() ? holder.mirror().getDeclaredConstructor(parameterTypes) : holder.mirror().getDeclaredMethod(name, parameterTypes);
 540             toJavaCache = result;
 541             return result;
 542         } catch (NoSuchMethodException | NoClassDefFoundError e) {
 543             return null;
 544         }
 545     }
 546 
 547     @Override
 548     public boolean canBeInlined() {
 549         if (isDontInline()) {
 550             return false;
 551         }
 552         return compilerToVM().canInlineMethod(this);
 553     }
 554 
 555     @Override
 556     public boolean shouldBeInlined() {
 557         if (isForceInline()) {
 558             return true;
 559         }
 560         return compilerToVM().shouldInlineMethod(this);
 561     }
 562 
 563     @Override
 564     public LineNumberTable getLineNumberTable() {
 565         final boolean hasLineNumberTable = (getConstMethodFlags() & config().constMethodHasLineNumberTable) != 0;
 566         if (!hasLineNumberTable) {
 567             return null;
 568         }
 569 
 570         long[] values = compilerToVM().getLineNumberTable(this);
 571         if (values == null || values.length == 0) {
 572             // Empty table so treat is as non-existent
 573             return null;
 574         }
 575         assert values.length % 2 == 0;
 576         int[] bci = new int[values.length / 2];
 577         int[] line = new int[values.length / 2];
 578 
 579         for (int i = 0; i < values.length / 2; i++) {
 580             bci[i] = (int) values[i * 2];
 581             line[i] = (int) values[i * 2 + 1];
 582         }
 583 
 584         return new LineNumberTable(line, bci);
 585     }
 586 
 587     @Override
 588     public LocalVariableTable getLocalVariableTable() {
 589         final boolean hasLocalVariableTable = (getConstMethodFlags() & config().constMethodHasLocalVariableTable) != 0;
 590         if (!hasLocalVariableTable) {
 591             return null;
 592         }
 593 
 594         HotSpotVMConfig config = config();
 595         long localVariableTableElement = compilerToVM().getLocalVariableTableStart(this);
 596         final int localVariableTableLength = compilerToVM().getLocalVariableTableLength(this);
 597         Local[] locals = new Local[localVariableTableLength];
 598 
 599         for (int i = 0; i < localVariableTableLength; i++) {
 600             final int startBci = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset);
 601             final int endBci = startBci + UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset);
 602             final int nameCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset);
 603             final int typeCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset);
 604             final int slot = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset);
 605 
 606             String localName = getConstantPool().lookupUtf8(nameCpIndex);
 607             String localType = getConstantPool().lookupUtf8(typeCpIndex);
 608 
 609             locals[i] = new Local(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot);
 610 
 611             // Go to the next LocalVariableTableElement
 612             localVariableTableElement += config.localVariableTableElementSize;
 613         }
 614 
 615         return new LocalVariableTable(locals);
 616     }
 617 
 618     /**
 619      * Returns the offset of this method into the v-table. The method must have a v-table entry as
 620      * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is
 621      * thrown.
 622      *
 623      * @return the offset of this method into the v-table
 624      */
 625     public int vtableEntryOffset(ResolvedJavaType resolved) {
 626         if (!isInVirtualMethodTable(resolved)) {
 627             throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved);
 628         }
 629         HotSpotVMConfig config = config();
 630         final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved);
 631         return config.klassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
 632     }
 633 
 634     @Override
 635     public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
 636         if (resolved instanceof HotSpotResolvedObjectTypeImpl) {
 637             HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved;
 638             int vtableIndex = getVtableIndex(hotspotResolved);
 639             return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength();
 640         }
 641         return false;
 642     }
 643 
 644     private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) {
 645         if (!holder.isLinked()) {
 646             return config().invalidVtableIndex;
 647         }
 648         if (holder.isInterface()) {
 649             if (resolved.isInterface()) {
 650                 return config().invalidVtableIndex;
 651             }
 652             return getVtableIndexForInterfaceMethod(resolved);
 653         }
 654         return getVtableIndex();
 655     }
 656 
 657     /**
 658      * Returns this method's virtual table index.
 659      *
 660      * @return virtual table index
 661      */
 662     private int getVtableIndex() {
 663         assert !holder.isInterface();
 664         HotSpotVMConfig config = config();
 665         int result = UNSAFE.getInt(metaspaceMethod + config.methodVtableIndexOffset);
 666         assert result >= config.nonvirtualVtableIndex : "must be linked";
 667         return result;
 668     }
 669 
 670     private int getVtableIndexForInterfaceMethod(ResolvedJavaType resolved) {
 671         HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved;
 672         return compilerToVM().getVtableIndexForInterfaceMethod(hotspotType, this);
 673     }
 674 
 675     /**
 676      * The {@link SpeculationLog} for methods compiled by JVMCI hang off this per-declaring-type
 677      * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is
 678      * never moves and b) we never read from it.
 679      * <p>
 680      * One implication is that we will preserve {@link SpeculationLog}s for methods that have been
 681      * redefined via class redefinition. It's tempting to periodically flush such logs but we cannot
 682      * read the JVM_ACC_IS_OBSOLETE bit (or anything else) via the raw pointer as obsoleted methods
 683      * are subject to clean up and deletion (see InstanceKlass::purge_previous_versions_internal).
 684      */
 685     private static final ClassValue<Map<Long, SpeculationLog>> SpeculationLogs = new ClassValue<Map<Long, SpeculationLog>>() {
 686         @Override
 687         protected Map<Long, SpeculationLog> computeValue(java.lang.Class<?> type) {
 688             return new HashMap<>(4);
 689         }
 690     };
 691 
 692     public SpeculationLog getSpeculationLog() {
 693         Map<Long, SpeculationLog> map = SpeculationLogs.get(holder.mirror());
 694         synchronized (map) {
 695             SpeculationLog log = map.get(this.metaspaceMethod);
 696             if (log == null) {
 697                 log = new HotSpotSpeculationLog();
 698                 map.put(metaspaceMethod, log);
 699             }
 700             return log;
 701         }
 702     }
 703 
 704     public int intrinsicId() {
 705         HotSpotVMConfig config = config();
 706         return UNSAFE.getChar(metaspaceMethod + config.methodIntrinsicIdOffset);
 707     }
 708 
 709     public boolean isIntrinsicCandidate() {
 710         return (getFlags() & config().methodFlagsIntrinsicCandidate) != 0;
 711     }
 712 
 713     /**
 714      * Allocates a compile id for this method by asking the VM for one.
 715      *
 716      * @param entryBCI entry bci
 717      * @return compile id
 718      */
 719     public int allocateCompileId(int entryBCI) {
 720         return compilerToVM().allocateCompileId(this, entryBCI);
 721     }
 722 
 723     public boolean hasCodeAtLevel(int entryBCI, int level) {
 724         if (entryBCI == config().invocationEntryBci) {
 725             return hasCompiledCodeAtLevel(level);
 726         }
 727         return compilerToVM().hasCompiledCodeForOSR(this, entryBCI, level);
 728     }
 729 }