diff --git a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java index 58841e55afb..4c171eca2ce 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamClass.java +++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java @@ -1034,12 +1034,12 @@ public final class ObjectStreamClass implements Serializable { new AccessControlContext(domains)); } catch (UndeclaredThrowableException x) { Throwable cause = x.getCause(); - if (cause instanceof InstantiationException) - throw (InstantiationException) cause; - if (cause instanceof InvocationTargetException) - throw (InvocationTargetException) cause; - if (cause instanceof IllegalAccessException) - throw (IllegalAccessException) cause; + if (cause instanceof InstantiationException ie) + throw ie; + if (cause instanceof InvocationTargetException ite) + throw ite; + if (cause instanceof IllegalAccessException iae) + throw iae; // not supposed to happen throw x; } @@ -1047,6 +1047,12 @@ public final class ObjectStreamClass implements Serializable { } catch (IllegalAccessException ex) { // should not occur, as access checks have been suppressed throw new InternalError(ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof Error err) + throw err; + else + throw ex; } catch (InstantiationError err) { var ex = new InstantiationException(); ex.initCause(err); diff --git a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java index 7373b4d846e..850ed2f53f9 100644 --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -128,12 +128,11 @@ sealed class DirectMethodHandle extends MethodHandle { } static DirectMethodHandle make(MemberName member) { if (member.isConstructor()) - return makeAllocator(member); + return makeAllocator(member.getDeclaringClass(), member); return make(member.getDeclaringClass(), member); } - private static DirectMethodHandle makeAllocator(MemberName ctor) { + static DirectMethodHandle makeAllocator(Class instanceClass, MemberName ctor) { assert(ctor.isConstructor() && ctor.getName().equals("")); - Class instanceClass = ctor.getDeclaringClass(); ctor = ctor.asConstructor(); assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor; MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index 48d5ff70020..e79c8463d30 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -1647,6 +1647,12 @@ abstract class MethodHandleImpl { public Class[] exceptionTypes(MethodHandle handle) { return VarHandles.exceptionTypes(handle); } + + @Override + public MethodHandle serializableConstructor(Class decl, Constructor ctorToCall) throws IllegalAccessException { + return IMPL_LOOKUP.serializableConstructor(decl, ctorToCall); + } + }); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index c55f08564ae..49de59d7aa5 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -3525,6 +3525,33 @@ return mh1; return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor); } + /* + * Produces a method handle that is capable of creating instances of the given class + * and instantiated by the given constructor. No security manager check. + * + * This method should only be used by ReflectionFactory::newConstructorForSerialization. + */ + /* package-private */ MethodHandle serializableConstructor(Class decl, Constructor c) throws IllegalAccessException { + MemberName ctor = new MemberName(c); + assert(ctor.isConstructor() && constructorInSuperclass(decl, c)); + checkAccess(REF_newInvokeSpecial, decl, ctor); + assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here + return DirectMethodHandle.makeAllocator(decl, ctor).setVarargs(ctor); + } + + private static boolean constructorInSuperclass(Class decl, Constructor ctor) { + if (decl == ctor.getDeclaringClass()) + return true; + + Class cl = decl; + while ((cl = cl.getSuperclass()) != null) { + if (cl == ctor.getDeclaringClass()) { + return true; + } + } + return false; + } + /** * Produces a method handle giving read access to a reflected field. * The type of the method handle will have a return type of the field's diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java index b359a30d606..563870381fe 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java @@ -162,4 +162,12 @@ public interface JavaLangInvokeAccess { * @return an array of exceptions, or {@code null}. */ Class[] exceptionTypes(MethodHandle handle); + + /** + * Returns a method handle that allocates an instance of the given class + * and then invoke the given constructor of one of its superclasses. + * + * This method should only be used by ReflectionFactory::newConstructorForSerialization. + */ + MethodHandle serializableConstructor(Class decl, Constructor ctorToCall) throws IllegalAccessException; } diff --git a/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java index a1438bcc206..f345d4cdb71 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java +++ b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java @@ -194,7 +194,8 @@ class DirectMethodHandleAccessor extends MethodAccessorImpl { private void checkReceiver(Object o) { // NOTE: will throw NullPointerException, as specified, if o is null if (!declaringClass.isAssignableFrom(o.getClass())) { - throw new IllegalArgumentException("object is not an instance of declaring class"); + throw new IllegalArgumentException("object of type " + o.getClass().getName() + + " is not an instance of " + declaringClass.getName()); } } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java index bdcdaa672d5..3a367272315 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java @@ -101,23 +101,63 @@ final class MethodHandleAccessorFactory { // Ensure class initialized outside the invocation of method handle // so that EIIE is propagated (not wrapped with ITE) ensureClassInitialized(ctor.getDeclaringClass()); - try { - MethodHandle mh = JLIA.unreflectConstructor(ctor); - int paramCount = mh.type().parameterCount(); - MethodHandle target = mh.asFixedArity(); - MethodType mtype = specializedMethodTypeForConstructor(paramCount); - if (paramCount > SPECIALIZED_PARAM_COUNT) { - // spread the parameters only for the non-specialized case - target = target.asSpreader(Object[].class, paramCount); - } - target = target.asType(mtype); + MethodHandle target = makeConstructorHandle(JLIA.unreflectConstructor(ctor)); return DirectConstructorHandleAccessor.constructorAccessor(ctor, target); } catch (IllegalAccessException e) { throw new InternalError(e); } } + /** + * Creates a ConstructorAccessor that is capable of creating instances + * of the given class and instantiated by the given constructor. + * + * @param decl the class to instantiate + * @param ctor the constructor to call + * @return an accessible constructor + */ + static ConstructorAccessorImpl newSerializableConstructorAccessor(Class decl, Constructor ctor) { + if (!constructorInSuperclass(decl, ctor)) { + throw new UnsupportedOperationException(ctor + " not a superclass of " + decl.getName()); + } + + // ExceptionInInitializerError may be thrown during class initialization + // Ensure class initialized outside the invocation of method handle + // so that EIIE is propagated (not wrapped with ITE) + ensureClassInitialized(decl); + try { + MethodHandle target = makeConstructorHandle(JLIA.serializableConstructor(decl, ctor)); + return DirectConstructorHandleAccessor.constructorAccessor(ctor, target); + } catch (IllegalAccessException e) { + throw new InternalError(e); + } + } + + private static boolean constructorInSuperclass(Class decl, Constructor ctor) { + if (decl == ctor.getDeclaringClass()) + return true; + + Class cl = decl; + while ((cl = cl.getSuperclass()) != null) { + if (cl == ctor.getDeclaringClass()) { + return true; + } + } + return false; + } + + private static MethodHandle makeConstructorHandle(MethodHandle ctor) { + int paramCount = ctor.type().parameterCount(); + MethodHandle target = ctor.asFixedArity(); + MethodType mtype = specializedMethodTypeForConstructor(paramCount); + if (paramCount > SPECIALIZED_PARAM_COUNT) { + // spread the parameters only for the non-specialized case + target = target.asSpreader(Object[].class, paramCount); + } + return target.asType(mtype); + } + /** * Creates a FieldAccessor for the given reflected field. * diff --git a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java index 905e70b7406..2222c810524 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java @@ -370,27 +370,27 @@ public class ReflectionFactory { private final Constructor generateConstructor(Class cl, Constructor constructorToCall) { - - ConstructorAccessor acc = new SerializationConstructorAccessorGenerator(). - generateSerializationConstructor(cl, + Constructor ctor = newConstructor(constructorToCall.getDeclaringClass(), constructorToCall.getParameterTypes(), + constructorToCall.getExceptionTypes(), constructorToCall.getModifiers(), - constructorToCall.getDeclaringClass()); - Constructor c = newConstructor(constructorToCall.getDeclaringClass(), - constructorToCall.getParameterTypes(), - constructorToCall.getExceptionTypes(), - constructorToCall.getModifiers(), - langReflectAccess. - getConstructorSlot(constructorToCall), - langReflectAccess. - getConstructorSignature(constructorToCall), - langReflectAccess. - getConstructorAnnotations(constructorToCall), - langReflectAccess. - getConstructorParameterAnnotations(constructorToCall)); - setConstructorAccessor(c, acc); - c.setAccessible(true); - return c; + langReflectAccess.getConstructorSlot(constructorToCall), + langReflectAccess.getConstructorSignature(constructorToCall), + langReflectAccess.getConstructorAnnotations(constructorToCall), + langReflectAccess.getConstructorParameterAnnotations(constructorToCall)); + ConstructorAccessor acc; + if (useOldSerializableConstructor()) { + acc = new SerializationConstructorAccessorGenerator(). + generateSerializationConstructor(cl, + constructorToCall.getParameterTypes(), + constructorToCall.getModifiers(), + constructorToCall.getDeclaringClass()); + } else { + acc = MethodHandleAccessorFactory.newSerializableConstructorAccessor(cl, ctor); + } + setConstructorAccessor(ctor, acc); + ctor.setAccessible(true); + return ctor; } public final MethodHandle readObjectForSerialization(Class cl) { @@ -548,6 +548,10 @@ public class ReflectionFactory { return config().useNativeAccessorOnly; } + static boolean useOldSerializableConstructor() { + return config().useOldSerializableConstructor; + } + private static boolean disableSerialConstructorChecks() { return config().disableSerialConstructorChecks; } @@ -564,6 +568,7 @@ public class ReflectionFactory { private static @Stable Config config; private static final Config DEFAULT_CONFIG = new Config(false, // useNativeAccessorOnly + false, // useOldSerializeableConstructor false); // disableSerialConstructorChecks /** @@ -578,6 +583,7 @@ public class ReflectionFactory { * is to override them. */ private record Config(boolean useNativeAccessorOnly, + boolean useOldSerializableConstructor, boolean disableSerialConstructorChecks) { } @@ -601,10 +607,12 @@ public class ReflectionFactory { Properties props = GetPropertyAction.privilegedGetProperties(); boolean useNativeAccessorOnly = "true".equals(props.getProperty("jdk.reflect.useNativeAccessorOnly")); + boolean useOldSerializableConstructor = + "true".equals(props.getProperty("jdk.reflect.useOldSerializableConstructor")); boolean disableSerialConstructorChecks = "true".equals(props.getProperty("jdk.disableSerialConstructorChecks")); - return new Config(useNativeAccessorOnly, disableSerialConstructorChecks); + return new Config(useNativeAccessorOnly, useOldSerializableConstructor, disableSerialConstructorChecks); } /** diff --git a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java index 410a3420b94..b7e6b52b5dc 100644 --- a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java +++ b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java @@ -397,7 +397,7 @@ public class MethodHandleAccessorsTest { }; private static final Throwable[] mismatched_target_type = new Throwable[] { new IllegalArgumentException("argument type mismatch"), - new IllegalArgumentException("object is not an instance of declaring class"), + new IllegalArgumentException("object of type java.lang.Object is not an instance of MethodHandleAccessorsTest"), }; private static final Throwable[] cannot_get_field = new Throwable[] { new IllegalArgumentException("Can not get") diff --git a/test/jdk/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java b/test/jdk/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java index 853b5f394ff..ebeebc1e19e 100644 --- a/test/jdk/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java +++ b/test/jdk/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java @@ -136,6 +136,11 @@ public class ReflectionFactoryTest { } } + @Test(expectedExceptions = UnsupportedOperationException.class) + static void testConstructorNotSuperClass() throws ReflectiveOperationException { + factory.newConstructorForSerialization(Bar.class, Baz.class.getDeclaredConstructor()); + } + static class Foo { private int foo; public Foo() {