Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,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);
Expand Down Expand Up @@ -3295,6 +3303,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)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,19 @@ int decodeFieldIndexToCPIndex(HotSpotConstantPool constantPool, int rawIndex) {
*/
int decodeMethodIndexToCPIndex(HotSpotConstantPool constantPool, int rawIndex) {
return decodeMethodIndexToCPIndex(constantPool, constantPool.getConstantPoolPointer(), 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 decodeMethodIndexToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int rawIndex);
private native int getNumIndyEntries(HotSpotConstantPool constantPool, long constantPoolPointer);

/**
* Resolves the details for invoking the bootstrap method associated with the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -530,19 +531,21 @@ public int size() {
}
}

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
Expand Down Expand Up @@ -570,6 +573,24 @@ public List<JavaConstant> getStaticArguments() {
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(", ", "[", "]"));
Expand Down Expand Up @@ -612,12 +633,36 @@ public BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you forgot to add the definition of isDynamicEntry that I suggested:

    private boolean isDynamicEntry(int cpi) {
        JvmConstant tagAt = getTagAt(cpi);
        return tagAt != null && tagAt.name.equals("Dynamic");
    }

Copy link
Author

@teshull teshull May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I applied the suggested change via github, and am just validating it works now (which of course it doesn't). I'll fix it

.mapToObj(cpi -> lookupBootstrapMethodInvocation(cpi, -1))
.toList();
}
}

/**
* Gets the {@link JavaConstant} for the {@code ConstantValue} attribute of a field.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,28 @@ interface BootstrapMethodInvocation {
* }
* </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();
}

/**
Expand All @@ -204,13 +221,27 @@ interface BootstrapMethodInvocation {
* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
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;
Expand Down Expand Up @@ -376,9 +376,10 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
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++) {
Expand All @@ -389,9 +390,10 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
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) {
Expand Down Expand Up @@ -423,6 +425,41 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
}

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) {
Expand Down