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