diff --git a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index f2bb8ebc62caa73e8dc9310f5da73671aa189cfd..f7191f7679f68347731e72d47ea3a39e6698919e 100644 --- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -53,7 +53,7 @@ import java.security.PrivilegedAction; private static final String NAME_OBJECT = "java/lang/Object"; private static final String DESCR_CTOR_SERIALIZED_LAMBDA = MethodType.methodType(void.class, - String.class, + Class.class, int.class, String.class, String.class, String.class, int.class, String.class, String.class, String.class, String.class, @@ -284,7 +284,7 @@ import java.security.PrivilegedAction; mv.visitCode(); mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); mv.visitInsn(DUP);; - mv.visitLdcInsn(targetClass.getName().replace('.', '/')); + mv.visitLdcInsn(Type.getType(targetClass)); mv.visitLdcInsn(samInfo.getReferenceKind()); mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); mv.visitLdcInsn(samInfo.getName()); diff --git a/src/share/classes/java/lang/invoke/SerializedLambda.java b/src/share/classes/java/lang/invoke/SerializedLambda.java index 1a2db3f072a45e0199c0106c3f6327666a1fc32b..3679e3f726d91716277a1e56db0cd595e0367e88 100644 --- a/src/share/classes/java/lang/invoke/SerializedLambda.java +++ b/src/share/classes/java/lang/invoke/SerializedLambda.java @@ -40,7 +40,7 @@ import java.util.Objects; */ public final class SerializedLambda implements Serializable { private static final long serialVersionUID = 8025925345765570181L; - private final String capturingClass; + private final Class capturingClass; private final String functionalInterfaceClass; private final String functionalInterfaceMethodName; private final String functionalInterfaceMethodSignature; @@ -73,7 +73,7 @@ public final class SerializedLambda implements Serializable { * @param capturedArgs The dynamic arguments to the lambda factory site, which represent variables captured by * the lambda */ - public SerializedLambda(String capturingClass, + public SerializedLambda(Class capturingClass, int functionalInterfaceMethodKind, String functionalInterfaceClass, String functionalInterfaceMethodName, @@ -99,7 +99,7 @@ public final class SerializedLambda implements Serializable { /** Get the name of the class that captured this lambda */ public String getCapturingClass() { - return capturingClass; + return capturingClass.getName().replace('.', '/'); } /** Get the name of the functional interface class to which this lambda has been converted */ @@ -166,9 +166,7 @@ public final class SerializedLambda implements Serializable { Method deserialize = AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public Method run() throws Exception { - Class clazz = Class.forName(capturingClass.replace('/', '.'), true, - Thread.currentThread().getContextClassLoader()); - Method m = clazz.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class); + Method m = capturingClass.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class); m.setAccessible(true); return m; } @@ -196,14 +194,4 @@ public final class SerializedLambda implements Serializable { MethodHandleInfo.getReferenceKindString(implMethodKind), implClass, implMethodName, implMethodSignature, instantiatedMethodType, capturedArgs.length); } - - /* - // @@@ Review question: is it worthwhile implementing a versioned serialization protocol? - - private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - } - - private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { - } -*/ } diff --git a/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java b/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java new file mode 100644 index 0000000000000000000000000000000000000000..9d38102b14afda77b413c051f486e52765a4ddd2 --- /dev/null +++ b/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java @@ -0,0 +1,164 @@ +/* + * 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 8004970 +@summary Lambda serialization in the presence of class loaders +@author Peter Levart +*/ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; + +public class LambdaClassLoaderSerialization { + + public interface SerializableRunnable extends Runnable, Serializable {} + + public static class MyCode implements SerializableRunnable { + + private byte[] serialize(Object o) { + ByteArrayOutputStream baos; + try ( + ObjectOutputStream oos = + new ObjectOutputStream(baos = new ByteArrayOutputStream()) + ) { + oos.writeObject(o); + } + catch (IOException e) { + throw new RuntimeException(e); + } + return baos.toByteArray(); + } + + private T deserialize(byte[] bytes) { + try ( + ObjectInputStream ois = + new ObjectInputStream(new ByteArrayInputStream(bytes)) + ) { + return (T) ois.readObject(); + } + catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public void run() { + System.out.println(" this: " + this); + + SerializableRunnable deSerializedThis = deserialize(serialize(this)); + System.out.println(" deSerializedThis: " + deSerializedThis); + + SerializableRunnable runnable = () -> {System.out.println("HELLO");}; + System.out.println(" runnable: " + runnable); + + SerializableRunnable deSerializedRunnable = deserialize(serialize(runnable)); + System.out.println("deSerializedRunnable: " + deSerializedRunnable); + } + } + + public static void main(String[] args) throws Exception { + ClassLoader myCl = new MyClassLoader( + LambdaClassLoaderSerialization.class.getClassLoader() + ); + Class myCodeClass = Class.forName( + LambdaClassLoaderSerialization.class.getName() + "$MyCode", + true, + myCl + ); + Runnable myCode = (Runnable) myCodeClass.newInstance(); + myCode.run(); + } + + static class MyClassLoader extends ClassLoader { + MyClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.indexOf('.') < 0) { + synchronized (getClassLoadingLock(name)) { + Class c = findLoadedClass(name); + if (c == null) { + c = findClass(name); + } + if (resolve) { + resolveClass(c); + } + return c; + } + } else { + return super.loadClass(name, resolve); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + String path = name.replace('.', '/').concat(".class"); + try (InputStream is = getResourceAsStream(path)) { + if (is != null) { + byte[] bytes = readFully(is); + return defineClass(name, bytes, 0, bytes.length); + } else { + throw new ClassNotFoundException(name); + } + } + catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } + + static byte[] readFully(InputStream is) throws IOException { + byte[] output = {}; + int pos = 0; + while (true) { + int bytesToRead; + if (pos >= output.length) { // Only expand when there's no room + bytesToRead = output.length + 1024; + if (output.length < pos + bytesToRead) { + output = Arrays.copyOf(output, pos + bytesToRead); + } + } else { + bytesToRead = output.length - pos; + } + int cc = is.read(output, pos, bytesToRead); + if (cc < 0) { + if (output.length != pos) { + output = Arrays.copyOf(output, pos); + } + break; + } + pos += cc; + } + return output; + } + } +} diff --git a/test/java/lang/invoke/lambda/LambdaSerialization.java b/test/java/lang/invoke/lambda/LambdaSerialization.java index ebe846b173e28b4dc9e33f1f55ef85fa83b72fdb..6d681c35a66df7d1f71fe746b48fd2e237e1d555 100644 --- a/test/java/lang/invoke/lambda/LambdaSerialization.java +++ b/test/java/lang/invoke/lambda/LambdaSerialization.java @@ -25,7 +25,6 @@ @test @bug 8004970 @summary Lambda serialization -@run main/othervm LambdaSerialization */ import java.io.*;