changeset 7614:a89351237ea6

Modify serialization testing agent to work in all-static-mangling mode; adjustments to tests that are serialization-hostile
author briangoetz
date Tue, 12 Mar 2013 17:20:37 -0400
parents 571a828ffc94
children f5ce4c3958b1
files src/share/classes/java/lang/invoke/SerializedLambda.java test-ng/agent/conf/agent.props test-ng/agent/conf/serialize.list test-ng/agent/src/com/oracle/lambda/Agent.java test-ng/agent/src/com/oracle/lambda/Main.java test-ng/agent/src/com/oracle/lambda/SerializationInjector.java test-ng/agent/src/com/oracle/lambda/TestLambdaSerialization.java test-ng/build.xml test-ng/tests/org/openjdk/tests/java/lang/invoke/DeserializeMethodTest.java test-ng/tests/org/openjdk/tests/java/util/BitsetStreamTest.java test-ng/tests/org/openjdk/tests/java/util/MapTest.java test-ng/tests/org/openjdk/tests/java/util/stream/CollectionAndMapModifyStreamTest.java test-ng/tests/org/openjdk/tests/java/util/stream/ForEachOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/GroupByOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/IntSliceOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/SequentialOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/SliceOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/SortedOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java test-ng/tests/org/openjdk/tests/java/util/stream/TeeOpTest.java
diffstat 19 files changed, 210 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/SerializedLambda.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/src/share/classes/java/lang/invoke/SerializedLambda.java	Tue Mar 12 17:20:37 2013 -0400
@@ -25,6 +25,7 @@
 package java.lang.invoke;
 
 import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
--- a/test-ng/agent/conf/serialize.list	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/agent/conf/serialize.list	Tue Mar 12 17:20:37 2013 -0400
@@ -1,5 +1,6 @@
 #java/util/Optional --- Duplicate class
 #java/util/OptionalDouble --- Duplicate class
+java/lang/invoke/MagicLambdaImpl
 java/util/OptionalInt
 java/util/ArrayList\$SubList
 java/util/function/Function\$\$Lambda.*
@@ -10,13 +11,13 @@
 java/util/stream/StreamTestData\$.*
 java/util/stream/Nodes.*NodeBuilder
 java/util/stream/Nodes\$.*Node
+java/util/stream/AbstractSpinedBuffer
 java/util/stream/SpinedBuffer
 org/openjdk/tests/java/util/stream/OpTestCase\$ExerciseDataStreamBuilder
 org/openjdk/tests/java/util/stream/OpTestCase\$ExerciseDataTerminalBuilder
-org/openjdk/tests/java/util/stream/TabulatorsTest\$GroupedMapAssertion
 org/openjdk/tests/java/util/stream/IntNodeTest
 org/openjdk/tests/java/util/stream/UnorderedStreamTest
 org/openjdk/tests/java/util/stream/GroupByOpTest
-org/openjdk/tests/java/util/stream/TabulatorsTest\$ReduceAssertion
-org/openjdk/tests/java/util/stream/TabulatorsTest\$CollectionAssertion
+org/openjdk/tests/java/util/stream/TabulatorsTest
+org/openjdk/tests/java/util/stream/TabulatorsTest\$.*Assertion
 org/openjdk/tests/java/util/stream/StreamLinkTest
--- a/test-ng/agent/src/com/oracle/lambda/Agent.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/agent/src/com/oracle/lambda/Agent.java	Tue Mar 12 17:20:37 2013 -0400
@@ -31,14 +31,19 @@
 public class Agent {
     public static void premain(String agentArgs, Instrumentation instrumentation)
             throws IOException {
-        final SerializationInjector si = new SerializationInjector(agentArgs);
-        instrumentation.addTransformer(new ClassFileTransformer() {
-            @Override
-            public byte[] transform(final ClassLoader cl, String string,
-                    Class<?> type, ProtectionDomain pd,
-                    byte[] bytes) throws IllegalClassFormatException {
-                return si.transformClass(cl, bytes, false);
-            }
-        });
+        // If run without a config file, all we do is set up the shutdown hook
+        if (agentArgs != null) {
+            final SerializationInjector si = new SerializationInjector(agentArgs);
+            instrumentation.addTransformer(new ClassFileTransformer() {
+                @Override
+                public byte[] transform(final ClassLoader cl, String string,
+                                        Class<?> type, ProtectionDomain pd,
+                                        byte[] bytes) throws IllegalClassFormatException {
+                    return si.transformClass(cl, bytes, false);
+                }
+            });
+        }
+        else
+            TestLambdaSerialization.initializeShutDownHook();
     }
  }
--- a/test-ng/agent/src/com/oracle/lambda/Main.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/agent/src/com/oracle/lambda/Main.java	Tue Mar 12 17:20:37 2013 -0400
@@ -41,6 +41,7 @@
 import java.nio.file.attribute.FileAttribute;
 import java.nio.file.attribute.PosixFilePermission;
 import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -58,35 +59,6 @@
  * @author kumasrin
  */
 public class Main {
-      static void recursiveDelete(File target) throws IOException {
-        if (!target.exists()) {
-            return;
-        }
-        Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() {
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
-                try {
-                    Files.deleteIfExists(dir);
-                } catch (IOException ex) {
-                    System.out.println("Error: could not delete: " + dir.toString());
-                    System.out.println(ex.getMessage());
-                    return FileVisitResult.TERMINATE;
-                }
-                return FileVisitResult.CONTINUE;
-            }
-            @Override
-            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
-                try {
-                    Files.deleteIfExists(file);
-                } catch (IOException ex) {
-                    System.out.println("Error: could not delete: " + file.toString());
-                    System.out.println(ex.getMessage());
-                    return FileVisitResult.TERMINATE;
-                }
-                return FileVisitResult.CONTINUE;
-            }
-        });
-    }
 
     static void usage() {
         System.out.println("usage: -o output_dir -f path_to_list path_to/SomeClass.class");
@@ -108,7 +80,7 @@
         }
         File outDir = null;
         File inFile = null;
-        File listFile = null;
+        File confFile = null;
         String className = null;
         for (int i = 0 ; i < args.length ; i++) {
             switch (args[i]) {
@@ -118,7 +90,7 @@
                     break;
                 case "-f":
                     i++;
-                    listFile = new File(args[i]);
+                    confFile = new File(args[i]);
                     break;
                 case "-c":
                     i++;
@@ -133,7 +105,6 @@
         }
         if (inFile.isDirectory()) {
             if (outDir.exists()) {
-                recursiveDelete(outDir);
                 outDir.mkdirs();
             }
             URL[] urls = {inFile.toURI().toURL()};
@@ -146,13 +117,12 @@
             }
         } else if (inFile.getName().endsWith(".jar")) {
             if (outDir.exists()) {
-                recursiveDelete(outDir);
                 outDir.mkdirs();
             }
             URL[] urls = {inFile.toURI().toURL()};
             ClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
             Pattern pattern = Pattern.compile(className != null ? Pattern.quote("className" + ".class") : ".*");
-            doJar(listFile, inFile, outDir, cl, pattern, className != null);
+            doJar(confFile, inFile, outDir, cl, pattern, className != null);
         } else {
             outDir.mkdirs();
             URL[] urls = {inFile.getParentFile().toURI().toURL()};
@@ -175,10 +145,10 @@
         return baos.toByteArray();
     }
 
-    static void doJar(File listFile, File inFile, File outDir, ClassLoader cl, Pattern pattern, boolean forceSerialize) throws IOException {
+    static void doJar(File confFile, File inFile, File outDir, ClassLoader cl, Pattern pattern, boolean forceSerialize) throws IOException {
         JarFile jf = new JarFile(inFile);
         byte[] buffer;
-        SerializationInjector si = new SerializationInjector(listFile);
+        SerializationInjector si = new SerializationInjector(confFile.getAbsolutePath());
         FileSystem fs = null;
         boolean jarOut = outDir.getName().endsWith(".jar");
         Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxrwxrwx");
--- a/test-ng/agent/src/com/oracle/lambda/SerializationInjector.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/agent/src/com/oracle/lambda/SerializationInjector.java	Tue Mar 12 17:20:37 2013 -0400
@@ -28,7 +28,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
-import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -38,9 +37,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
+
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassVisitor;
 import jdk.internal.org.objectweb.asm.ClassWriter;
@@ -48,55 +49,46 @@
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 import jdk.internal.org.objectweb.asm.Type;
-import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
+
 import static java.nio.file.StandardOpenOption.*;
 
 public class SerializationInjector {
     static final String PREFIX = "com.oracle.lambda";
-    static final String EXCLUDES = "java.*|javax.*";
+    static final String INCLUDES = "org.openjdk.tests.java.*"; // also should include java.util...
     static final String SERIALIZE = "";
     static PrintStream outputStream;
     static boolean shouldClose = false;
-    final DebugPrint debug;
-    final Pattern excludePattern;
-    final Pattern serializePattern;
     static boolean storeStackTraces;
     static boolean deserializationWorkaround;
+
+    final DebugPrint debug;
+    final Pattern includePattern;
+    final Pattern serializePattern;
     final boolean serializeOnly;
 
     public SerializationInjector() {
-        excludePattern = Pattern.compile(EXCLUDES);
+        includePattern = Pattern.compile(INCLUDES);
         serializePattern = Pattern.compile(SERIALIZE);
         outputStream = System.out;
         debug = new DebugPrint(outputStream, false);
-        initializeShutDownHook();
+        TestLambdaSerialization.initializeShutDownHook();
         storeStackTraces = false;
         deserializationWorkaround = false;
         serializeOnly = false;
     }
-    protected SerializationInjector(File serialize) throws IOException {
-        excludePattern = Pattern.compile(EXCLUDES);
-        serializePattern = fileToPattern(serialize);
-        outputStream = System.out;
-        debug = new DebugPrint(outputStream, true);
-        storeStackTraces = false;
-        deserializationWorkaround = false;
-        serializeOnly = true;
-    }
 
-    public SerializationInjector(String args) throws IOException {
-        final File propsFile = args == null ? null : new File(args);
-        if (propsFile != null) {    
+    public SerializationInjector(String confFile) throws IOException {
+        if (confFile != null) {
             Properties props = new Properties();
-            try (FileInputStream fis = new FileInputStream(args)) {
+            try (FileInputStream fis = new FileInputStream(confFile)) {
                 props.load(fis);
             } catch (IOException ex) {
                 System.err.println("Error: " + ex.getMessage());
                 throw new Error(ex);
             }
             props.list(System.err);
-            String exclude = props.getProperty(PREFIX + ".exclude.files", EXCLUDES);
-            excludePattern = Pattern.compile(exclude);
+            String include = props.getProperty(PREFIX + ".include.files", INCLUDES);
+            includePattern = Pattern.compile(include);
             String injectSerializeName = props.getProperty(PREFIX + ".serialize.file", null);
             storeStackTraces = props.getProperty(PREFIX + ".storeStackTraces", null) != null;
             deserializationWorkaround = props.getProperty(PREFIX +
@@ -110,8 +102,7 @@
                 // if there is no serialization file listed, try to see if there
                 // is one in the agent/conf directory it is usually the same dir
                 // as the props file, othewise use the default patterns.
-                File serializeFile = new File(propsFile.getParentFile(),
-                        "serialize.list");
+                File serializeFile = new File(new File(confFile).getParentFile(), "serialize.list");
                 if (serializeFile.exists()) {
                     System.err.println(serializeFile.getAbsolutePath());
                     serializePattern = fileToPattern(serializeFile);
@@ -141,14 +132,14 @@
             debug = new DebugPrint(outputStream,
             props.getProperty(PREFIX + ".debug") != null);
         } else {
-            excludePattern = Pattern.compile(EXCLUDES);
+            includePattern = Pattern.compile(INCLUDES);
             serializePattern = Pattern.compile(SERIALIZE);
             outputStream = System.out;
             debug = new DebugPrint(outputStream, false);
             storeStackTraces = false;
             deserializationWorkaround = false;
         }
-        initializeShutDownHook();
+        TestLambdaSerialization.initializeShutDownHook();
         serializeOnly = false;
     }
     final Pattern fileToPattern(String filename) throws IOException {
@@ -171,18 +162,6 @@
         return Pattern.compile(regStr);
     }
 
-    final boolean initializeShutDownHook() {
-        Runtime.getRuntime().addShutdownHook(
-            new Thread() {
-                @Override
-                public void run() {
-                    TestLambdaSerialization.printStats();
-                }
-            }
-        );
-        return true;
-    }
-
     byte[] transformClass(ClassLoader cl, byte[] classBuffer, boolean forceSerialize) {
         byte[] xBuffer = classBuffer;
         ClassReader classReader = new ClassReader(xBuffer);
@@ -194,7 +173,7 @@
         if (serializeOnly) {
             return xBuffer;
         }
-        if (excludePattern.matcher(cname).matches()) {
+        if (!includePattern.matcher(cname).matches()) {
             debug.println("Excluding SAND: " + cname);
             return xBuffer;
         }
@@ -209,9 +188,8 @@
                 ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
         classReader.accept(makeSANDVisitor(classWriter, locals), 0);
         byte[] byteArray = classWriter.toByteArray();
-        CheckClassAdapter.verify(new ClassReader(byteArray), cl, false,
-                                 new PrintWriter(System.err));
-
+//        CheckClassAdapter.verify(new ClassReader(byteArray), cl, false,
+//                                 new PrintWriter(System.err));
         return byteArray;
     }
 
@@ -242,13 +220,13 @@
                 };
             }
         }, 0);
+
         return locals;
     }
 
     ClassVisitor makeSANDVisitor(ClassWriter cw, final Map<String, Integer>methodLocals) {
         return new ClassVisitor(V1_8, cw) {
             String cname = null;
-            final Map<String, Integer> mlocals = methodLocals;
             @Override
             public void visit(int version, int access, String name,
                               String signature, String superName,
@@ -266,7 +244,7 @@
                 return new MethodVisitor(V1_8,
                         super.visitMethod(access, name, desc, signature, exceptions)) {
                     final String mid = cname + "+" + name + "+" + desc + "+" + signature;
-                    int locals = mlocals.get(mid);
+                    int locals = methodLocals.containsKey(mid) ? methodLocals.get(mid) : 0;
 
                     int saveArgs(Type[] types) {
                         final int n = locals + 1;
@@ -368,17 +346,33 @@
         ClassReader classReader = new ClassReader(classBuffer);
         ClassWriter classWriter = new ClassWriter(classReader,
                 ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
-        classReader.accept(makeSerializationVisitor(classWriter), 0);
+        boolean hasNoArgCtor = hasNoArgCtor(classBuffer);
+        classReader.accept(makeSerializationVisitor(classWriter, !hasNoArgCtor), 0);
         byte[] byteArray = classWriter.toByteArray();
-        CheckClassAdapter.verify(new ClassReader(byteArray), cl, false,
-                new PrintWriter(System.err));
+        // Needed to comment this out because some classes are actually loaded during this verification
+        // and they fail the package name test (java.*)
+//        CheckClassAdapter.verify(new ClassReader(byteArray), cl, false,
+//                new PrintWriter(System.err));
 
 //        dumpClass(classBuffer, "pre-serialization");
 //        dumpClass(byteArray, "post-serialization");
         return byteArray;
     }
 
-    ClassVisitor makeSerializationVisitor(final ClassWriter cw) {
+    private boolean hasNoArgCtor(byte[] buffer) {
+        final AtomicBoolean foundNoArg = new AtomicBoolean();
+        new ClassReader(buffer).accept(new ClassVisitor(V1_8, null) {
+            @Override
+            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+                if (name.equals("<init>") && desc.equals("()V"))
+                    foundNoArg.set(true);
+                return super.visitMethod(access, name, desc, signature, exceptions);
+            }
+        }, 0);
+        return foundNoArg.get();
+    }
+
+    ClassVisitor makeSerializationVisitor(final ClassWriter cw, final boolean injectNoArg) {
         return new ClassVisitor(V1_8, cw) {
             @Override
             public void visit(int version, int access, String name,
@@ -397,6 +391,15 @@
                         signature += "L" + sername + ";";
                 }
                 super.visit(version, access, name, signature, superName, interfaces);
+                if (injectNoArg && (access & ACC_INTERFACE) == 0) {
+                    MethodVisitor ctor = visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+                    ctor.visitCode();
+                    ctor.visitVarInsn(ALOAD, 0);
+                    ctor.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V");
+                    ctor.visitInsn(RETURN);
+                    ctor.visitMaxs(-1, -1);
+                    ctor.visitEnd();
+                }
             }
         };
     }
--- a/test-ng/agent/src/com/oracle/lambda/TestLambdaSerialization.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/agent/src/com/oracle/lambda/TestLambdaSerialization.java	Tue Mar 12 17:20:37 2013 -0400
@@ -34,6 +34,7 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class TestLambdaSerialization {
@@ -49,6 +50,8 @@
 
     public static void printStats() {
         PrintStream rpt = SerializationInjector.outputStream;
+        if (rpt == null)
+            rpt = System.out;
         try {
             rpt.println("Lambda Serialization Test Status:");
             rpt.println("  serializations attempted:       " + serCounters.attempted);
@@ -195,6 +198,24 @@
         serializeAndDeserialize(s, "Hi", array);
         serializeAndDeserialize(s, "Bye", array);
     }
+
+    static AtomicBoolean initialized = new AtomicBoolean();
+    static {
+        initializeShutDownHook();
+    }
+
+    static void initializeShutDownHook() {
+        if (initialized.compareAndSet(false, true)) {
+            Runtime.getRuntime().addShutdownHook(
+                    new Thread() {
+                        @Override
+                        public void run() {
+                            printStats();
+                        }
+                    }
+            );
+        }
+    }
 }
 
 class CounterSet {
--- a/test-ng/build.xml	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/build.xml	Tue Mar 12 17:20:37 2013 -0400
@@ -29,7 +29,8 @@
 
     <property name="build.dir" value="../../build/test-ng" />
     <property name="tests.classes.dir" value="${build.dir}/test-classes"/>
-    <property name="mangled.dir" value="${build.dir}/mangled-classes"/>
+    <property name="mangled.bootdir" value="${build.dir}/mangled-bootclasses"/>
+    <property name="mangled.testdir" value="${build.dir}/mangled-testclasses"/>
     <property name="boottests.classes.dir" value="${build.dir}/boottest-classes"/>
     <property name="bootlib.classes.dir" value="${build.dir}/test-bootlib"/>
     <property name="test.reports.dir" value="${build.dir}/test-reports"/>
@@ -43,8 +44,7 @@
     <property name="agent.dir"       location="${basedir}/agent"/>
     <property name="agent.jar"       value="${agent.dir}/dist/SerializationInjectorAgent.jar" />
     <property name="agent.conf.file" value="${agent.dir}/conf/agent.props" />
-    <property name="agent.ser.file"  value="${agent.dir}/conf/serialize.list" />
-    <property name="agent.rt.jar"    value="${java.home}/lib/rt.jar" />
+    <property name="agent.rt.jar"    value="rt.jar" />
 
     <property name="lib.testng.jar" value="${lib.dir}/testng.jar"/>
     <property name="lib.tools.jar" value="${java.home}/../lib/tools.jar"/>
@@ -62,11 +62,17 @@
         <pathelement location="${lib.tools.jar}"/>
     </path>
 
+    <path id="mangled.class.path">
+        <pathelement location="${mangled.testdir}" />
+        <pathelement location="${lib.testng.jar}"/>
+        <pathelement location="${lib.tools.jar}"/>
+    </path>
+
     <taskdef name="testng" classpathref="test.class.path" classname="org.testng.TestNGAntTask" />
 
     <target name="prepare">
         <mkdir dir="${build.dir}"/>
-        <mkdir dir="${mangled.dir}"/>
+        <mkdir dir="${mangled.bootdir}"/>
         <mkdir dir="${tests.classes.dir}"/>
         <mkdir dir="${boottests.classes.dir}"/>
         <mkdir dir="${bootlib.classes.dir}"/>
@@ -138,63 +144,84 @@
         </ant>
     </target>
 
-    <target name="mangle-jdk" depends="build-mangler">
+    <target name="premangle" depends="build-mangler, test-compile">
+        <delete dir="${mangled.bootdir}" />
+        <mkdir dir="${mangled.bootdir}" />
+        <delete dir="${mangled.testdir}" />
+        <mkdir dir="${mangled.testdir}" />
+        <!-- Mangle JDK to mangled.bootdir -->
         <java jar="${agent.jar}" fork="true" >
             <arg value="-o"/>
-            <arg value="${mangled.dir}" />
+            <arg value="${mangled.bootdir}" />
             <arg value="-f"/>
-            <arg value="${agent.ser.file}"/>
+            <arg value="${agent.conf.file}"/>
             <arg value="${agent.rt.jar}"/>
         </java>
+        <!-- Mangle boot test classes to mangled.bootdir -->
+        <jar destfile="${build.dir}/tmpA.jar" basedir="${boottests.classes.dir}"/>
+        <java jar="${agent.jar}" fork="true" >
+            <arg value="-o"/>
+            <arg value="${mangled.bootdir}" />
+            <arg value="-f"/>
+            <arg value="${agent.conf.file}"/>
+            <arg value="${build.dir}/tmpA.jar"/>
+        </java>
+        <!-- Mangle boot lib classes to mangled.bootdir -->
+        <jar destfile="${build.dir}/tmpB.jar" basedir="${bootlib.classes.dir}"/>
+        <java jar="${agent.jar}" fork="true" >
+            <arg value="-o"/>
+            <arg value="${mangled.bootdir}" />
+            <arg value="-f"/>
+            <arg value="${agent.conf.file}"/>
+            <arg value="${build.dir}/tmpB.jar"/>
+        </java>
+        <!-- Mangle test classes to mangled.testdir -->
+        <jar destfile="${build.dir}/tmpC.jar" basedir="${tests.classes.dir}"/>
+        <java jar="${agent.jar}" fork="true" >
+            <arg value="-o"/>
+            <arg value="${mangled.testdir}" />
+            <arg value="-f"/>
+            <arg value="${agent.conf.file}"/>
+            <arg value="${build.dir}/tmpC.jar"/>
+        </java>
     </target>
 
-    <target name="mangle-jdk-lite" depends="build-mangler">
-        <java jar="${agent.jar}" fork="true" >
-            <arg value="-o"/>
-            <arg value="${mangled.dir}" />
-            <arg value="-c"/>
-            <arg value="X"/>
-            <arg value="${java.home}/classes/java/util/ArrayList$SubList.class"/>
-        </java>
-        <java jar="${agent.jar}" fork="true" >
-            <arg value="-o"/>
-            <arg value="${mangled.dir}" />
-            <arg value="-c"/>
-            <arg value="X"/>
-            <arg value="${java.home}/classes/java/util/stream/SpinedBuffer.class"/>
-        </java>
-    </target>
-
+    <!--
+        Some tests are hostile to our serialization testing strategy; these are those where lambdas
+        have side-effects on captured arguments, and then we test properties of the captured arguments
+        separately.  This is because serialization/deserialization severs the aliasing that this test
+        strategy depends on.  So we mark and exclude these classes.
+    -->
     <target name="test-mangled" depends="test-compile, build-mangler" >
-        <testng classpathref="test.class.path" outputdir="${test.reports.dir}" >
-            <classfileset dir="${tests.classes.dir}" includes="org/openjdk/tests/java/**/*.class"
+        <testng classpathref="mangled.class.path" outputdir="${test.reports.dir}"
+                excludedgroups="serialization-hostile">
+            <classfileset dir="${mangled.testdir}" includes="org/openjdk/tests/java/**/*.class"
                           excludes="**/SerializedLambdaTest.class"/>
-            <classfileset dir="${boottests.classes.dir}" includes="java/**/*.class"/>
-            <jvmarg value="-Xbootclasspath/p:${mangled.dir}"/>
-            <jvmarg value="-Xbootclasspath/p:${boottests.classes.dir}"/>
-            <jvmarg value="-Xbootclasspath/p:${bootlib.classes.dir}"/>
+            <classfileset dir="${mangled.bootdir}" includes="java/util/stream/**/*.class"/>
+            <jvmarg value="-Xbootclasspath/p:${agent.jar}"/>
+            <jvmarg value="-Xbootclasspath/p:${mangled.bootdir}"/>
             <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/>
             <jvmarg value="-ea" />
             <jvmarg value="-esa" />
             <jvmarg value="-Xverify:all" />
             <jvmarg value="-Xmx2500m" />
-            <jvmarg value="-javaagent:${agent.jar}=${agent.conf.file}"/>
+            <jvmarg value="-javaagent:${agent.jar}"/>
         </testng>
     </target>
 
     <target name="test-mangledp" depends="test-compile, build-mangler" >
-        <testng classpathref="test.class.path" outputdir="${test.reports.dir}" >
-            <classfileset dir="${tests.classes.dir}" includes="**/${test.pattern}.class"/>
-            <classfileset dir="${boottests.classes.dir}" includes="**/${test.pattern}.class"/>
-            <jvmarg value="-Xbootclasspath/p:${mangled.dir}"/>
-            <jvmarg value="-Xbootclasspath/p:${boottests.classes.dir}"/>
-            <jvmarg value="-Xbootclasspath/p:${bootlib.classes.dir}"/>
+        <testng classpathref="mangled.class.path" outputdir="${test.reports.dir}"
+                excludedgroups="serialization-hostile">
+            <classfileset dir="${mangled.testdir}" includes="**/${test.pattern}.class"/>
+            <classfileset dir="${mangled.bootdir}" includes="java/util/stream/**/${test.pattern}.class"/>
+            <jvmarg value="-Xbootclasspath/p:${agent.jar}"/>
+            <jvmarg value="-Xbootclasspath/p:${mangled.bootdir}"/>
             <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/>
             <jvmarg value="-ea" />
             <jvmarg value="-esa" />
             <jvmarg value="-Xverify:all" />
             <jvmarg value="-Xmx2500m" />
-            <jvmarg value="-javaagent:${agent.jar}=${agent.conf.file}"/>
+            <jvmarg value="-javaagent:${agent.jar}"/>
         </testng>
     </target>
     <!-- mangler related,  end -->
--- a/test-ng/tests/org/openjdk/tests/java/lang/invoke/DeserializeMethodTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/lang/invoke/DeserializeMethodTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -33,12 +33,10 @@
 import static org.testng.Assert.fail;
 
 /**
- * TestDeserializeMethod
- *
- * @author Brian Goetz
+ * Ensure that the $deserializeLambda$ method is present when it should be, and absent otherwise
  */
 
-@Test
+@Test(groups = { "serialization-hostile" })
 public class DeserializeMethodTest {
     private void assertDeserializeMethod(Class<?> clazz, boolean expectedPresent) {
         try {
--- a/test-ng/tests/org/openjdk/tests/java/util/BitsetStreamTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/BitsetStreamTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -24,7 +24,9 @@
  */
 package org.openjdk.tests.java.util;
 
+import java.util.Iterator;
 import java.util.PrimitiveIterator;
+import java.util.function.Consumer;
 import java.util.stream.StreamTestData;
 import java.util.stream.StreamTestDataProvider;
 import org.testng.annotations.Test;
@@ -53,7 +55,8 @@
     @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
     public void testBitsetStream(String name, StreamTestData<Integer> data) {
         BitSet bs = new BitSet();
-        data.forEach(bs::set);
+        for (int i : data.stream().map(e -> (int) e).toArray())
+            bs.set(i);
 
         assertEquals(bs.cardinality(), data.stream().collect(Collectors.toSet()).size());
         assertEquals(bs.cardinality(), bs.stream().reduce(0, (s, i) -> s+1));
--- a/test-ng/tests/org/openjdk/tests/java/util/MapTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/MapTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -78,7 +78,7 @@
         map = null;
     }
 
-    @Test
+    @Test(groups = { "serialization-hostile" })
     public void testForEach() {
         final Set<String> values = new HashSet<>(EXPECTED.size());
         map.forEach((k, v) -> {values.add(v);});
@@ -92,5 +92,4 @@
             assertEquals(entry.getValue(), EXPECTED.get(entry.getKey()).toUpperCase());
         }
     }
-
 }
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/CollectionAndMapModifyStreamTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/CollectionAndMapModifyStreamTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -78,7 +78,8 @@
         collections.add(new ConcurrentSkipListSet<>(content));
 
         ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<>(content.size());
-        for (Integer i : content) arrayBlockingQueue.add(i);
+        for (Integer i : content)
+            arrayBlockingQueue.add(i);
         collections.add(arrayBlockingQueue);
         collections.add(new PriorityBlockingQueue<>(content));
         collections.add(new LinkedBlockingQueue<>(content));
@@ -145,12 +146,12 @@
         return params;
     }
 
-    @Test(dataProvider = "maps")
+    @Test(dataProvider = "maps", groups = { "serialization-hostile" })
     public void testMapKeysSizeRemove(String name, Supplier<Map<Integer, Integer>> c) {
         testCollectionSizeRemove(name + " key set", c.get().keySet());
     }
 
-    @Test(dataProvider = "maps")
+    @Test(dataProvider = "maps", groups = { "serialization-hostile" })
     public void testMapValuesSizeRemove(String name, Supplier<Map<Integer, Integer>> c) {
         testCollectionSizeRemove(name + " value set", c.get().values());
     }
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/ForEachOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/ForEachOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -44,6 +44,7 @@
 @Test
 public class ForEachOpTest extends OpTestCase {
 
+    @Test(groups = { "serialization-hostile" })
     public void testForEach() {
         exerciseTerminalOps(countTo(10),
                             s -> {
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/GroupByOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/GroupByOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -65,7 +65,9 @@
                 = Collectors.groupingBy(Functions.forPredicate(pEven, true, false));
 
         Map<Boolean, Collection<Integer>> m = collector.resultSupplier().get();
-        countTo(10).stream().forEach(e -> collector.accumulator().accept(m, e));
+        int[] ints = countTo(10).stream().map(e -> (int) e).toArray();
+        for (int i : ints)
+            collector.accumulator().accept(m, i);
 
         assertEquals(2, m.keySet().size());
         for(Collection<Integer> group : m.values()) {
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/IntSliceOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/IntSliceOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -189,6 +189,7 @@
         exerciseOps(Streams.intRange(1, 101).map(i -> 101 - i).toArray(), s -> s.limit(10).sorted());
     }
 
+    @Test(groups = { "serialization-hostile" })
     public void testLimitShortCircuit() {
         for (int l : Arrays.asList(0, 10)) {
             AtomicInteger ai = new AtomicInteger();
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/SequentialOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/SequentialOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -50,7 +50,8 @@
  */
 public class SequentialOpTest extends OpTestCase {
     @SuppressWarnings({"rawtypes", "unchecked"})
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class,
+          groups = { "serialization-hostile" })
     public void testLazy(String name, StreamTestData<Integer> data) {
         Function<Integer, Integer> id = Functions.identity();
         AtomicInteger counter = new AtomicInteger();
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/SliceOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/SliceOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -189,6 +189,7 @@
         exerciseOps(l, s -> s.limit(10).sorted(Comparators.naturalOrder()));
     }
 
+    @Test(groups = { "serialization-hostile" })
     public void testLimitShortCircuit() {
         for (int l : Arrays.asList(0, 10)) {
             AtomicInteger ai = new AtomicInteger();
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/SortedOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/SortedOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -57,6 +57,7 @@
         assertFalse(s.hasCharacteristic(Spliterator.SORTED));
     }
 
+    @Test(groups = { "serialization-hostile" })
     public void testSequentialShortCircuitTerminal() {
         // The sorted op for sequential evaluation will buffer all elements when accepting
         // then at the end sort those elements and push those elements downstream
@@ -152,6 +153,7 @@
 
     //
 
+    @Test(groups = { "serialization-hostile" })
     public void testIntSequentialShortCircuitTerminal() {
         int[] a = new int[]{5, 4, 3, 2, 1};
 
@@ -215,6 +217,7 @@
 
     //
 
+    @Test(groups = { "serialization-hostile" })
     public void testLongSequentialShortCircuitTerminal() {
         long[] a = new long[]{5, 4, 3, 2, 1};
 
@@ -278,6 +281,7 @@
 
     //
 
+    @Test(groups = { "serialization-hostile" })
     public void testDoubleSequentialShortCircuitTerminal() {
         double[] a = new double[]{5.0, 4.0, 3.0, 2.0, 1.0};
 
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -28,6 +28,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -148,7 +149,8 @@
                 fail(String.format("Class mismatch in CollectionAssertion: %s, %s", clazz, value.getClass()));
             Stream<T> stream = source.get();
             Collection<T> result = clazz.newInstance();
-            stream.forEach(result::add);
+            for (Iterator<T> it = stream.iterator(); it.hasNext(); ) // avoid capturing result::add
+                result.add(it.next());
             if (StreamOpFlagTestHelper.isStreamOrdered(stream) && targetOrdered && ordered)
                 assertContents(value, result);
             else
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/TeeOpTest.java	Tue Mar 12 14:48:50 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/TeeOpTest.java	Tue Mar 12 17:20:37 2013 -0400
@@ -41,6 +41,7 @@
 /**
  * TeeOpTest
  */
+@Test(groups = { "serialization-hostile" })
 public class TeeOpTest extends OpTestCase {
 
     public void testTee() {
@@ -83,11 +84,11 @@
         }
         final RecordingConsumer b = new RecordingConsumer();
 
-        withData(data).
-                stream(s -> s.peek(b)).
-                before(b::before).
-                after(b::after).
-                exercise();
+        withData(data)
+                .stream(s -> s.peek(b))
+                .before(b::before)
+                .after(b::after)
+                .exercise();
     }
 
     @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
@@ -99,11 +100,11 @@
         }
         final RecordingConsumer b = new RecordingConsumer();
 
-        withData(data).
-                stream(s -> s.peek(b)).
-                before(b::before).
-                after(b::after).
-                exercise();
+        withData(data)
+                .stream(s -> s.peek(b))
+                .before(b::before)
+                .after(b::after)
+                .exercise();
     }
 
     @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
@@ -115,11 +116,11 @@
         }
         final RecordingConsumer b = new RecordingConsumer();
 
-        withData(data).
-                stream(s -> s.peek(b)).
-                before(b::before).
-                after(b::after).
-                exercise();
+        withData(data)
+                .stream(s -> s.peek(b))
+                .before(b::before)
+                .after(b::after)
+                .exercise();
     }
 
     @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
@@ -131,10 +132,10 @@
         }
         final RecordingConsumer b = new RecordingConsumer();
 
-        withData(data).
-                stream(s -> s.peek(b)).
-                before(b::before).
-                after(b::after).
-                exercise();
+        withData(data)
+                .stream(s -> s.peek(b))
+                .before(b::before)
+                .after(b::after)
+                .exercise();
     }
 }