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 }