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