Skip to content

Commit

Permalink
Match RI behaviour for unrelfect with OpenJDK MHs in JDK8
Browse files Browse the repository at this point in the history
This patch addresses #14985.
For MethodHandles to private interface methods, the RI erroneously
initializes the MethodHandle to have an InvokeInterface reference
kind, leading to an invoke-time AbstractMethodError throw.

As per the spec, OpenJ9 initializes MethodHandles to private interface
methods to have an InvokeSpecial reference kind which does not throw
the error.

This distinction causes a test failure for a test reported in
#14985. Though not-spec compliant, the test expects
an AbstractMethodError to be thrown to conform to the RI behaviour in
JDK8. For OpenJ9 MHs, MethodHandles.unreflect identifies private
interface methods and installs an AbstractMethodError-thrower MH.
This patch sets up a MethodHandleResolver method that installs the
AbstractMethodError-thrower MH when called from OpenJDK MH unreflect
implementation.

Issues: #14985
Signed-off-by: Nathan Henderson <[email protected]>
  • Loading branch information
ThanHenderson committed Dec 4, 2023
1 parent e89fd87 commit 235525e
Showing 1 changed file with 56 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
import java.lang.invoke.MethodHandleInfo;
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */

/*[IF OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9)]*/
import java.lang.reflect.Modifier;
import java.lang.reflect.Method;
/*[ENDIF] OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9) */

/**
* Static methods for the MethodHandle class.
*/
Expand All @@ -62,6 +67,10 @@ final class MethodHandleResolver {
private static final int BSM_TYPE_ARGUMENT_INDEX = 2;
private static final int BSM_OPTIONAL_ARGUMENTS_START_INDEX = 3;

/*[IF OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9)]*/
private static final int VARARGS = 0x80;
/*[ENDIF] OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9) */

/*
* Return the result of J9_CP_TYPE(J9Class->romClass->cpShapeDescription, index)
*/
Expand Down Expand Up @@ -785,4 +794,51 @@ private static final Object upcallLinkCallerMethod(Class<?> callerClass, MethodT
/*[ENDIF] OPENJDK_METHODHANDLES*/
}
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */

/*[IF OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9)]*/
/**
* Create a MethodHandle to throw an AbstractMethodError if the error conditions
* are met. Used in MethodHandles.unreflect for OpenJDK MethodHandles to match
* the RI behaviour for JDK8 for private interface methods.
*
* @param m the Method whose modifiers determine if an AbstractMethodError
* thrower should be installed
*
* @return an AbstractMethodError-thrower MethodHandle or null
*
* @throws InternalError if the AbstractMethodError constructor cannot be found
*/
@SuppressWarnings("unused")
public static final MethodHandle maybeCreateAbstractMethodErrorThrower(Method m) {
MethodHandle result = null;
Class<?> declaringClass = m.getDeclaringClass();
int methodModifiers = m.getModifiers();

if (declaringClass.isInterface()
&& !Modifier.isStatic(methodModifiers)
&& Modifier.isPrivate(methodModifiers)
) {
MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
type = type.insertParameterTypes(0, declaringClass);

MethodHandle thrower = MethodHandles.throwException(type.returnType(), AbstractMethodError.class);
MethodHandle constructor;
try {
constructor = IMPL_LOOKUP.findConstructor(
AbstractMethodError.class,
MethodType.methodType(void.class));
} catch (IllegalAccessException | NoSuchMethodException e) {
throw new InternalError("Unable to find AbstractMethodError.<init>()"); //$NON-NLS-1$
}
result = MethodHandles.foldArguments(thrower, constructor);
result = MethodHandles.dropArguments(result, 0, type.parameterList());

if ((methodModifiers & VARARGS) != 0) {
Class<?> lastClass = result.type().lastParameterType();
result = result.asVarargsCollector(lastClass);
}
}
return result;
}
/*[ENDIF] OPENJDK_METHODHANDLES & (JAVA_SPEC_VERSION < 9) */
}

0 comments on commit 235525e

Please sign in to comment.