diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 016421b7bf567..6d4a1c288c0f3 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -697,15 +697,24 @@ C2V_VMENTRY_NULL(jobject, getUncachedStringInPool, (JNIEnv* env, jobject, ARGUME return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(obj)); C2V_END -C2V_VMENTRY_NULL(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) +C2V_VMENTRY_NULL(jobject, lookupConstantInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint cp_index, bool resolve)) constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); - oop obj = cp->resolve_possibly_cached_constant_at(index, CHECK_NULL); - constantTag tag = cp->tag_at(index); + oop obj; + if (!resolve) { + bool found_it; + obj = cp->find_cached_constant_at(cp_index, found_it, CHECK_NULL); + if (!found_it) { + return nullptr; + } + } else { + obj = cp->resolve_possibly_cached_constant_at(cp_index, CHECK_NULL); + } + constantTag tag = cp->tag_at(cp_index); if (tag.is_dynamic_constant()) { if (obj == nullptr) { return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } - BasicType bt = Signature::basic_type(cp->uncached_signature_ref_at(index)); + BasicType bt = Signature::basic_type(cp->uncached_signature_ref_at(cp_index)); if (!is_reference_type(bt)) { if (!is_java_primitive(bt)) { return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_ILLEGAL()); @@ -1578,16 +1587,18 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job return nullptr; C2V_END -C2V_VMENTRY_0(int, resolveInvokeDynamicInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) - if (!ConstantPool::is_invokedynamic_index(index)) { - JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an invokedynamic index %d", index)); +C2V_VMENTRY_0(int, decodeIndyIndexToCPIndex, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint encoded_indy_index, jboolean resolve)) + if (!ConstantPool::is_invokedynamic_index(encoded_indy_index)) { + JVMCI_THROW_MSG_0(IllegalStateException, err_msg("not an encoded indy index %d", encoded_indy_index)); } constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); CallInfo callInfo; - LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, Bytecodes::_invokedynamic, CHECK_0); - int indy_index = cp->decode_invokedynamic_index(index); - cp->cache()->set_dynamic_call(callInfo, indy_index); + int indy_index = cp->decode_invokedynamic_index(encoded_indy_index); + if (resolve) { + LinkResolver::resolve_invoke(callInfo, Handle(), cp, encoded_indy_index, Bytecodes::_invokedynamic, CHECK_0); + cp->cache()->set_dynamic_call(callInfo, indy_index); + } return cp->resolved_indy_entry_at(indy_index)->constant_pool_index(); C2V_END @@ -3108,13 +3119,13 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "lookupKlassInPool", CC "(" HS_CONSTANT_POOL2 "I)Ljava/lang/Object;", FN_PTR(lookupKlassInPool)}, {CC "lookupAppendixInPool", CC "(" HS_CONSTANT_POOL2 "I)" 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 "constantPoolRemapInstructionOperandFromCache", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(constantPoolRemapInstructionOperandFromCache)}, {CC "resolveBootstrapMethod", CC "(" HS_CONSTANT_POOL2 "I)[" OBJECT, FN_PTR(resolveBootstrapMethod)}, {CC "getUncachedStringInPool", CC "(" HS_CONSTANT_POOL2 "I)" JAVACONSTANT, FN_PTR(getUncachedStringInPool)}, - {CC "resolvePossiblyCachedConstantInPool", CC "(" HS_CONSTANT_POOL2 "I)" JAVACONSTANT, FN_PTR(resolvePossiblyCachedConstantInPool)}, {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)}, - {CC "resolveInvokeDynamicInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(resolveInvokeDynamicInPool)}, + {CC "decodeIndyIndexToCPIndex", CC "(" HS_CONSTANT_POOL2 "IZ)I", FN_PTR(decodeIndyIndexToCPIndex)}, {CC "resolveInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)V", FN_PTR(resolveInvokeHandleInPool)}, {CC "isResolvedInvokeHandleInPool", CC "(" HS_CONSTANT_POOL2 "I)I", FN_PTR(isResolvedInvokeHandleInPool)}, {CC "resolveMethod", CC "(" HS_KLASS2 HS_METHOD2 HS_KLASS2 ")" HS_METHOD, FN_PTR(resolveMethod)}, 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 37e91531d6288..777f0fe5c31f1 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 @@ -271,19 +271,22 @@ JavaConstant getUncachedStringInPool(HotSpotConstantPool constantPool, int cpi) private native JavaConstant getUncachedStringInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi); /** - * Resolves the entry at index {@code cpi} in {@code constantPool} to an object, looking in the + * Gets the entry at index {@code cpi} in {@code constantPool}, looking in the * constant pool cache first. * * The behavior of this method is undefined if {@code cpi} does not denote one of the following * entry types: {@code JVM_CONSTANT_Dynamic}, {@code JVM_CONSTANT_String}, * {@code JVM_CONSTANT_MethodHandle}, {@code JVM_CONSTANT_MethodHandleInError}, * {@code JVM_CONSTANT_MethodType} and {@code JVM_CONSTANT_MethodTypeInError}. + * + * @param resolve specifies if a resolved entry is expected. If {@code false}, + * {@code null} is returned for an unresolved entry. */ - JavaConstant resolvePossiblyCachedConstantInPool(HotSpotConstantPool constantPool, int cpi) { - return resolvePossiblyCachedConstantInPool(constantPool, constantPool.getConstantPoolPointer(), cpi); + JavaConstant lookupConstantInPool(HotSpotConstantPool constantPool, int cpi, boolean resolve) { + return lookupConstantInPool(constantPool, constantPool.getConstantPoolPointer(), cpi, resolve); } - private native JavaConstant resolvePossiblyCachedConstantInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi); + private native JavaConstant lookupConstantInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi, boolean resolve); /** * Gets the {@code JVM_CONSTANT_NameAndType} index referenced by the {@code rawIndex}. @@ -387,17 +390,18 @@ private native HotSpotResolvedJavaMethodImpl lookupMethodInPool(HotSpotConstantP long callerMethodPointer); /** - * Ensures that the type referenced by the specified {@code JVM_CONSTANT_InvokeDynamic} entry at - * index {@code cpi} in {@code constantPool} is loaded and initialized. + * Converts the encoded indy index operand of an invokedynamic instruction + * to an index directly into {@code constantPool}. * - * @throws IllegalArgumentException if {@code cpi} is not an invokedynamic index - * @return the invokedynamic index + * @param resolve if {@true}, then resolve the entry (which may call a bootstrap method) + * @throws IllegalArgumentException if {@code encoded_indy_index} is not an encoded indy index + * @return {@code JVM_CONSTANT_InvokeDynamic} constant pool entry index for the invokedynamic */ - int resolveInvokeDynamicInPool(HotSpotConstantPool constantPool, int cpi) { - return resolveInvokeDynamicInPool(constantPool, constantPool.getConstantPoolPointer(), cpi); + int decodeIndyIndexToCPIndex(HotSpotConstantPool constantPool, int encoded_indy_index, boolean resolve) { + return decodeIndyIndexToCPIndex(constantPool, constantPool.getConstantPoolPointer(), encoded_indy_index, resolve); } - private native int resolveInvokeDynamicInPool(HotSpotConstantPool constantPool, long constantPoolPointer, int cpi); + private native int decodeIndyIndexToCPIndex(HotSpotConstantPool constantPool, long constantPoolPointer, int encoded_indy_index, boolean resolve); /** * Resolves the details for invoking the bootstrap method associated with the @@ -440,7 +444,7 @@ void resolveInvokeHandleInPool(HotSpotConstantPool constantPool, int cpi) { /** * If {@code cpi} denotes an entry representing a resolved dynamic adapter (see - * {@link #resolveInvokeDynamicInPool} and {@link #resolveInvokeHandleInPool}), return the + * {@link #decodeIndyIndexToCPIndex} and {@link #resolveInvokeHandleInPool}), return the * opcode of the instruction for which the resolution was performed ({@code invokedynamic} or * {@code invokevirtual}), or {@code -1} otherwise. */ 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 5610f7e784e99..390becd4179f3 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 @@ -637,6 +637,11 @@ JavaConstant getStaticFieldConstantValue(int cpi) { @Override public Object lookupConstant(int cpi) { + return lookupConstant(cpi, true); + } + + @Override + public Object lookupConstant(int cpi, boolean resolve) { final JvmConstant tag = getTagAt(cpi); switch (tag.name) { case "Integer": @@ -658,17 +663,18 @@ public Object lookupConstant(int cpi) { * "pseudo strings" (arbitrary live objects) patched into a String entry. Such * entries do not have a symbol in the constant pool slot. */ - return compilerToVM().resolvePossiblyCachedConstantInPool(this, cpi); + return compilerToVM().lookupConstantInPool(this, cpi, true); case "MethodHandle": case "MethodHandleInError": case "MethodType": case "MethodTypeInError": case "Dynamic": case "DynamicInError": - return compilerToVM().resolvePossiblyCachedConstantInPool(this, cpi); + return compilerToVM().lookupConstantInPool(this, cpi, resolve); default: throw new JVMCIError("Unknown constant pool tag %s", tag); } + } @Override @@ -822,7 +828,7 @@ public int rawIndexToConstantPoolIndex(int rawIndex, int opcode) { if (opcode != Bytecodes.INVOKEDYNAMIC) { throw new IllegalArgumentException("expected INVOKEDYNAMIC at " + rawIndex + ", got " + opcode); } - return compilerToVM().resolveInvokeDynamicInPool(this, rawIndex); + return compilerToVM().decodeIndyIndexToCPIndex(this, rawIndex, false); } if (opcode == Bytecodes.INVOKEDYNAMIC) { throw new IllegalArgumentException("unexpected INVOKEDYNAMIC at " + rawIndex); @@ -856,7 +862,7 @@ public void loadReferencedType(int cpi, int opcode, boolean initialize) { if (!isInvokedynamicIndex(cpi)) { throw new IllegalArgumentException("must use invokedynamic index but got " + cpi); } - index = compilerToVM().resolveInvokeDynamicInPool(this, cpi); + index = compilerToVM().decodeIndyIndexToCPIndex(this, cpi, true); break; } case Bytecodes.GETSTATIC: 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 853cd23ec47f6..7c7cfb5687f1b 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 @@ -220,6 +220,20 @@ default BootstrapMethodInvocation lookupBootstrapMethodInvocation(int cpi, int o */ Object lookupConstant(int cpi); + /** + * Looks up a constant at the specified index. + * + * If {@code resolve == false} and the denoted constant is of type + * {@code JVM_CONSTANT_Dynamic}, {@code JVM_CONSTANT_MethodHandle} or + * {@code JVM_CONSTANT_MethodType} and it's not yet resolved then + * {@code null} is returned. + * + * @param cpi the constant pool index + * @return the {@code Constant} or {@code JavaType} instance representing the constant pool + * entry + */ + Object lookupConstant(int cpi, boolean resolve); + /** * Looks up the appendix at the specified index. * diff --git a/test/hotspot/jtreg/compiler/jvmci/common/patches/jdk.internal.vm.ci/jdk/vm/ci/hotspot/CompilerToVMHelper.java b/test/hotspot/jtreg/compiler/jvmci/common/patches/jdk.internal.vm.ci/jdk/vm/ci/hotspot/CompilerToVMHelper.java index 452a92a4909c6..57fc369f4f32e 100644 --- a/test/hotspot/jtreg/compiler/jvmci/common/patches/jdk.internal.vm.ci/jdk/vm/ci/hotspot/CompilerToVMHelper.java +++ b/test/hotspot/jtreg/compiler/jvmci/common/patches/jdk.internal.vm.ci/jdk/vm/ci/hotspot/CompilerToVMHelper.java @@ -101,8 +101,8 @@ public static HotSpotResolvedObjectType lookupTypeHelper(String name, } } - public static Object resolvePossiblyCachedConstantInPool(ConstantPool constantPool, int cpi) { - DirectHotSpotObjectConstantImpl obj = (DirectHotSpotObjectConstantImpl) CTVM.resolvePossiblyCachedConstantInPool((HotSpotConstantPool) constantPool, cpi); + public static Object lookupConstantInPool(ConstantPool constantPool, int cpi, boolean resolve) { + DirectHotSpotObjectConstantImpl obj = (DirectHotSpotObjectConstantImpl) CTVM.lookupConstantInPool((HotSpotConstantPool) constantPool, cpi, resolve); return obj.object; } @@ -132,11 +132,6 @@ public static HotSpotResolvedJavaMethod lookupMethodInPool( return CTVM.lookupMethodInPool((HotSpotConstantPool) constantPool, cpi, opcode, null); } - public static void resolveInvokeDynamicInPool( - ConstantPool constantPool, int cpi) { - CTVM.resolveInvokeDynamicInPool((HotSpotConstantPool) constantPool, cpi); - } - public static void resolveInvokeHandleInPool( ConstantPool constantPool, int cpi) { CTVM.resolveInvokeHandleInPool((HotSpotConstantPool) constantPool, cpi); diff --git a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ResolvePossiblyCachedConstantInPoolTest.java b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/LookupConstantInPoolTest.java similarity index 85% rename from test/hotspot/jtreg/compiler/jvmci/compilerToVM/ResolvePossiblyCachedConstantInPoolTest.java rename to test/hotspot/jtreg/compiler/jvmci/compilerToVM/LookupConstantInPoolTest.java index db12c105a137c..ac36662673045 100644 --- a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ResolvePossiblyCachedConstantInPoolTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/LookupConstantInPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI * -XX:-UseJVMCICompiler - * compiler.jvmci.compilerToVM.ResolvePossiblyCachedConstantInPoolTest + * compiler.jvmci.compilerToVM.LookupConstantInPoolTest */ package compiler.jvmci.compilerToVM; @@ -64,15 +64,15 @@ import static compiler.jvmci.compilerToVM.ConstantPoolTestCase.ConstantTypes.CONSTANT_STRING; /** - * Test for {@code jdk.vm.ci.hotspot.CompilerToVM.resolvePossiblyCachedConstantInPool} method + * Test for {@code jdk.vm.ci.hotspot.CompilerToVM.lookupConstantInPool} method */ -public class ResolvePossiblyCachedConstantInPoolTest { +public class LookupConstantInPoolTest { public static void main(String[] args) throws Exception { Map typeTests = new HashMap<>(); - typeTests.put(CONSTANT_STRING, ResolvePossiblyCachedConstantInPoolTest::validateString); - typeTests.put(CONSTANT_METHODHANDLE, ResolvePossiblyCachedConstantInPoolTest::validateMethodHandle); - typeTests.put(CONSTANT_METHODTYPE, ResolvePossiblyCachedConstantInPoolTest::validateMethodType); + typeTests.put(CONSTANT_STRING, LookupConstantInPoolTest::validateString); + typeTests.put(CONSTANT_METHODHANDLE, LookupConstantInPoolTest::validateMethodHandle); + typeTests.put(CONSTANT_METHODTYPE, LookupConstantInPoolTest::validateMethodType); ConstantPoolTestCase testCase = new ConstantPoolTestCase(typeTests); testCase.test(); // The next "Class.forName" and repeating "testCase.test()" @@ -103,7 +103,7 @@ private static void validateString(ConstantPool constantPoolCTVM, index = cpci; cached = "cached "; } - Object constantInPool = CompilerToVMHelper.resolvePossiblyCachedConstantInPool(constantPoolCTVM, index); + Object constantInPool = CompilerToVMHelper.lookupConstantInPool(constantPoolCTVM, index, true); String stringToVerify = (String) constantInPool; String stringToRefer = entry.name; if (stringToRefer.equals("") && cpci != ConstantPoolTestsHelper.NO_CP_CACHE_PRESENT) { @@ -114,14 +114,14 @@ private static void validateString(ConstantPool constantPoolCTVM, } private static final String NOT_NULL_MSG - = "Object returned by resolvePossiblyCachedConstantInPool method should not be null"; + = "Object returned by lookupConstantInPool method should not be null"; private static void validateMethodHandle(ConstantPool constantPoolCTVM, ConstantTypes cpType, DummyClasses dummyClass, int index) { - Object constantInPool = CompilerToVMHelper.resolvePossiblyCachedConstantInPool(constantPoolCTVM, index); + Object constantInPool = CompilerToVMHelper.lookupConstantInPool(constantPoolCTVM, index, true); String msg = String.format("%s for index %d", NOT_NULL_MSG, index); Asserts.assertNotNull(constantInPool, msg); if (!(constantInPool instanceof MethodHandle)) { @@ -138,7 +138,7 @@ private static void validateMethodType(ConstantPool constantPoolCTVM, ConstantTypes cpType, DummyClasses dummyClass, int index) { - Object constantInPool = CompilerToVMHelper.resolvePossiblyCachedConstantInPool(constantPoolCTVM, index); + Object constantInPool = CompilerToVMHelper.lookupConstantInPool(constantPoolCTVM, index, true); String msg = String.format("%s for index %d", NOT_NULL_MSG, index); Asserts.assertNotNull(constantInPool, msg); Class mtToVerify = constantInPool.getClass(); 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 61e5e79d24265..05f44fcdb7709 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 @@ -38,12 +38,15 @@ import java.io.File; import java.io.IOException; +import java.lang.invoke.CallSite; import java.lang.invoke.ConstantBootstraps; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatFactory; import java.lang.reflect.Method; import java.nio.file.Files; import java.util.List; +import java.util.Set; import org.testng.Assert; import org.testng.annotations.Test; @@ -201,6 +204,14 @@ byte[] generateClassfile() { concat.visitMaxs(0, 0); concat.visitEnd(); + MethodVisitor shouldNotBeCalled = cw.visitMethod(PUBLIC_STATIC, "shouldNotBeCalled", "()V", null, null); + sig = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; + handle = new Handle(H_INVOKESTATIC, testClassInternalName, "shouldNotBeCalledBSM", sig, false); + shouldNotBeCalled.visitInvokeDynamicInsn("do_shouldNotBeCalled", "()V", handle); + shouldNotBeCalled.visitInsn(RETURN); + shouldNotBeCalled.visitMaxs(0, 0); + shouldNotBeCalled.visitEnd(); + cw.visitEnd(); return cw.toByteArray(); } @@ -235,6 +246,37 @@ protected Class findClass(String name) throws ClassNotFoundException { } } + /** + * Asserts that {@link ConstantPool#lookupConstant(int, boolean)} with {@code resolve == false} + * returns null for all resolvable constant entries. + */ + private static void assertNoEagerConstantResolution(Class testClass, ConstantPool cp, Method getTagAt) throws Exception { + for (int cpi = 1; cpi < cp.length(); cpi++) { + String tag = String.valueOf(getTagAt.invoke(cp, cpi)); + switch (tag) { + case "MethodHandle": + case "MethodType": + case "Dynamic": { + Object con = cp.lookupConstant(cpi, false); + Assert.assertNull(con, "Unexpected eager resolution in " + testClass + " at cpi " + cpi + " (tag: " + tag + ")"); + break; + } + } + } + } + + /** + * Ensures {@link ConstantPool#lookupBootstrapMethodInvocation} does not invoke the associated bootstrap method. + */ + private static void assertLookupBMIDoesNotInvokeBM(MetaAccessProvider metaAccess, Class testClass) throws Exception { + ResolvedJavaMethod shouldNotBeCalled = metaAccess.lookupJavaMethod(testClass.getDeclaredMethod("shouldNotBeCalled")); + ConstantPool cp = shouldNotBeCalled.getConstantPool(); + int cpi = getFirstInvokedynamicOperand(shouldNotBeCalled); + BootstrapMethodInvocation bmi = cp.lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC); + Assert.assertEquals(bmi.getName(), "do_shouldNotBeCalled"); + Assert.assertEquals(bmi.getMethod().getName(), "shouldNotBeCalledBSM"); + } + @SuppressWarnings("try") @Test public void test() throws Throwable { @@ -263,6 +305,10 @@ public void test() throws Throwable { Method m = testClass.getDeclaredMethod("run"); ResolvedJavaMethod run = metaAccess.lookupJavaMethod(m); ConstantPool cp = run.getConstantPool(); + + assertNoEagerConstantResolution(testClass, cp, getTagAt); + assertLookupBMIDoesNotInvokeBM(metaAccess, testClass); + Object lastConstant = null; for (int cpi = 1; cpi < cp.length(); cpi++) { String tag = String.valueOf(getTagAt.invoke(cp, cpi)); @@ -301,7 +347,12 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess Method m = testClass.getDeclaredMethod("concat"); ResolvedJavaMethod concat = metaAccess.lookupJavaMethod(m); ConstantPool cp = concat.getConstantPool(); - Object lastConstant = null; + + Set expectedBSMs = Set.of( + "jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM", + "java.lang.invoke.StringConcatFactory.makeConcatWithConstants" + ); + for (int cpi = 1; cpi < cp.length(); cpi++) { String tag = String.valueOf(getTagAt.invoke(cp, cpi)); BootstrapMethodInvocation bsmi = cp.lookupBootstrapMethodInvocation(cpi, -1); @@ -310,7 +361,7 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess String bsm = bsmi.getMethod().format("%H.%n"); if (tag.equals("InvokeDynamic")) { Assert.assertTrue(bsmi.isInvokeDynamic()); - Assert.assertEquals(bsm, "java.lang.invoke.StringConcatFactory.makeConcatWithConstants"); + Assert.assertTrue(expectedBSMs.contains(bsm), expectedBSMs.toString()); } else { Assert.assertFalse(bsmi.isInvokeDynamic()); if (condyType == CondyType.CALL_DIRECT_BSM) { @@ -324,24 +375,41 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess } } - testLoadReferencedType(concat); + testLoadReferencedType(concat, cp); } 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); } + private static int beU1(byte[] data, int bci) { + return data[bci] & 0xff; + } private static final int LDC2_W = 20; - private static void testLoadReferencedType(ResolvedJavaMethod method) { - // Make sure that loadReferencedType for an invokedynamic call site works. + + /** + * Gets the operand of the first invokedynamic in {@code method}. This + * assumes that the bytecode of {@code method} is an INVOKEDYNAMIC instruction, + * possibly preceded by an LDC instruction. + */ + private static int getFirstInvokedynamicOperand(ResolvedJavaMethod method) { byte[] code = method.getCode(); - Assert.assertTrue(code[0] == LDC || code[0] == LDC2_W, "unexpected ldc sequence"); - int bci = code[0] == LDC ? 2 : 3; - Assert.assertTrue((code[bci] & 0xff) == INVOKEDYNAMIC, "unexpected bytecode"); - int cpi = beS4(code, bci + 1); - method.getConstantPool().loadReferencedType(cpi, INVOKEDYNAMIC, false); - BootstrapMethodInvocation bmi = method.getConstantPool().lookupBootstrapMethodInvocation(cpi, INVOKEDYNAMIC); - Assert.assertEquals(bmi.getName(), "do_concat"); + int opcode = beU1(code, 0); + if (opcode == INVOKEDYNAMIC) { + return beS4(code, 1); + } + Assert.assertTrue(opcode == LDC || opcode == LDC2_W, String.valueOf(opcode)); + int bci = opcode == LDC ? 2 : 3; + Assert.assertEquals(beU1(code, bci), INVOKEDYNAMIC); + return beS4(code, bci + 1); + } + + /** + * Ensures that loadReferencedType for an invokedynamic call site does not throw an exception. + */ + private static void testLoadReferencedType(ResolvedJavaMethod method, ConstantPool cp) { + int cpi = getFirstInvokedynamicOperand(method); + cp.loadReferencedType(cpi, INVOKEDYNAMIC, false); } // @formatter:off @@ -382,4 +450,9 @@ private static void testLoadReferencedType(ResolvedJavaMethod method) { public static Object getObject (Object v1, Object v2) { return null; } public static List getList (List v1, List v2) { return List.of(v1, v2); } // @formatter:on + + // A bootstrap method that should never be called + public static CallSite shouldNotBeCalledBSM(MethodHandles.Lookup caller, String name, MethodType type) throws Exception { + throw new RuntimeException("should not be called"); + } }