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 }