8357660: [JVMCI] Add support for retrieving all BootstrapMethodInvocations directly from ConstantPool

Reviewed-by: dnsimon, yzheng
This commit is contained in:
Tom Shull 2025-06-04 13:50:36 +00:00 committed by Doug Simon
parent a653ff4893
commit 0352477ff5
5 changed files with 144 additions and 12 deletions

View File

@ -813,6 +813,14 @@ C2V_VMENTRY_NULL(jobject, lookupConstantInPool, (JNIEnv* env, jobject, ARGUMENT_
return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(obj));
C2V_END
C2V_VMENTRY_0(jint, getNumIndyEntries, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp)))
constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
if (cp->cache()->resolved_indy_entries() == nullptr) {
return 0;
}
return cp->resolved_indy_entries_length();
C2V_END
C2V_VMENTRY_NULL(jobjectArray, resolveBootstrapMethod, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index))
constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
constantTag tag = cp->tag_at(index);
@ -3320,6 +3328,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "lookupAppendixInPool", CC "(" HS_CONSTANT_POOL2 "II)" OBJECTCONSTANT, FN_PTR(lookupAppendixInPool)},
{CC "lookupMethodInPool", CC "(" HS_CONSTANT_POOL2 "IB" HS_METHOD2 ")" HS_METHOD, FN_PTR(lookupMethodInPool)},
{CC "lookupConstantInPool", CC "(" HS_CONSTANT_POOL2 "IZ)" JAVACONSTANT, FN_PTR(lookupConstantInPool)},
{CC "getNumIndyEntries", CC "(" HS_CONSTANT_POOL2 ")I", FN_PTR(getNumIndyEntries)},
{CC "resolveBootstrapMethod", CC "(" HS_CONSTANT_POOL2 "I)[" OBJECT, FN_PTR(resolveBootstrapMethod)},
{CC "bootstrapArgumentIndexAt", CC "(" HS_CONSTANT_POOL2 "II)I", FN_PTR(bootstrapArgumentIndexAt)},
{CC "getUncachedStringInPool", CC "(" HS_CONSTANT_POOL2 "I)" JAVACONSTANT, FN_PTR(getUncachedStringInPool)},

View File

@ -468,9 +468,19 @@ final class CompilerToVM {
*/
int decodeMethodIndexToCPIndex(HotSpotConstantPool constantPool, int rawIndex) {
return decodeMethodIndexToCPIndex(constantPool, constantPool.getConstantPoolPointer(), rawIndex);
}
}
private native int decodeMethodIndexToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int rawIndex);
private native int decodeMethodIndexToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int rawIndex);
/**
* Returns the number of {@code ResolvedIndyEntry}s present within this constant
* pool.
*/
int getNumIndyEntries(HotSpotConstantPool constantPool) {
return getNumIndyEntries(constantPool, constantPool.getConstantPoolPointer());
}
private native int getNumIndyEntries(HotSpotConstantPool constantPool, long constantPoolPointer);
/**
* Resolves the details for invoking the bootstrap method associated with the

View File

@ -25,6 +25,7 @@ package jdk.vm.ci.hotspot;
import java.util.AbstractList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import jdk.vm.ci.common.JVMCIError;
import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
@ -530,19 +531,21 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
}
}
static class BootstrapMethodInvocationImpl implements BootstrapMethodInvocation {
class BootstrapMethodInvocationImpl implements BootstrapMethodInvocation {
private final boolean indy;
private final ResolvedJavaMethod method;
private final String name;
private final JavaConstant type;
private final List<JavaConstant> staticArguments;
private final int cpiOrIndyIndex;
BootstrapMethodInvocationImpl(boolean indy, ResolvedJavaMethod method, String name, JavaConstant type, List<JavaConstant> staticArguments) {
BootstrapMethodInvocationImpl(boolean indy, ResolvedJavaMethod method, String name, JavaConstant type, List<JavaConstant> staticArguments, int cpiOrIndyIndex) {
this.indy = indy;
this.method = method;
this.name = name;
this.type = type;
this.staticArguments = staticArguments;
this.cpiOrIndyIndex = cpiOrIndyIndex;
}
@Override
@ -570,6 +573,24 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
return staticArguments;
}
@Override
public void resolve() {
if (isInvokeDynamic()) {
loadReferencedType(cpiOrIndyIndex, Bytecodes.INVOKEDYNAMIC);
} else {
lookupConstant(cpiOrIndyIndex, true);
}
}
@Override
public JavaConstant lookup() {
if (isInvokeDynamic()) {
return lookupAppendix(cpiOrIndyIndex, Bytecodes.INVOKEDYNAMIC);
} else {
return (JavaConstant) lookupConstant(cpiOrIndyIndex, false);
}
}
@Override
public String toString() {
String static_args = staticArguments.stream().map(BootstrapMethodInvocationImpl::argumentAsString).collect(Collectors.joining(", ", "[", "]"));
@ -612,12 +633,36 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
int bss_index = bsciArgs[1];
staticArgumentsList = new CachedBSMArgs(this, bss_index, argCount);
}
return new BootstrapMethodInvocationImpl(tag.name.equals("InvokeDynamic"), method, name, type, staticArgumentsList);
boolean isIndy = tag.name.equals("InvokeDynamic");
return new BootstrapMethodInvocationImpl(isIndy, method, name, type, staticArgumentsList, isIndy ? index : cpi);
default:
return null;
}
}
private boolean isDynamicEntry(int cpi) {
JvmConstant tagAt = getTagAt(cpi);
return tagAt != null && tagAt.name.equals("Dynamic");
}
@Override
public List<BootstrapMethodInvocation> lookupBootstrapMethodInvocations(boolean invokeDynamic){
if (invokeDynamic) {
int numIndys = compilerToVM().getNumIndyEntries(this);
if (numIndys == 0) {
return List.of();
}
return IntStream.range(0, numIndys)
.mapToObj(i -> lookupBootstrapMethodInvocation(i, Bytecodes.INVOKEDYNAMIC))
.toList();
} else {
return IntStream.range(1, length())
.filter(this::isDynamicEntry)
.mapToObj(cpi -> lookupBootstrapMethodInvocation(cpi, -1))
.toList();
}
}
/**
* Gets the {@link JavaConstant} for the {@code ConstantValue} attribute of a field.
*/

View File

@ -186,11 +186,28 @@ public interface ConstantPool {
* }
* </pre>
*
* The other types of entries are already resolved an can be used directly.
* The other types of entries are already resolved and can be used directly.
*
* @jvms 5.4.3.6
*/
List<JavaConstant> getStaticArguments();
/**
* Resolves the element corresponding to this bootstrap. If
* {@code isInvokeDynamic()}, then the corresponding invoke dynamic is resolved.
* If {@code !isInvokeDynamic()}, then the dynamic constant pool entry will be
* resolved.
*
* @jvms 5.4.3.6
*/
void resolve();
/**
* If {@code isInvokeDynamic()}, then this method looks up the corresponding
* invoke dynamic's appendix. If {@code !isInvokeDynamic()}, then this will
* return the constant pool entry's value.
*/
JavaConstant lookup();
}
/**
@ -204,13 +221,27 @@ public interface ConstantPool {
* @param opcode must be {@code Bytecodes.INVOKEDYNAMIC}, or -1 if
* {@code index} was not decoded from a bytecode stream
* @return the bootstrap method invocation details or {@code null} if the entry specified by {@code index}
* is not a {@code CONSTANT_Dynamic_info} or @{code CONSTANT_InvokeDynamic_info}
* is not a {@code CONSTANT_Dynamic_info} or {@code CONSTANT_InvokeDynamic_info}
* @jvms 4.7.23 The {@code BootstrapMethods} Attribute
*/
default BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int opcode) {
throw new UnsupportedOperationException();
}
/**
* Returns either the BootstrapMethodInvocation instances for all invokedynamic
* bytecodes which reference this constant pool, or all
* {@code CONSTANT_Dynamic_info} BootstrapMethodInvocations within this constant
* pool. The returned List is unmodifiable; calls to any mutator method will
* always cause {@code UnsupportedOperationException} to be thrown.
*
* @param invokeDynamic when true, return all invokedynamic
* BootstrapMethodInvocations; otherwise, return all
* {@code CONSTANT_Dynamic_info}
* BootstrapMethodInvocations.
*/
List<BootstrapMethodInvocation> lookupBootstrapMethodInvocations(boolean invokeDynamic);
/**
* Looks up a reference to a type. If {@code opcode} is non-negative, then resolution checks
* specific to the bytecode it denotes are performed if the type is already resolved. Should any

View File

@ -49,7 +49,7 @@ import java.lang.invoke.StringConcatFactory;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.Map;
import org.testng.Assert;
import org.testng.annotations.Test;
@ -376,9 +376,10 @@ public class TestDynamicConstant implements Opcodes {
ResolvedJavaMethod concat = metaAccess.lookupJavaMethod(m);
ConstantPool cp = concat.getConstantPool();
Set<String> expectedBSMs = Set.of(
"jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM",
"java.lang.invoke.StringConcatFactory.makeConcatWithConstants"
// Contains a map of (bootstrap method names, resolvable) values.
Map<String, Boolean> expectedIndyBSMs = Map.of(
"jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM", false,
"java.lang.invoke.StringConcatFactory.makeConcatWithConstants", true
);
for (int cpi = 1; cpi < cp.length(); cpi++) {
@ -389,9 +390,10 @@ public class TestDynamicConstant implements Opcodes {
String bsm = bsmi.getMethod().format("%H.%n");
if (tag.equals("InvokeDynamic")) {
Assert.assertTrue(bsmi.isInvokeDynamic());
Assert.assertTrue(expectedBSMs.contains(bsm), expectedBSMs.toString());
Assert.assertTrue(expectedIndyBSMs.containsKey(bsm), expectedIndyBSMs.toString());
} else {
Assert.assertFalse(bsmi.isInvokeDynamic());
Assert.assertNull(bsmi.lookup());
checkBsmName(condyType, bsm);
List<JavaConstant> staticArguments = bsmi.getStaticArguments();
for (int i = 0; i < staticArguments.size(); ++i) {
@ -423,6 +425,41 @@ public class TestDynamicConstant implements Opcodes {
}
testLoadReferencedType(concat, cp);
testLookupBootstrapMethodInvocations(condyType, cp, expectedIndyBSMs);
}
private static void testLookupBootstrapMethodInvocations(CondyType condyType, ConstantPool cp, Map<String, Boolean> expectedIndyBSMs) {
List<BootstrapMethodInvocation> indyBSMs = cp.lookupBootstrapMethodInvocations(true);
Assert.assertEquals(indyBSMs.size(), 2);
for (var bsmi : indyBSMs) {
String bsm = bsmi.getMethod().format("%H.%n");
Assert.assertTrue(expectedIndyBSMs.containsKey(bsm), expectedIndyBSMs.toString());
Assert.assertTrue(bsmi.isInvokeDynamic());
if (expectedIndyBSMs.get(bsm)) {
bsmi.resolve();
Assert.assertNotNull(bsmi.lookup());
} else {
try {
bsmi.resolve();
} catch (BootstrapMethodError bme) {
// expected error
}
Assert.assertNull(bsmi.lookup());
}
}
List<BootstrapMethodInvocation> condyBSMs = cp.lookupBootstrapMethodInvocations(false);
int expectedNumCondys = switch(condyType) {
case CALL_DIRECT_BSM, CALL_INDIRECT_BSM -> 1;
case CALL_DIRECT_WITH_ARGS_BSM, CALL_INDIRECT_WITH_ARGS_BSM -> 2;
};
Assert.assertEquals(condyBSMs.size(), expectedNumCondys);
for (var bsmi : condyBSMs) {
Assert.assertTrue(!bsmi.isInvokeDynamic());
bsmi.resolve();
Assert.assertNotNull(bsmi.lookup());
}
}
private static void checkBsmName(CondyType condyType, String bsm) {