/* * 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 * 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. */ package org.openjdk.tests.java.lang.invoke; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.LongConsumer; import java.util.function.Predicate; import java.util.function.Supplier; import org.testng.annotations.Test; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; /** * SerializedLambdaTest * * @author Brian Goetz */ @Test public class SerializedLambdaTest { public static final int REPS = 50; @SuppressWarnings("unchecked") private<T> void assertSerial(T p, Consumer<T> asserter) throws IOException, ClassNotFoundException { asserter.accept(p); for (int i=0; i<REPS; i++) { byte[] bytes = serialize(p); assertTrue(bytes.length > 0); asserter.accept((T) deserialize(bytes)); } } private void assertNotSerial(Predicate<String> p, Consumer<Predicate<String>> asserter) throws IOException, ClassNotFoundException { asserter.accept(p); try { byte[] bytes = serialize(p); fail("Expected serialization failure"); } catch (NotSerializableException e) { // success } } private byte[] serialize(Object o) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(o); oos.close(); return bos.toByteArray(); } private Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException { try(ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { return ois.readObject(); } } // Test instantiating against intersection type public void testSimpleSerializedInstantiation() throws IOException, ClassNotFoundException { @SuppressWarnings("unchecked") Predicate<String> pred = (Predicate<String> & Serializable) s -> true; assertSerial(pred, p -> { assertTrue(p instanceof Predicate); assertTrue(p instanceof Serializable); assertTrue(p.test("")); }); } interface SerPredicate<T> extends Predicate<T>, Serializable { } // Test instantiating against derived type public void testSimpleSerializedInstantiation2() throws IOException, ClassNotFoundException { SerPredicate<String> serPred = (SerPredicate<String>) s -> true; assertSerial(serPred, p -> { assertTrue(p instanceof Predicate); assertTrue(p instanceof Serializable); assertTrue(p instanceof SerPredicate); assertTrue(p.test("")); }); } // Negative test: non-serializable lambdas are in fact not serializable public void testNonserializableInstantiation() throws IOException, ClassNotFoundException { @SuppressWarnings("unchecked") Predicate<String> pred = (Predicate<String>) s -> true; assertNotSerial(pred, p -> { assertTrue(p instanceof Predicate); assertFalse(p instanceof Serializable); assertTrue(p.test("")); }); } // Test lambda capturing int public void testSerializeCapturingInt() throws IOException, ClassNotFoundException { class Moo { @SuppressWarnings("unchecked") Predicate<String> foo(int x) { return (Predicate<String> & Serializable) s -> s.length() >= x; } } Predicate<String> pred = new Moo().foo(3); assertSerial(pred, p -> { assertTrue(p.test("yada")); assertFalse(p.test("no")); }); } // Test lambda capturing String public void testSerializeCapturingString() throws IOException, ClassNotFoundException { class Moo { @SuppressWarnings("unchecked") Predicate<String> foo(String t) { return (Predicate<String> & Serializable) s -> s.equals(t); } } Predicate<String> pred = new Moo().foo("goo"); assertSerial(pred, p -> { assertTrue(p.test("goo")); assertFalse(p.test("foo")); }); } // Negative test: lambdas that capture a non-serializable var public void testSerializeCapturingNonSerializable() throws IOException, ClassNotFoundException { class Box { String s; Box(String s) { this.s = s; } } class Moo { @SuppressWarnings("unchecked") Predicate<String> foo(Box b) { return (Predicate<String> & Serializable) s -> s.equals(b.s); } } Predicate<String> pred = new Moo().foo(new Box("goo")); assertNotSerial(pred, p -> { assertTrue(p.test("goo")); assertFalse(p.test("foo")); }); } static boolean startsWithA(String s) { return s.startsWith("a"); } // Test static method ref public void testStaticMR() throws IOException, ClassNotFoundException { @SuppressWarnings("unchecked") Predicate<String> mh1 = (Predicate<String> & Serializable) SerializedLambdaTest::startsWithA; @SuppressWarnings("unchecked") Predicate<String> mh2 = (SerPredicate<String>) SerializedLambdaTest::startsWithA; Consumer<Predicate<String>> b = p -> { assertTrue(p instanceof Serializable); assertTrue(p.test("arf")); assertFalse(p.test("barf")); }; assertSerial(mh1, b); assertSerial(mh2, b); } // Test unbound method ref of nonserializable class -- should still succeed public void testUnboundMR() throws IOException, ClassNotFoundException { class Moo { public boolean startsWithB(String s) { return s.startsWith("b"); } } @SuppressWarnings("unchecked") BiPredicate<Moo, String> mh1 = (BiPredicate<Moo, String> & Serializable) Moo::startsWithB; Consumer<BiPredicate<Moo, String>> b = p -> { assertTrue(p instanceof Serializable); assertTrue(p.test(new Moo(), "barf")); assertFalse(p.test(new Moo(), "arf")); }; assertSerial(mh1, b); } // Negative test: test bound MR of nonserializable class public void testBoundMRNotSerReceiver() throws IOException, ClassNotFoundException { class Moo { public boolean startsWithB(String s) { return s.startsWith("b"); } } Moo moo = new Moo(); @SuppressWarnings("unchecked") Predicate<String> mh1 = (Predicate<String> & Serializable) moo::startsWithB; @SuppressWarnings("unchecked") Predicate<String> mh2 = (SerPredicate<String>) moo::startsWithB; Consumer<Predicate<String>> b = p -> { assertTrue(p instanceof Serializable); assertTrue(p.test("barf")); assertFalse(p.test("arf")); }; assertNotSerial(mh1, b); assertNotSerial(mh2, b); } // Test bound MR of serializable class @SuppressWarnings("serial") static class ForBoundMRef implements Serializable { public boolean startsWithB(String s) { return s.startsWith("b"); } } public void testBoundMR() throws IOException, ClassNotFoundException { ForBoundMRef moo = new ForBoundMRef(); @SuppressWarnings("unchecked") Predicate<String> mh1 = (Predicate<String> & Serializable) moo::startsWithB; @SuppressWarnings("unchecked") Predicate<String> mh2 = (SerPredicate<String>) moo::startsWithB; Consumer<Predicate<String>> b = p -> { assertTrue(p instanceof Serializable); assertTrue(p.test("barf")); assertFalse(p.test("arf")); }; assertSerial(mh1, b); assertSerial(mh2, b); } static class ForCtorRef { public boolean startsWithB(String s) { return s.startsWith("b"); } } // Test ctor ref of nonserializable class public void testCtorRef() throws IOException, ClassNotFoundException { @SuppressWarnings("unchecked") Supplier<ForCtorRef> ctor = (Supplier<ForCtorRef> & Serializable) ForCtorRef::new; Consumer<Supplier<ForCtorRef>> b = s -> { assertTrue(s instanceof Serializable); ForCtorRef m = s.get(); assertTrue(m.startsWithB("barf")); assertFalse(m.startsWithB("arf")); }; assertSerial(ctor, b); } //Test throwing away return type public void testDiscardReturnBound() throws IOException, ClassNotFoundException { List<String> list = new ArrayList<>(); Consumer<String> c = (Consumer<String> & Serializable) list::add; assertSerial(c, cc -> { assertTrue(cc instanceof Consumer); }); AtomicLong a = new AtomicLong(); LongConsumer lc = (LongConsumer & Serializable) a::addAndGet; assertSerial(lc, plc -> { plc.accept(3); }); } // Tests of direct use of metafactories private static boolean foo(Object s) { return s != null && ((String) s).length() > 0; } private static final MethodType predicateMT = MethodType.methodType(boolean.class, Object.class); private static final MethodType stringPredicateMT = MethodType.methodType(boolean.class, String.class); private static final Consumer<Predicate<String>> fooAsserter = x -> { assertTrue(x.test("foo")); assertFalse(x.test("")); assertFalse(x.test(null)); }; // standard MF: nonserializable supertype public void testDirectStdNonser() throws Throwable { MethodHandle fooMH = MethodHandles.lookup().findStatic(SerializedLambdaTest.class, "foo", predicateMT); // Standard metafactory, non-serializable target: not serializable CallSite cs = LambdaMetafactory.metafactory(MethodHandles.lookup(), "test", MethodType.methodType(Predicate.class), predicateMT, fooMH, stringPredicateMT); Predicate<String> p = (Predicate<String>) cs.getTarget().invokeExact(); assertNotSerial(p, fooAsserter); } // standard MF: serializable supertype public void testDirectStdSer() throws Throwable { MethodHandle fooMH = MethodHandles.lookup().findStatic(SerializedLambdaTest.class, "foo", predicateMT); // Standard metafactory, serializable target: not serializable CallSite cs = LambdaMetafactory.metafactory(MethodHandles.lookup(), "test", MethodType.methodType(SerPredicate.class), predicateMT, fooMH, stringPredicateMT); assertNotSerial((SerPredicate<String>) cs.getTarget().invokeExact(), fooAsserter); } // alt MF: nonserializable supertype public void testAltStdNonser() throws Throwable { MethodHandle fooMH = MethodHandles.lookup().findStatic(SerializedLambdaTest.class, "foo", predicateMT); // Alt metafactory, non-serializable target: not serializable CallSite cs = LambdaMetafactory.altMetafactory(MethodHandles.lookup(), "test", MethodType.methodType(Predicate.class), predicateMT, fooMH, stringPredicateMT, 0); assertNotSerial((Predicate<String>) cs.getTarget().invokeExact(), fooAsserter); } // alt MF: serializable supertype public void testAltStdSer() throws Throwable { MethodHandle fooMH = MethodHandles.lookup().findStatic(SerializedLambdaTest.class, "foo", predicateMT); // Alt metafactory, serializable target, no FLAG_SERIALIZABLE: not serializable CallSite cs = LambdaMetafactory.altMetafactory(MethodHandles.lookup(), "test", MethodType.methodType(SerPredicate.class), predicateMT, fooMH, stringPredicateMT, 0); assertNotSerial((SerPredicate<String>) cs.getTarget().invokeExact(), fooAsserter); // Alt metafactory, serializable marker, no FLAG_SERIALIZABLE: not serializable cs = LambdaMetafactory.altMetafactory(MethodHandles.lookup(), "test", MethodType.methodType(Predicate.class), predicateMT, fooMH, stringPredicateMT, LambdaMetafactory.FLAG_MARKERS, 1, Serializable.class); assertNotSerial((Predicate<String>) cs.getTarget().invokeExact(), fooAsserter); } }