package jdk.vm.ci.hotspot;
import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import static jdk.vm.ci.hotspot.HotSpotModifiers.BRIDGE;
import static jdk.vm.ci.hotspot.HotSpotModifiers.SYNTHETIC;
import static jdk.vm.ci.hotspot.HotSpotModifiers.VARARGS;
import static jdk.vm.ci.hotspot.HotSpotModifiers.jvmMethodModifiers;
import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
import java.lang.annotation.Annotation;
import java.lang.reflect.Executable;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.DefaultProfilingInfo;
import jdk.vm.ci.meta.ExceptionHandler;
import jdk.vm.ci.meta.JavaMethod;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.LineNumberTable;
import jdk.vm.ci.meta.Local;
import jdk.vm.ci.meta.LocalVariableTable;
import jdk.vm.ci.meta.ProfilingInfo;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.SpeculationLog;
import jdk.vm.ci.meta.TriState;
final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, MetaspaceHandleObject {
private final long metadataHandle;
private final HotSpotResolvedObjectTypeImpl holder;
private final HotSpotConstantPool constantPool;
final HotSpotSignature signature;
private HotSpotMethodData methodData;
private byte[] code;
volatile Executable toJavaCache;
private String nameCache;
private static HotSpotResolvedObjectTypeImpl getHolder(long metaspaceHandle) {
HotSpotVMConfig config = config();
long metaspaceMethod = UNSAFE.getLong(metaspaceHandle);
assert metaspaceMethod != 0 : metaspaceHandle;
final long metaspaceConstMethod = UNSAFE.getAddress(metaspaceMethod + config.methodConstMethodOffset);
final long metaspaceConstantPool = UNSAFE.getAddress(metaspaceConstMethod + config.constMethodConstantsOffset);
HotSpotResolvedObjectTypeImpl result = compilerToVM().getResolvedJavaType(metaspaceConstantPool + config.constantPoolHolderOffset, false);
assert result != null;
return result;
}
@SuppressWarnings("unused")
@VMEntryPoint
private static HotSpotResolvedJavaMethod fromMetaspace(long metaspaceHandle) {
HotSpotResolvedObjectTypeImpl holder = getHolder(metaspaceHandle);
return holder.createMethod(metaspaceHandle);
}
HotSpotResolvedJavaMethodImpl(HotSpotResolvedObjectTypeImpl holder, long metaspaceHandle) {
this.metadataHandle = metaspaceHandle;
this.holder = holder;
HotSpotVMConfig config = config();
final long constMethod = getConstMethod();
final long metaspaceConstantPool = UNSAFE.getAddress(constMethod + config.constMethodConstantsOffset);
if (metaspaceConstantPool == holder.getConstantPool().getMetaspaceConstantPool()) {
this.constantPool = holder.getConstantPool();
} else {
this.constantPool = compilerToVM().getConstantPool(this);
}
final int signatureIndex = UNSAFE.getChar(constMethod + config.constMethodSignatureIndexOffset);
this.signature = (HotSpotSignature) constantPool.lookupSignature(signatureIndex);
HandleCleaner.create(this, metaspaceHandle);
}
private long getConstMethod() {
return UNSAFE.getAddress(getMetaspaceMethod() + config().methodConstMethodOffset);
}
@Override
public String getName() {
if (nameCache == null) {
final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
nameCache = constantPool.lookupUtf8(nameIndex);
}
return nameCache;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof HotSpotResolvedJavaMethodImpl) {
HotSpotResolvedJavaMethodImpl that = (HotSpotResolvedJavaMethodImpl) obj;
return that.getMetaspaceMethod() == getMetaspaceMethod();
}
return false;
}
@Override
public int hashCode() {
return (int) getMetaspaceMethod();
}
private int getFlags() {
return UNSAFE.getShort(getMetaspaceMethod() + config().methodFlagsOffset);
}
private int getConstMethodFlags() {
return UNSAFE.getChar(getConstMethod() + config().constMethodFlagsOffset);
}
@Override
public HotSpotResolvedObjectTypeImpl getDeclaringClass() {
return holder;
}
public Constant getMetaspaceMethodConstant() {
return HotSpotMetaspaceConstantImpl.forMetaspaceObject(this, false);
}
long getMetaspaceMethod() {
long metaspacePointer = getMetaspacePointer();
if (metaspacePointer == 0) {
throw new NullPointerException("Method* is null");
}
return metaspacePointer;
}
@Override
public long getMetadataHandle() {
return metadataHandle;
}
@Override
public Constant getEncoding() {
return getMetaspaceMethodConstant();
}
public int getAllModifiers() {
return UNSAFE.getInt(getMetaspaceMethod() + config().methodAccessFlagsOffset);
}
@Override
public int getModifiers() {
return getAllModifiers() & jvmMethodModifiers();
}
@Override
public boolean canBeStaticallyBound() {
return (isFinal() || isPrivate() || isStatic() || holder.isLeaf() || isConstructor()) && isConcrete();
}
@Override
public byte[] getCode() {
if (getCodeSize() == 0) {
return null;
}
if (code == null && holder.isLinked()) {
code = compilerToVM().getBytecode(this);
assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
}
return code;
}
@Override
public int getCodeSize() {
return UNSAFE.getChar(getConstMethod() + config().constMethodCodeSizeOffset);
}
@Override
public ExceptionHandler[] getExceptionHandlers() {
final boolean hasExceptionTable = (getConstMethodFlags() & config().constMethodHasExceptionTable) != 0;
if (!hasExceptionTable) {
return new ExceptionHandler[0];
}
HotSpotVMConfig config = config();
final int exceptionTableLength = compilerToVM().getExceptionTableLength(this);
ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength];
long exceptionTableElement = compilerToVM().getExceptionTableStart(this);
for (int i = 0; i < exceptionTableLength; i++) {
final int startPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementStartPcOffset);
final int endPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementEndPcOffset);
final int handlerPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementHandlerPcOffset);
int catchTypeIndex = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementCatchTypeIndexOffset);
JavaType catchType;
if (catchTypeIndex == 0) {
catchType = null;
} else {
final int opcode = -1;
catchType = constantPool.lookupType(catchTypeIndex, opcode);
if (catchType instanceof HotSpotResolvedObjectTypeImpl) {
HotSpotResolvedObjectTypeImpl resolvedType = (HotSpotResolvedObjectTypeImpl) catchType;
if (resolvedType.equals(runtime().getJavaLangThrowable())) {
catchTypeIndex = 0;
catchType = null;
}
}
}
handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType);
exceptionTableElement += config.exceptionTableElementSize;
}
return handlers;
}
@Override
public boolean isCallerSensitive() {
return (getFlags() & config().methodFlagsCallerSensitive) != 0;
}
@Override
public boolean isForceInline() {
return (getFlags() & config().methodFlagsForceInline) != 0;
}
@Override
public boolean hasReservedStackAccess() {
return (getFlags() & config().methodFlagsReservedStackAccess) != 0;
}
@Override
public void setNotInlinableOrCompilable() {
compilerToVM().setNotInlinableOrCompilable(this);
}
@Override
public boolean ignoredBySecurityStackWalk() {
return compilerToVM().methodIsIgnoredBySecurityStackWalk(this);
}
@Override
public boolean isClassInitializer() {
if (isStatic()) {
final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
long nameSymbol = constantPool.getEntryAt(nameIndex);
long clinitSymbol = config().symbolClinit;
return nameSymbol == clinitSymbol;
}
return false;
}
@Override
public boolean isConstructor() {
if (!isStatic()) {
final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
long nameSymbol = constantPool.getEntryAt(nameIndex);
long initSymbol = config().symbolInit;
return nameSymbol == initSymbol;
}
return false;
}
@Override
public int getMaxLocals() {
if (isAbstract() || isNative()) {
return 0;
}
HotSpotVMConfig config = config();
return UNSAFE.getChar(getConstMethod() + config.methodMaxLocalsOffset);
}
@Override
public int getMaxStackSize() {
if (isAbstract() || isNative()) {
return 0;
}
HotSpotVMConfig config = config();
return config.extraStackEntries + UNSAFE.getChar(getConstMethod() + config.constMethodMaxStackOffset);
}
@Override
public StackTraceElement asStackTraceElement(int bci) {
if (bci < 0 || bci >= getCodeSize()) {
StackTraceElement ste = compilerToVM().getStackTraceElement(this, 0);
return new StackTraceElement(ste.getClassName(), ste.getMethodName(), ste.getFileName(), -1);
}
return compilerToVM().getStackTraceElement(this, bci);
}
@Override
public ResolvedJavaMethod uniqueConcreteMethod(HotSpotResolvedObjectType receiver) {
assert !canBeStaticallyBound() : this;
if (receiver.isInterface()) {
return null;
}
assert !receiver.isLinked() || isInVirtualMethodTable(receiver);
if (this.isDefault()) {
return null;
}
return compilerToVM().findUniqueConcreteMethod(((HotSpotResolvedObjectTypeImpl) receiver), this);
}
@Override
public HotSpotSignature getSignature() {
return signature;
}
private long getCompiledCode() {
HotSpotVMConfig config = config();
return UNSAFE.getAddress(getMetaspaceMethod() + config.methodCodeOffset);
}
@Override
public boolean hasCompiledCode() {
return getCompiledCode() != 0L;
}
@Override
public boolean hasCompiledCodeAtLevel(int level) {
long compiledCode = getCompiledCode();
if (compiledCode != 0) {
return UNSAFE.getInt(compiledCode + config().nmethodCompLevelOffset) == level;
}
return false;
}
@Override
public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
ProfilingInfo info;
if (Option.UseProfilingInformation.getBoolean() && methodData == null) {
long metaspaceMethodData = UNSAFE.getAddress(getMetaspaceMethod() + config().methodDataOffset);
if (metaspaceMethodData != 0) {
methodData = new HotSpotMethodData(metaspaceMethodData, this);
String methodDataFilter = Option.TraceMethodDataFilter.getString();
if (methodDataFilter != null && this.format("%H.%n").contains(methodDataFilter)) {
String line = methodData.toString() + System.lineSeparator();
byte[] lineBytes = line.getBytes();
CompilerToVM.compilerToVM().writeDebugOutput(lineBytes, 0, lineBytes.length, true, true);
}
}
}
if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) {
info = DefaultProfilingInfo.get(TriState.FALSE);
} else {
info = new HotSpotProfilingInfo(methodData, this, includeNormal, includeOSR);
}
return info;
}
@Override
public void reprofile() {
compilerToVM().reprofile(this);
}
@Override
public ConstantPool getConstantPool() {
return constantPool;
}
@Override
public Parameter[] getParameters() {
if (signature.getParameterCount(false) == 0) {
return new ResolvedJavaMethod.Parameter[0];
}
return runtime().reflection.getParameters(this);
}
@Override
public Annotation[][] getParameterAnnotations() {
if ((getConstMethodFlags() & config().constMethodHasParameterAnnotations) == 0 || isClassInitializer()) {
return new Annotation[signature.getParameterCount(false)][0];
}
return runtime().reflection.getParameterAnnotations(this);
}
@Override
public Annotation[] getAnnotations() {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
return new Annotation[0];
}
return runtime().reflection.getMethodAnnotations(this);
}
@Override
public Annotation[] getDeclaredAnnotations() {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
return new Annotation[0];
}
return runtime().reflection.getMethodDeclaredAnnotations(this);
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
return null;
}
return runtime().reflection.getMethodAnnotation(this, annotationClass);
}
@Override
public boolean isBridge() {
return (BRIDGE & getModifiers()) != 0;
}
@Override
public boolean isSynthetic() {
return (SYNTHETIC & getModifiers()) != 0;
}
@Override
public boolean isVarArgs() {
return (VARARGS & getModifiers()) != 0;
}
@Override
public boolean isDefault() {
int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
}
@Override
public Type[] getGenericParameterTypes() {
if (isClassInitializer()) {
return new Type[0];
}
return runtime().reflection.getGenericParameterTypes(this);
}
@Override
public boolean canBeInlined() {
if (hasNeverInlineDirective()) {
return false;
}
return compilerToVM().isCompilable(this);
}
@Override
public boolean hasNeverInlineDirective() {
return compilerToVM().hasNeverInlineDirective(this);
}
@Override
public boolean shouldBeInlined() {
if (isForceInline()) {
return true;
}
return compilerToVM().shouldInlineMethod(this);
}
@Override
public LineNumberTable getLineNumberTable() {
final boolean hasLineNumberTable = (getConstMethodFlags() & config().constMethodHasLineNumberTable) != 0;
if (!hasLineNumberTable) {
return null;
}
long[] values = compilerToVM().getLineNumberTable(this);
if (values == null || values.length == 0) {
return null;
}
assert values.length % 2 == 0;
int[] bci = new int[values.length / 2];
int[] line = new int[values.length / 2];
for (int i = 0; i < values.length / 2; i++) {
bci[i] = (int) values[i * 2];
line[i] = (int) values[i * 2 + 1];
}
return new LineNumberTable(line, bci);
}
@Override
public LocalVariableTable getLocalVariableTable() {
final boolean hasLocalVariableTable = (getConstMethodFlags() & config().constMethodHasLocalVariableTable) != 0;
if (!hasLocalVariableTable) {
return null;
}
HotSpotVMConfig config = config();
long localVariableTableElement = compilerToVM().getLocalVariableTableStart(this);
final int localVariableTableLength = compilerToVM().getLocalVariableTableLength(this);
Local[] locals = new Local[localVariableTableLength];
for (int i = 0; i < localVariableTableLength; i++) {
final int startBci = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset);
final int endBci = startBci + UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset);
final int nameCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset);
final int typeCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset);
final int slot = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset);
String localName = getConstantPool().lookupUtf8(nameCpIndex);
String localType = getConstantPool().lookupUtf8(typeCpIndex);
locals[i] = new Local(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot);
localVariableTableElement += config.localVariableTableElementSize;
}
return new LocalVariableTable(locals);
}
@Override
public int vtableEntryOffset(ResolvedJavaType resolved) {
if (!isInVirtualMethodTable(resolved)) {
throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved);
}
HotSpotVMConfig config = config();
final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved);
return config.klassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
}
@Override
public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
if (resolved instanceof HotSpotResolvedObjectTypeImpl) {
HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved;
int vtableIndex = getVtableIndex(hotspotResolved);
return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength();
}
return false;
}
private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) {
if (!holder.isLinked()) {
return config().invalidVtableIndex;
}
if (holder.isInterface()) {
if (resolved.isInterface() || !resolved.isLinked()) {
return config().invalidVtableIndex;
}
return getVtableIndexForInterfaceMethod(resolved);
}
return getVtableIndex();
}
private int getVtableIndex() {
assert !holder.isInterface();
HotSpotVMConfig config = config();
int result = UNSAFE.getInt(getMetaspaceMethod() + config.methodVtableIndexOffset);
assert result >= config.nonvirtualVtableIndex : "must be linked";
return result;
}
private int getVtableIndexForInterfaceMethod(ResolvedJavaType resolved) {
HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved;
return compilerToVM().getVtableIndexForInterfaceMethod(hotspotType, this);
}
@Override
public SpeculationLog getSpeculationLog() {
long address = compilerToVM().getFailedSpeculationsAddress(this);
return new HotSpotSpeculationLog(address);
}
@Override
public int intrinsicId() {
HotSpotVMConfig config = config();
return UNSAFE.getChar(getMetaspaceMethod() + config.methodIntrinsicIdOffset);
}
@Override
public boolean isIntrinsicCandidate() {
return (getFlags() & config().methodFlagsIntrinsicCandidate) != 0;
}
@Override
public int allocateCompileId(int entryBCI) {
return compilerToVM().allocateCompileId(this, entryBCI);
}
@Override
public boolean hasCodeAtLevel(int entryBCI, int level) {
if (entryBCI == config().invocationEntryBci) {
return hasCompiledCodeAtLevel(level);
}
return compilerToVM().hasCompiledCodeForOSR(this, entryBCI, level);
}
@Override
public int methodIdnum() {
return UNSAFE.getChar(getConstMethod() + config().constMethodMethodIdnumOffset);
}
}