Skip to content
Closed
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
14 changes: 11 additions & 3 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,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
Expand Down Expand Up @@ -838,6 +840,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);
Expand Down Expand Up @@ -3134,6 +3141,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)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,28 @@ Object[] resolveBootstrapMethod(HotSpotConstantPool constantPool, int cpi) {

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
Copy link
Member

Choose a reason for hiding this comment

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

What happens when index is out of bounds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently, it returns another constant pool index or a random number. I can add a check in the CachedBSMArgs.get method to prevent this.

Copy link
Member

Choose a reason for hiding this comment

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

Yes please. We should opt for exceptions over undefined behavior whenever possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After double checking, there is in fact already an assert for checking if the index is out of bounds. Should I change it to an exception?

Copy link
Member

Choose a reason for hiding this comment

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

Is the assertion in code that can throw exceptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The assertion is here. I don't think we can throw exception there. I could check the size in jvmciCompilerToVM.bootstrapArgumentIndexAt, but it would require a bit of code duplication to obtain it.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. You should just add a note in the javadoc that the result is undefined if index is out of bounds. This is all internal API so I assume we can guarantee the value is correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is only used in CachedBSMArgs.get, which will throw an ArrayIndexOutOfBoundsException before we arrive there.

* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
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;
Expand All @@ -38,6 +40,7 @@
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;
Expand Down Expand Up @@ -526,6 +529,60 @@ private int flags() {
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> {
Copy link
Member

Choose a reason for hiding this comment

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

This class needs documentation.

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);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on my understanding and my testing, this can only be reached for CONSTANT_Dynamic constant pool entries, as other types of entries should resolve even with resolve = false. Am I correct or am I missing some edge cases?

Copy link
Member

Choose a reason for hiding this comment

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

I think that's right based on the spec for ConstantPool.lookupConstant.

}
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;
Expand Down Expand Up @@ -604,8 +661,9 @@ public BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ default JavaMethod lookupMethod(int cpi, int opcode) {

/**
* 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
Expand Down Expand Up @@ -165,6 +165,29 @@ interface BootstrapMethodInvocation {
/**
* 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();
Expand All @@ -182,8 +205,6 @@ interface BootstrapMethodInvocation {
* {@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) {
Expand Down
Loading