From 60f121a71f9be52df3c40027f29caa6735c4da37 Mon Sep 17 00:00:00 2001 From: Nathan Henderson Date: Mon, 13 Nov 2023 07:14:36 -0800 Subject: [PATCH] Conform to spec for BootstrapMethodError for OJDK MHs for JDK 8 This patch fixes both eclipse-openj9/openj9#14986 and eclipse-openj9/openj9#14991. The implementation of OJDK MHs for JDK 8 did not conform to the spec with respect to wrapping resolution errors relating to bootstrap methods in a BootstrapMethodError. Issues: eclipse-openj9/openj9#14986 eclipse-openj9/openj9#14991 Signed-off-by: Nathan Henderson --- .../lang/invoke/MethodHandleResolver.java | 94 ++++++++++++------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/jcl/src/java.base/share/classes/java/lang/invoke/MethodHandleResolver.java b/jcl/src/java.base/share/classes/java/lang/invoke/MethodHandleResolver.java index af93eff8e1c..930837cd9af 100644 --- a/jcl/src/java.base/share/classes/java/lang/invoke/MethodHandleResolver.java +++ b/jcl/src/java.base/share/classes/java/lang/invoke/MethodHandleResolver.java @@ -236,7 +236,8 @@ private static final Object resolveInvokeDynamic(long j9class, String name, Stri Object internalConstantPool = access.getInternalConstantPoolFromJ9Class(j9class); Class classObject = getClassFromJ9Class(j9class); - MethodType type; + MethodType type = null; + Object[] result = new Object[2]; /*[IF JAVA_SPEC_VERSION >= 11]*/ type = MethodTypeHelper.vmResolveFromMethodDescriptorString(methodDescriptor, access.getClassloader(classObject), null); final MethodHandles.Lookup lookup = IMPL_LOOKUP.in(classObject); @@ -244,47 +245,68 @@ private static final Object resolveInvokeDynamic(long j9class, String name, Stri /*[ELSE] JAVA_SPEC_VERSION >= 11*/ try { type = MethodTypeHelper.vmResolveFromMethodDescriptorString(methodDescriptor, access.getClassloader(classObject), null); - } catch(Throwable e) { - throw new BootstrapMethodError(e); - } /*[ENDIF] JAVA_SPEC_VERSION >= 11 */ - int bsmIndex = UNSAFE.getShort(bsmData); - int bsmArgCount = UNSAFE.getShort(bsmData + BSM_ARGUMENT_COUNT_OFFSET); - long bsmArgs = bsmData + BSM_ARGUMENTS_OFFSET; - MethodHandle bsm = getCPMethodHandleAt(internalConstantPool, bsmIndex); - if (null == bsm) { - /*[MSG "K05cd", "unable to resolve 'bootstrap_method_ref' in '{0}' at index {1}"]*/ - throw new NullPointerException(Msg.getString("K05cd", classObject.toString(), bsmIndex)); //$NON-NLS-1$ - } + int bsmIndex = UNSAFE.getShort(bsmData); + int bsmArgCount = UNSAFE.getShort(bsmData + BSM_ARGUMENT_COUNT_OFFSET); + long bsmArgs = bsmData + BSM_ARGUMENTS_OFFSET; + MethodHandle bsm = getCPMethodHandleAt(internalConstantPool, bsmIndex); + if (null == bsm) { + /*[MSG "K05cd", "unable to resolve 'bootstrap_method_ref' in '{0}' at index {1}"]*/ + throw new NullPointerException(Msg.getString("K05cd", classObject.toString(), bsmIndex)); //$NON-NLS-1$ + } - Object[] staticArgs = new Object[bsmArgCount]; - /* Static optional arguments */ - int bsmTypeArgCount = bsm.type().parameterCount(); - for (int i = 0; i < bsmArgCount; i++) { - staticArgs[i] = getAdditionalBsmArg(access, internalConstantPool, classObject, bsm, bsmArgs, bsmTypeArgCount, i); - } + Object[] staticArgs = new Object[bsmArgCount]; + /* Static optional arguments */ + int bsmTypeArgCount = bsm.type().parameterCount(); + for (int i = 0; i < bsmArgCount; i++) { + staticArgs[i] = getAdditionalBsmArg(access, internalConstantPool, classObject, bsm, bsmArgs, bsmTypeArgCount, i); + } - Object[] appendixResult = new Object[1]; - Object[] result = new Object[2]; + Object[] appendixResult = new Object[1]; + + /* result[0] stores a MemberName object, which specifies the caller method (generated bytecodes), or a Throwable. + * + * This leads to a type check in the interpreter for the object stored in result[0]. + * + * TODO: Investigate if the Throwable can be wrapped in a MemberName. This will help prevent type checks in the + * interpreter since result[0] will always be a MemberName. This will improve performance. + */ + result[0] = MethodHandleNatives.linkCallSite(classObject, + /* The second parameter is not used in Java 8 and Java 18+ (JDK bug: 8272614). */ + /*[IF (JAVA_SPEC_VERSION > 8) & (JAVA_SPEC_VERSION < 18)]*/ + 0, + /*[ENDIF] (JAVA_SPEC_VERSION > 8) & (JAVA_SPEC_VERSION < 18) */ + bsm, name, type, (Object)staticArgs, appendixResult); - /* result[0] stores a MemberName object, which specifies the caller method (generated bytecodes), or a Throwable. - * - * This leads to a type check in the interpreter for the object stored in result[0]. - * - * TODO: Investigate if the Throwable can be wrapped in a MemberName. This will help prevent type checks in the - * interpreter since result[0] will always be a MemberName. This will improve performance. - */ - result[0] = MethodHandleNatives.linkCallSite(classObject, - /* The second parameter is not used in Java 8 and Java 18+ (JDK bug: 8272614). */ - /*[IF (JAVA_SPEC_VERSION > 8) & (JAVA_SPEC_VERSION < 18)]*/ - 0, - /*[ENDIF] (JAVA_SPEC_VERSION > 8) & (JAVA_SPEC_VERSION < 18) */ - bsm, name, type, (Object)staticArgs, appendixResult); - - /* result[1] stores a MethodHandle object, which is used as the last argument to the caller method. */ - result[1] = appendixResult[0]; + /* result[1] stores a MethodHandle object, which is used as the last argument to the caller method. */ + result[1] = appendixResult[0]; +/*[IF JAVA_SPEC_VERSION < 11]*/ + } catch (Throwable e) { + if (type == null) { + throw new BootstrapMethodError(e); + } + /* linkCallSite may correctly throw a BootstrapMethodError. */ + if (e instanceof BootstrapMethodError) { + throw e; + } + + /* Any other throwables are wrapped in an invoke-time BootstrapMethodError exception throw. */ + try { + MethodHandle thrower = MethodHandles.throwException(type.returnType(), BootstrapMethodError.class); + MethodHandle constructor = IMPL_LOOKUP.findConstructor(BootstrapMethodError.class, MethodType.methodType(void.class, Throwable.class)); + MethodHandle resultHandle = MethodHandles.foldArguments(thrower, constructor.bindTo(e)); + MemberName memberName = resultHandle.internalForm().compileToBytecode(); + result[0] = memberName; + result[1] = resultHandle; + } catch (IllegalAccessException iae) { + throw new Error(iae); + } catch (NoSuchMethodException nsme) { + throw new Error(nsme); + } + } +/*[ENDIF] JAVA_SPEC_VERSION < 11 */ return (Object)result; /*[ELSE] OPENJDK_METHODHANDLES*/ MethodHandle result = null;