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