From 9e3a2ea37460af0e3487eeb598c091413b2525fe Mon Sep 17 00:00:00 2001 From: rfield Date: Mon, 4 Nov 2013 10:12:18 -0800 Subject: [PATCH] 7194897: JSR 292: Cannot create more than 16 instances of an anonymous class 8027681: Lambda serialization fails once reflection proxy generation kicks in Reviewed-by: ksrini, briangoetz, jfranck Contributed-by: joel.franck@oracle.com, brian.goetz@oracle.com, robert.field@oracle.com --- .../NativeConstructorAccessorImpl.java | 9 ++- .../sun/reflect/NativeMethodAccessorImpl.java | 9 ++- .../classes/sun/reflect/misc/ReflectUtil.java | 11 +++- .../lambda/RepetitiveLambdaSerialization.java | 53 ++++++++++++++++ .../lang/invoke/SerializedLambdaTest.java | 12 ++-- .../ManyNewInstanceAnonTest.java | 62 +++++++++++++++++++ 6 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 test/java/lang/invoke/lambda/RepetitiveLambdaSerialization.java create mode 100644 test/sun/reflect/AnonymousNewInstance/ManyNewInstanceAnonTest.java diff --git a/src/share/classes/sun/reflect/NativeConstructorAccessorImpl.java b/src/share/classes/sun/reflect/NativeConstructorAccessorImpl.java index 4f91ddc44..d56c3caab 100644 --- a/src/share/classes/sun/reflect/NativeConstructorAccessorImpl.java +++ b/src/share/classes/sun/reflect/NativeConstructorAccessorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, 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 @@ -26,6 +26,7 @@ package sun.reflect; import java.lang.reflect.*; +import sun.reflect.misc.ReflectUtil; /** Used only for the first few invocations of a Constructor; afterward, switches to bytecode-based implementation */ @@ -44,7 +45,11 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { IllegalArgumentException, InvocationTargetException { - if (++numInvocations > ReflectionFactory.inflationThreshold()) { + // We can't inflate a constructor belonging to a vm-anonymous class + // because that kind of class can't be referred to by name, hence can't + // be found from the generated bytecode. + if (++numInvocations > ReflectionFactory.inflationThreshold() + && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) { ConstructorAccessorImpl acc = (ConstructorAccessorImpl) new MethodAccessorGenerator(). generateConstructor(c.getDeclaringClass(), diff --git a/src/share/classes/sun/reflect/NativeMethodAccessorImpl.java b/src/share/classes/sun/reflect/NativeMethodAccessorImpl.java index 93f186b7b..b1d39e7c9 100644 --- a/src/share/classes/sun/reflect/NativeMethodAccessorImpl.java +++ b/src/share/classes/sun/reflect/NativeMethodAccessorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, 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 @@ -26,6 +26,7 @@ package sun.reflect; import java.lang.reflect.*; +import sun.reflect.misc.ReflectUtil; /** Used only for the first few invocations of a Method; afterward, switches to bytecode-based implementation */ @@ -42,7 +43,11 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl { public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { - if (++numInvocations > ReflectionFactory.inflationThreshold()) { + // We can't inflate methods belonging to vm-anonymous classes because + // that kind of class can't be referred to by name, hence can't be + // found from the generated bytecode. + if (++numInvocations > ReflectionFactory.inflationThreshold() + && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), diff --git a/src/share/classes/sun/reflect/misc/ReflectUtil.java b/src/share/classes/sun/reflect/misc/ReflectUtil.java index 1f871e8de..4316d422c 100644 --- a/src/share/classes/sun/reflect/misc/ReflectUtil.java +++ b/src/share/classes/sun/reflect/misc/ReflectUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, 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 @@ -298,4 +298,13 @@ public final class ReflectUtil { } return false; } + + /** + * Checks if {@code Class cls} is a VM-anonymous class + * as defined by {@link sun.misc.Unsafe#defineAnonymousClass} + * (not to be confused with a Java Language anonymous inner class). + */ + public static boolean isVMAnonymousClass(Class cls) { + return cls.getSimpleName().contains("/"); + } } diff --git a/test/java/lang/invoke/lambda/RepetitiveLambdaSerialization.java b/test/java/lang/invoke/lambda/RepetitiveLambdaSerialization.java new file mode 100644 index 000000000..5c57615a3 --- /dev/null +++ b/test/java/lang/invoke/lambda/RepetitiveLambdaSerialization.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8027681 + * @summary Lambda serialization fails once reflection proxy generation kicks in + * @author Robert Field + * @run main/othervm RepetitiveLambdaSerialization + */ + +import java.io.*; + +public class RepetitiveLambdaSerialization { + + static final int REPS = 20; + + public static void main(String[] args) throws Exception { + LSI ls = z -> "[" + z + "]"; + for (int i = 0; i < REPS; ++i) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutput out = new ObjectOutputStream(baos); + out.writeObject(ls); + out.flush(); + out.close(); + } + System.out.println("Passed."); + } +} + +interface LSI extends Serializable { + String convert(String x); +} diff --git a/test/java/util/stream/test/org/openjdk/tests/java/lang/invoke/SerializedLambdaTest.java b/test/java/util/stream/test/org/openjdk/tests/java/lang/invoke/SerializedLambdaTest.java index 8f01dada8..fdbd689ad 100644 --- a/test/java/util/stream/test/org/openjdk/tests/java/lang/invoke/SerializedLambdaTest.java +++ b/test/java/util/stream/test/org/openjdk/tests/java/lang/invoke/SerializedLambdaTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, 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 @@ -56,14 +56,18 @@ import static org.testng.Assert.fail; */ @Test public class SerializedLambdaTest { + public static final int REPS = 50; + @SuppressWarnings("unchecked") private void assertSerial(T p, Consumer asserter) throws IOException, ClassNotFoundException { asserter.accept(p); - byte[] bytes = serialize(p); - assertTrue(bytes.length > 0); + for (int i=0; i 0); - asserter.accept((T) deserialize(bytes)); + asserter.accept((T) deserialize(bytes)); + } } private void assertNotSerial(Predicate p, Consumer> asserter) diff --git a/test/sun/reflect/AnonymousNewInstance/ManyNewInstanceAnonTest.java b/test/sun/reflect/AnonymousNewInstance/ManyNewInstanceAnonTest.java new file mode 100644 index 000000000..b931b8a65 --- /dev/null +++ b/test/sun/reflect/AnonymousNewInstance/ManyNewInstanceAnonTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 7194897 + * @summary JSR 292: Cannot create more than 16 instances of an anonymous class + * @author Robert Field + * @library /lib/testlibrary + * @compile -XDignore.symbol.file ManyNewInstanceAnonTest.java + * @run main ClassFileInstaller ManyNewInstanceAnonTest + * @run main/othervm -Xbootclasspath/a:. -Xverify:all ManyNewInstanceAnonTest + */ +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import sun.misc.Unsafe; + +public class ManyNewInstanceAnonTest { + + static final int REPS = 20; + static final Class klass = ManyNewInstanceAnonTest.class; + + public static void main(String[] args) throws Exception { + Class c = Unsafe.getUnsafe().defineAnonymousClass(klass, readClassFile(), null); + for (int i = 0; i < REPS; ++i) { + System.out.printf("%d: %s\n", i, c.newInstance()); + } + System.out.println("Passed."); + } + + private static byte[] readClassFile() throws Exception { + try (InputStream in = klass.getResourceAsStream(klass.getSimpleName() + ".class"); + ByteArrayOutputStream out = new ByteArrayOutputStream()) + { + int b; + while ((b = in.read()) != -1) { + out.write(b); + } + return out.toByteArray(); + } + } +} -- GitLab