diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 4f6d4aaa099..d45036ced94 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -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)}, diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index 045b1f0fc12..0cb56354498 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -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 diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java index 84fb21d1948..20e3915527d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java @@ -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 staticArguments; + private final int cpiOrIndyIndex; - BootstrapMethodInvocationImpl(boolean indy, ResolvedJavaMethod method, String name, JavaConstant type, List staticArguments) { + BootstrapMethodInvocationImpl(boolean indy, ResolvedJavaMethod method, String name, JavaConstant type, List 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 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. */ diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java index 2273b256f03..0b7fd70762d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ConstantPool.java @@ -186,11 +186,28 @@ public interface ConstantPool { * } * * - * 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 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 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 diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java index b22cb01d81a..b6e4f9cf2ef 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/TestDynamicConstant.java @@ -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 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 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 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 expectedIndyBSMs) { + List 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 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) {