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