8315771: [JVMCI] Resolution of bootstrap methods with int[] static arguments
Reviewed-by: dnsimon, psandoz
This commit is contained in:
parent
83b01cf3c2
commit
015f6f5d94
@ -771,9 +771,11 @@ C2V_VMENTRY_NULL(jobjectArray, resolveBootstrapMethod, (JNIEnv* env, jobject, AR
|
||||
}
|
||||
// Get the indy entry based on CP index
|
||||
int indy_index = -1;
|
||||
for (int i = 0; i < cp->resolved_indy_entries_length(); i++) {
|
||||
if (cp->resolved_indy_entry_at(i)->constant_pool_index() == index) {
|
||||
indy_index = i;
|
||||
if (is_indy) {
|
||||
for (int i = 0; i < cp->resolved_indy_entries_length(); i++) {
|
||||
if (cp->resolved_indy_entry_at(i)->constant_pool_index() == index) {
|
||||
indy_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Resolve the bootstrap specifier, its name, type, and static arguments
|
||||
@ -839,6 +841,11 @@ C2V_VMENTRY_NULL(jobjectArray, resolveBootstrapMethod, (JNIEnv* env, jobject, AR
|
||||
return JVMCIENV->get_jobjectArray(bsmi);
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jint, bootstrapArgumentIndexAt, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint cpi, jint index))
|
||||
constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
|
||||
return cp->bootstrap_argument_index_at(cpi, index);
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index, jint opcode))
|
||||
constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp));
|
||||
return cp->name_and_type_ref_index_at(index, (Bytecodes::Code)opcode);
|
||||
@ -3177,6 +3184,7 @@ JNINativeMethod CompilerToVM::methods[] = {
|
||||
{CC "lookupConstantInPool", CC "(" HS_CONSTANT_POOL2 "IZ)" JAVACONSTANT, FN_PTR(lookupConstantInPool)},
|
||||
{CC "constantPoolRemapInstructionOperandFromCache", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(constantPoolRemapInstructionOperandFromCache)},
|
||||
{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)},
|
||||
{CC "resolveTypeInPool", CC "(" HS_CONSTANT_POOL2 "I)" HS_KLASS, FN_PTR(resolveTypeInPool)},
|
||||
{CC "resolveFieldInPool", CC "(" HS_CONSTANT_POOL2 "I" HS_METHOD2 "B[I)" HS_KLASS, FN_PTR(resolveFieldInPool)},
|
||||
|
@ -483,6 +483,28 @@ final class CompilerToVM {
|
||||
|
||||
private native Object[] resolveBootstrapMethod(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi);
|
||||
|
||||
/**
|
||||
* Gets the constant pool index of a static argument of a {@code CONSTANT_Dynamic_info} or
|
||||
* @{code CONSTANT_InvokeDynamic_info} entry. Used when the list of static arguments in the
|
||||
* {@link BootstrapMethodInvocation} is a {@code List<PrimitiveConstant>} of the form
|
||||
* {{@code arg_count}, {@code pool_index}}, meaning the arguments are not already resolved and that
|
||||
* the JDK has to lookup the arguments when they are needed. The {@code cpi} corresponds to
|
||||
* {@code pool_index} and the {@code index} has to be smaller than {@code arg_count}.
|
||||
*
|
||||
* The behavior of this method is undefined if {@code cpi} does not denote an entry representing
|
||||
* a {@code CONSTANT_Dynamic_info} or a @{code CONSTANT_InvokeDynamic_info}, or if the index
|
||||
* is out of bounds.
|
||||
*
|
||||
* @param cpi the index of a {@code CONSTANT_Dynamic_info} or @{code CONSTANT_InvokeDynamic_info} entry
|
||||
* @param index the index of the static argument in the list of static arguments
|
||||
* @return the constant pool index associated with the static argument
|
||||
*/
|
||||
int bootstrapArgumentIndexAt(HotSpotConstantPool constantPool, int cpi, int index) {
|
||||
return bootstrapArgumentIndexAt(constantPool, constantPool.getConstantPoolPointer(), cpi, index);
|
||||
}
|
||||
|
||||
private native int bootstrapArgumentIndexAt(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi, int index);
|
||||
|
||||
/**
|
||||
* If {@code cpi} denotes an entry representing a signature polymorphic method ({@jvms 2.9}),
|
||||
* this method ensures that the type referenced by the entry is loaded and initialized. It
|
||||
|
@ -27,6 +27,8 @@ import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
|
||||
import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
|
||||
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@ -38,6 +40,7 @@ import jdk.vm.ci.meta.JavaConstant;
|
||||
import jdk.vm.ci.meta.JavaField;
|
||||
import jdk.vm.ci.meta.JavaMethod;
|
||||
import jdk.vm.ci.meta.JavaType;
|
||||
import jdk.vm.ci.meta.PrimitiveConstant;
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
import jdk.vm.ci.meta.ResolvedJavaType;
|
||||
import jdk.vm.ci.meta.Signature;
|
||||
@ -504,6 +507,60 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
|
||||
return UNSAFE.getInt(getConstantPoolPointer() + config().constantPoolFlagsOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a list of static arguments from a {@link BootstrapMethodInvocation} of the form
|
||||
* {{@code arg_count}, {@code pool_index}}, meaning the arguments are not already resolved
|
||||
* and that the JDK has to lookup the arguments when they are needed. The {@code bssIndex}
|
||||
* corresponds to {@code pool_index} and the {@code size} corresponds to {@code arg_count}.
|
||||
*/
|
||||
static class CachedBSMArgs extends AbstractList<JavaConstant> {
|
||||
private final JavaConstant[] cache;
|
||||
private final HotSpotConstantPool cp;
|
||||
private final int bssIndex;
|
||||
|
||||
CachedBSMArgs(HotSpotConstantPool cp, int bssIndex, int size) {
|
||||
this.cp = cp;
|
||||
this.bssIndex = bssIndex;
|
||||
this.cache = new JavaConstant[size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily resolves and caches the argument at the given index and returns it. The method
|
||||
* {@link CompilerToVM#bootstrapArgumentIndexAt} is used to obtain the constant pool
|
||||
* index of the entry and the method {@link ConstantPool#lookupConstant} is used to
|
||||
* resolve it. If the resolution failed, the index is returned as a
|
||||
* {@link PrimitiveConstant}.
|
||||
*
|
||||
* @param index index of the element to return
|
||||
* @return A {@link JavaConstant} corresponding to the static argument requested. A return
|
||||
* value of type {@link PrimitiveConstant} represents an unresolved constant pool entry
|
||||
*/
|
||||
@Override
|
||||
public JavaConstant get(int index) {
|
||||
JavaConstant res = cache[index];
|
||||
if (res == null) {
|
||||
int argCpi = compilerToVM().bootstrapArgumentIndexAt(cp, bssIndex, index);
|
||||
Object object = cp.lookupConstant(argCpi, false);
|
||||
if (object instanceof PrimitiveConstant primitiveConstant) {
|
||||
res = runtime().getReflection().boxPrimitive(primitiveConstant);
|
||||
} else if (object instanceof JavaConstant javaConstant) {
|
||||
res = javaConstant;
|
||||
} else if (object instanceof JavaType type) {
|
||||
res = runtime().getReflection().forObject(type);
|
||||
} else {
|
||||
res = JavaConstant.forInt(argCpi);
|
||||
}
|
||||
cache[index] = res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return cache.length;
|
||||
}
|
||||
}
|
||||
|
||||
static class BootstrapMethodInvocationImpl implements BootstrapMethodInvocation {
|
||||
private final boolean indy;
|
||||
private final ResolvedJavaMethod method;
|
||||
@ -582,8 +639,9 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
|
||||
staticArgumentsList = List.of((JavaConstant[]) staticArguments);
|
||||
} else {
|
||||
int[] bsciArgs = (int[]) staticArguments;
|
||||
String message = String.format("Resolving bootstrap static arguments for %s using BootstrapCallInfo %s not supported", method.format("%H.%n(%p)"), Arrays.toString(bsciArgs));
|
||||
throw new IllegalArgumentException(message);
|
||||
int argCount = bsciArgs[0];
|
||||
int bss_index = bsciArgs[1];
|
||||
staticArgumentsList = new CachedBSMArgs(this, bss_index, argCount);
|
||||
}
|
||||
return new BootstrapMethodInvocationImpl(tag.name.equals("InvokeDynamic"), method, name, type, staticArgumentsList);
|
||||
default:
|
||||
|
@ -131,7 +131,7 @@ public interface ConstantPool {
|
||||
|
||||
/**
|
||||
* The details for invoking a bootstrap method associated with a {@code CONSTANT_Dynamic_info}
|
||||
* or {@code CONSTANT_InvokeDynamic_info} pool entry .
|
||||
* or {@code CONSTANT_InvokeDynamic_info} pool entry.
|
||||
*
|
||||
* @jvms 4.4.10 The {@code CONSTANT_Dynamic_info} and {@code CONSTANT_InvokeDynamic_info}
|
||||
* Structures
|
||||
@ -165,6 +165,29 @@ public interface ConstantPool {
|
||||
/**
|
||||
* Gets the static arguments with which the bootstrap method will be invoked.
|
||||
*
|
||||
* The {@linkplain JavaConstant#getJavaKind kind} of each argument will be
|
||||
* {@link JavaKind#Object} or {@link JavaKind#Int}. The latter represents an
|
||||
* unresolved {@code CONSTANT_Dynamic_info} entry. To resolve this entry, the
|
||||
* corresponding bootstrap method has to be called first:
|
||||
*
|
||||
* <pre>
|
||||
* List<JavaConstant> args = bmi.getStaticArguments();
|
||||
* List<JavaConstant> resolvedArgs = new ArrayList<>(args.size());
|
||||
* for (JavaConstant c : args) {
|
||||
* JavaConstant r = c;
|
||||
* if (c.getJavaKind() == JavaKind.Int) {
|
||||
* // If needed, access corresponding BootstrapMethodInvocation using
|
||||
* // cp.lookupBootstrapMethodInvocation(pc.asInt(), -1)
|
||||
* r = cp.lookupConstant(c.asInt(), true);
|
||||
* } else {
|
||||
* assert c.getJavaKind() == JavaKind.Object;
|
||||
* }
|
||||
* resolvedArgs.append(r);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The other types of entries are already resolved an can be used directly.
|
||||
*
|
||||
* @jvms 5.4.3.6
|
||||
*/
|
||||
List<JavaConstant> getStaticArguments();
|
||||
@ -182,8 +205,6 @@ public interface ConstantPool {
|
||||
* {@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}
|
||||
* @throws IllegalArgumentException if the bootstrap method invocation makes use of
|
||||
* {@code java.lang.invoke.BootstrapCallInfo}
|
||||
* @jvms 4.7.23 The {@code BootstrapMethods} Attribute
|
||||
*/
|
||||
default BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int opcode) {
|
||||
|
@ -32,6 +32,9 @@
|
||||
* @run testng/othervm
|
||||
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
|
||||
* jdk.vm.ci.hotspot.test.TestDynamicConstant
|
||||
* @run testng/othervm
|
||||
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler -XX:UseBootstrapCallInfo=3
|
||||
* jdk.vm.ci.hotspot.test.TestDynamicConstant
|
||||
*/
|
||||
|
||||
package jdk.vm.ci.hotspot.test;
|
||||
@ -61,6 +64,7 @@ import jdk.vm.ci.hotspot.HotSpotObjectConstant;
|
||||
import jdk.vm.ci.hotspot.HotSpotConstantPool;
|
||||
import jdk.vm.ci.meta.ConstantPool;
|
||||
import jdk.vm.ci.meta.ConstantPool.BootstrapMethodInvocation;
|
||||
import jdk.vm.ci.meta.JavaConstant;
|
||||
import jdk.vm.ci.meta.MetaAccessProvider;
|
||||
import jdk.vm.ci.meta.PrimitiveConstant;
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
@ -86,6 +90,12 @@ public class TestDynamicConstant implements Opcodes {
|
||||
*/
|
||||
CALL_DIRECT_BSM,
|
||||
|
||||
/**
|
||||
* Condy whose bootstrap method is one of the {@code TestDynamicConstant.get<type>BSM(<type> constant, int i)}
|
||||
* methods with one condy arg and one int arg.
|
||||
*/
|
||||
CALL_DIRECT_WITH_ARGS_BSM,
|
||||
|
||||
/**
|
||||
* Condy whose bootstrap method is {@link ConstantBootstraps#invoke} that invokes one of the
|
||||
* {@code TestDynamicConstant.get<type>()} methods.
|
||||
@ -164,6 +174,24 @@ public class TestDynamicConstant implements Opcodes {
|
||||
run.visitInsn(type.getOpcode(IRETURN));
|
||||
run.visitMaxs(0, 0);
|
||||
run.visitEnd();
|
||||
} else if (condyType == CondyType.CALL_DIRECT_WITH_ARGS_BSM) {
|
||||
// Example: int TestDynamicConstant.getIntBSM(MethodHandles.Lookup l, String name,
|
||||
// Class<?> type, int constant, int i)
|
||||
String sig1 = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)" + desc;
|
||||
String sig2 = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;" + desc + "I)" + desc;
|
||||
|
||||
Handle handle1 = new Handle(H_INVOKESTATIC, testClassInternalName, getter + "BSM", sig1, false);
|
||||
Handle handle2 = new Handle(H_INVOKESTATIC, testClassInternalName, getter + "BSM", sig2, false);
|
||||
|
||||
ConstantDynamic condy1 = new ConstantDynamic("const1", desc, handle1);
|
||||
ConstantDynamic condy2 = new ConstantDynamic("const2", desc, handle2, condy1, Integer.MAX_VALUE);
|
||||
|
||||
condy = condy2;
|
||||
MethodVisitor run = cw.visitMethod(PUBLIC_STATIC, "run", "()" + desc, null, null);
|
||||
run.visitLdcInsn(condy);
|
||||
run.visitInsn(type.getOpcode(IRETURN));
|
||||
run.visitMaxs(0, 0);
|
||||
run.visitEnd();
|
||||
} else if (condyType == CondyType.CALL_INDIRECT_BSM) {
|
||||
// Example: int TestDynamicConstant.getInt()
|
||||
Handle handle = new Handle(H_INVOKESTATIC, testClassInternalName, getter, "()" + desc, false);
|
||||
@ -309,6 +337,12 @@ public class TestDynamicConstant implements Opcodes {
|
||||
assertNoEagerConstantResolution(testClass, cp, getTagAt);
|
||||
assertLookupBMIDoesNotInvokeBM(metaAccess, testClass);
|
||||
|
||||
if (type != Object.class) {
|
||||
testLookupBootstrapMethodInvocation(condyType, metaAccess, testClass, getTagAt);
|
||||
} else {
|
||||
// StringConcatFactoryStringConcatFactory cannot accept null constants
|
||||
}
|
||||
|
||||
Object lastConstant = null;
|
||||
for (int cpi = 1; cpi < cp.length(); cpi++) {
|
||||
String tag = String.valueOf(getTagAt.invoke(cp, cpi));
|
||||
@ -330,12 +364,6 @@ public class TestDynamicConstant implements Opcodes {
|
||||
actual = ((HotSpotObjectConstant) lastConstant).asObject(type);
|
||||
}
|
||||
Assert.assertEquals(actual, expect, m + ":");
|
||||
|
||||
if (type != Object.class) {
|
||||
testLookupBootstrapMethodInvocation(condyType, metaAccess, testClass, getTagAt);
|
||||
} else {
|
||||
// StringConcatFactoryStringConcatFactory cannot accept null constants
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,10 +392,29 @@ public class TestDynamicConstant implements Opcodes {
|
||||
Assert.assertTrue(expectedBSMs.contains(bsm), expectedBSMs.toString());
|
||||
} else {
|
||||
Assert.assertFalse(bsmi.isInvokeDynamic());
|
||||
if (condyType == CondyType.CALL_DIRECT_BSM) {
|
||||
Assert.assertTrue(bsm.startsWith("jdk.vm.ci.hotspot.test.TestDynamicConstant.get") && bsm.endsWith("BSM"), bsm);
|
||||
} else {
|
||||
Assert.assertEquals(bsm, "java.lang.invoke.ConstantBootstraps.invoke");
|
||||
checkBsmName(condyType, bsm);
|
||||
List<JavaConstant> staticArguments = bsmi.getStaticArguments();
|
||||
for (int i = 0; i < staticArguments.size(); ++i) {
|
||||
JavaConstant constant = staticArguments.get(i);
|
||||
if (constant instanceof PrimitiveConstant) {
|
||||
String innerTag = String.valueOf(getTagAt.invoke(cp, constant.asInt()));
|
||||
if (condyType == CondyType.CALL_DIRECT_WITH_ARGS_BSM) {
|
||||
Assert.assertEquals(i, 0);
|
||||
Assert.assertEquals(innerTag, "Dynamic");
|
||||
}
|
||||
if (innerTag.equals("Dynamic")) {
|
||||
BootstrapMethodInvocation innerBsmi = cp.lookupBootstrapMethodInvocation(constant.asInt(), -1);
|
||||
String innerBsm = innerBsmi.getMethod().format("%H.%n");
|
||||
checkBsmName(condyType, innerBsm);
|
||||
} else {
|
||||
Assert.assertEquals(innerTag, "MethodHandle");
|
||||
}
|
||||
} else {
|
||||
if (condyType == CondyType.CALL_DIRECT_WITH_ARGS_BSM) {
|
||||
Assert.assertEquals(i, 1);
|
||||
}
|
||||
Assert.assertTrue(staticArguments.get(i) instanceof HotSpotObjectConstant);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -378,6 +425,14 @@ public class TestDynamicConstant implements Opcodes {
|
||||
testLoadReferencedType(concat, cp);
|
||||
}
|
||||
|
||||
private static void checkBsmName(CondyType condyType, String bsm) {
|
||||
if (condyType == CondyType.CALL_DIRECT_BSM || condyType == CondyType.CALL_DIRECT_WITH_ARGS_BSM) {
|
||||
Assert.assertTrue(bsm.startsWith("jdk.vm.ci.hotspot.test.TestDynamicConstant.get") && bsm.endsWith("BSM"), bsm);
|
||||
} else {
|
||||
Assert.assertEquals(bsm, "java.lang.invoke.ConstantBootstraps.invoke");
|
||||
}
|
||||
}
|
||||
|
||||
private static int beS4(byte[] data, int bci) {
|
||||
return (data[bci] << 24) | ((data[bci + 1] & 0xff) << 16) | ((data[bci + 2] & 0xff) << 8) | (data[bci + 3] & 0xff);
|
||||
}
|
||||
@ -425,6 +480,18 @@ public class TestDynamicConstant implements Opcodes {
|
||||
@SuppressWarnings("unused") public static Object getObjectBSM (MethodHandles.Lookup l, String name, Class<?> type) { return null; }
|
||||
@SuppressWarnings("unused") public static List<?> getListBSM (MethodHandles.Lookup l, String name, Class<?> type) { return List.of("element"); }
|
||||
|
||||
@SuppressWarnings("unused") public static boolean getBooleanBSM(MethodHandles.Lookup l, String name, Class<?> type, boolean constant, int i) { return true; }
|
||||
@SuppressWarnings("unused") public static char getCharBSM (MethodHandles.Lookup l, String name, Class<?> type, char constant, int i) { return '*'; }
|
||||
@SuppressWarnings("unused") public static short getShortBSM (MethodHandles.Lookup l, String name, Class<?> type, short constant, int i) { return Short.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static byte getByteBSM (MethodHandles.Lookup l, String name, Class<?> type, byte constant, int i) { return Byte.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static int getIntBSM (MethodHandles.Lookup l, String name, Class<?> type, int constant, int i) { return Integer.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static float getFloatBSM (MethodHandles.Lookup l, String name, Class<?> type, float constant, int i) { return Float.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static long getLongBSM (MethodHandles.Lookup l, String name, Class<?> type, long constant, int i) { return Long.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static double getDoubleBSM (MethodHandles.Lookup l, String name, Class<?> type, double constant, int i) { return Double.MAX_VALUE; }
|
||||
@SuppressWarnings("unused") public static String getStringBSM (MethodHandles.Lookup l, String name, Class<?> type, String constant, int i) { return "a string"; }
|
||||
@SuppressWarnings("unused") public static Object getObjectBSM (MethodHandles.Lookup l, String name, Class<?> type, Object constant, int i) { return null; }
|
||||
@SuppressWarnings("unused") public static List<?> getListBSM (MethodHandles.Lookup l, String name, Class<?> type, List<?> constant, int i) { return List.of("element"); }
|
||||
|
||||
|
||||
public static boolean getBoolean() { return true; }
|
||||
public static char getChar () { return '*'; }
|
||||
|
Loading…
Reference in New Issue
Block a user