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