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