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