/*******************************************************************************
 * Copyright (c) 2014, 2016 GoPivotal, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                          Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
 *                          Bug 439889 - [1.8][compiler] [lambda] Deserializing lambda fails with IllegalArgumentException: "Invalid lambda deserialization"
 *                          Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
 *                          Bug 442418 - $deserializeLambda$ off-by-one error when deserializing the captured arguments of a lambda that also capture this
 *                          Bug 449467 - [1.8][compiler] Invalid lambda deserialization with anonymous class
 *        Olivier Tardieu tardieu@us.ibm.com - Contributions for
 *                          Bug 442416 - $deserializeLambda$ missing cases for nested lambdas
 *                          Bug 442418 - $deserializeLambda$ off-by-one error when deserializing the captured arguments of a lambda that also capture this
 *        IBM Corporation - Additional tests
 *******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;

import java.io.File;
import java.lang.reflect.Modifier;
import java.util.Map;

import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.util.ClassFileBytesDisassembler;
import org.eclipse.jdt.core.util.IBootstrapMethodsEntry;
import org.eclipse.jdt.core.util.IClassFileAttribute;
import org.eclipse.jdt.core.util.IClassFileReader;
import org.eclipse.jdt.core.util.IConstantPool;
import org.eclipse.jdt.core.util.IConstantPoolConstant;
import org.eclipse.jdt.core.util.IConstantPoolEntry;
import org.eclipse.jdt.core.util.IConstantPoolEntry2;
import org.eclipse.jdt.core.util.IMethodInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.core.util.BootstrapMethodsAttribute;

import junit.framework.Test;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class SerializableLambdaTest extends AbstractRegressionTest {

	static {
//		TESTS_NUMBERS = new int [] { 40 };
//		TESTS_NAMES = new String[] { "testTypeVariable" };
	}
	
	public static Class testClass() {
		return SerializableLambdaTest.class;
	}
	public static Test suite() {
		return buildMinimalComplianceTestSuite(testClass(), F_1_8);
	}
	public SerializableLambdaTest(String testName){
		super(testName);
	}

	// Enables the tests to run individually
	protected Map getCompilerOptions() {
		Map defaultOptions = super.getCompilerOptions();
		defaultOptions.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_8);
		defaultOptions.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_8);
		defaultOptions.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_8);
		return defaultOptions;
	}
	
	public static final String RUNNER_CLASS = 
		"public class Y {\n"+
		"  public static void main(String[]args) {\n"+
		"    com.foo.X.main(args);\n"+
		"  }\n"+
		"}";
	
	private static final String HELPER_CLASS =
		"package util;\n"+
		"import java.io.*;\n"+
		"public class Helper {\n"+
		"public static void print(Object o ) {System.err.println(o);}\n"+
	    "static byte[][] data;\n"+
	    "\n"+
	    "public static void write(Object o) { write(0,o); }\n"+
	    "public static void write(int i, Object o) {\n"+
	    "    if (data==null) data=new byte[10][];\n"+
	    "    try {\n"+
	    "        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n"+
	    "        ObjectOutputStream oos = new ObjectOutputStream(baos);\n"+
	    "        oos.writeObject(o);\n"+
	    "        oos.flush();\n"+
	    "        oos.close();\n"+
	    "        data[i] = baos.toByteArray();\n"+
	    "    } catch (Exception e) {\n"+
	    "    }\n"+
	    "}\n"+
	    "\n"+
	    "public static Object read() { return read(0); }\n"+
	    "public static Object read(int i) {\n"+
	    "    try {\n"+
	    "        ByteArrayInputStream bais = new ByteArrayInputStream(data[i]);\n"+
	    "        ObjectInputStream ois = new ObjectInputStream(bais);\n"+
	    "        Object o = ois.readObject();\n"+
	    "        ois.close();\n"+
	    "        return o;\n"+
	    "    } catch (Exception e) {\n"+
	    "    }\n"+
	    "    return null;\n"+
	    "}\n"+
		"}\n";	

	/**
	 * Verifies that after deserializing it is usable, also that the bootstrap methods attribute indicates use of altMetafactory
	 */
	public void test001_simple() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = () -> 3;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m());\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"3",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	/**
	 * Sanity test, non serializable should have bootstrap methods attribute reference to metafactory.
	 */
	public void test002_simpleNonSerializable() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo { int m(); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = () -> 3;\n"+
					"        System.out.println(f1.m());\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"3");
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}

	/**
	 * Basic test that deserializeLambda can cope with two lambda expressions.
	 */
	public void test003_twoSerializedLambdas() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null, f2 = null;\n"+
					"        f1 = () -> 33;\n"+
					"        f2 = () -> 99;\n"+
					"        util.Helper.write(0,f1);\n"+
					"        util.Helper.write(1,f2);\n"+
					"        f2 = (Foo)util.Helper.read(1);\n"+
					"        f1 = (Foo)util.Helper.read(0);\n"+
					"        System.out.println(f1.m());\n"+
					"        System.out.println(f2.m());\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"33\n99",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n"+
				"1: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$1:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test004_lambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",
					"public class Y {\n"+
					"  public static void main(String[]args) {\n"+
					"    com.foo.X.main(args);\n"+
					"  }\n"+
					"}",
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(int i); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null, f2 = null;\n"+
					"        f1 = (i) -> i*2;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(4));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"8",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (I)I\n"+
				"    invokestatic com/foo/X.lambda$0:(I)I\n"+
				"    (I)I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test005_capturingVariableLambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",
					"public class Y {\n"+
					"  public static void main(String[]args) {\n"+
					"    com.foo.X.main(args);\n"+
					"  }\n"+
					"}",
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(int i); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        int multiplier = 3;\n"+
					"        f1 = (i) -> i * multiplier;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(4));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"12",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (I)I\n"+
				"    invokestatic com/foo/X.lambda$0:(II)I\n"+
				"    (I)I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}

	// differing types, not just int
	public void test006_capturingVariableLambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",
					"public class Y {\n"+
					"  public static void main(String[]args) {\n"+
					"    com.foo.X.main(args);\n"+
					"  }\n"+
					"}",
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(String n); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        int multiplier = 3;\n"+
					"        f1 = (n) -> Integer.valueOf(n) * multiplier;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(\"33\"));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"99",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (Ljava/lang/String;)I\n"+
				"    invokestatic com/foo/X.lambda$0:(ILjava/lang/String;)I\n"+
				"    (Ljava/lang/String;)I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	// Fails the same way as javac right now... with NPE (b120)
	public void xtest007_capturingFieldLambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",
					"public class Y {\n"+
					"  public static void main(String[]args) {\n"+
					"    com.foo.X.main(args);\n"+
					"  }\n"+
					"}",
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    int multiplier = 3;\n"+
					"    interface Foo extends Serializable { int m(int i); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"      new X().run();\n"+
					"    }\n"+
					"    public void run() {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (i) -> i * this.multiplier;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(4));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"12",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (I)I\n"+
				"    invokestatic com/foo/X.lambda$0:(II)I\n"+
				"    (I)I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test008_capturingTwoVariableLambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",
					"public class Y {\n"+
					"  public static void main(String[]args) {\n"+
					"    com.foo.X.main(args);\n"+
					"  }\n"+
					"}",
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { float m(int i, float f); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"      new X().run();\n"+
					"    }\n"+
					"    public void run() {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (i,f) -> ((float)i) * f;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(3,4.0f));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"12.0",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (IF)F\n"+
				"    invokestatic com/foo/X.lambda$0:(IF)F\n"+
				"    (IF)F\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test009_capturingTwoSlotVariablesLambdaWithParameterInPackage() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",RUNNER_CLASS,
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { double m(int i, long l); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"      new X().run();\n"+
					"    }\n"+
					"    public void run() {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (i,l) -> (double)(i*l);\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(3,40L));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"120.0",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (IJ)D\n"+
				"    invokestatic com/foo/X.lambda$0:(IJ)D\n"+
				"    (IJ)D\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test010_VarargsLambdaExpression() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",RUNNER_CLASS,
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { String m(String... ss); }\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"      new X().run();\n"+
					"    }\n"+
					"    public void run() {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (strings) -> strings[0]+strings[1];\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m(\"abc\",\"def\"));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"abcdef",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    invokestatic com/foo/X.lambda$0:([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    ([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}
	
	// Fails same way as javac right now... with an NPE (b120)
	public void xtest011_CapturingInstance() throws Exception {
		this.runConformTest(
				new String[]{
					"Y.java",RUNNER_CLASS,
					"X.java",
					"package com.foo;\n"+
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { String m(); }\n"+
					"\n"+
					"    String fieldValue = \"hello\";\n"+
					"    public static void main(String[] args) {\n"+
					"      new X().run();\n"+
					"    }\n"+
					"    public void run() {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = () -> this.fieldValue;\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m());\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"abcdef",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    invokestatic com/foo/X.lambda$0:([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    ([Ljava/lang/String;)Ljava/lang/String;\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "com"+File.separator+"foo"+File.separator+"X.class");
		checkExpected(expectedOutput,data);
	}

	public void test012_intersectionCast() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(); }\n"+
					"    interface Marker {}\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (Foo & Marker) () -> 3;\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m());\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"isMarker?true\n3\nisMarker?true",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    3\n"+ // BitFlags: 0x01 = FLAG_SERIALIZABLE 0x02 = FLAG_MARKER
				"    1\n"+ // Marker interface count
				"    X$Marker\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}

	public void test013_intersectionCast() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"interface Goo {}\n"+
					"public class X {\n"+
					"    interface Foo extends Serializable { int m(); }\n"+
					"    interface Marker {}\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (Foo & Goo & Serializable & Marker) () -> 3;\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        System.out.println(\"isGoo?\"+(f1 instanceof Goo));\n"+
					"        System.out.println(\"isSerializable?\"+(f1 instanceof Serializable));\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m());\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        System.out.println(\"isGoo?\"+(f1 instanceof Goo));\n"+
					"        System.out.println(\"isSerializable?\"+(f1 instanceof Serializable));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"isMarker?true\nisGoo?true\nisSerializable?true\n3\nisMarker?true\nisGoo?true\nisSerializable?true",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    3\n"+ // BitFlags: 0x01 = FLAG_SERIALIZABLE 0x02 = FLAG_MARKER
				"    2\n"+ // Marker interface count
				"    Goo\n"+
				"    X$Marker\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test014_intersectionCastAndNotSerializable() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"interface Goo {}\n"+
					"public class X {\n"+
					"    interface Foo { int m(); }\n"+
					"    interface Marker {}\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (Foo & Goo & Marker) () -> 3;\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        System.out.println(\"isGoo?\"+(f1 instanceof Goo));\n"+
					"        System.out.println(\"isSerializable?\"+(f1 instanceof Serializable));\n"+
					"        System.out.println(f1.m());\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"isMarker?true\nisGoo?true\nisSerializable?false\n3",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    2\n"+ // BitFlags: 0x02 = FLAG_MARKER
				"    2\n"+ // Marker interface count
				"    Goo\n"+
				"    X$Marker\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test015_serializableViaIntersectionCast() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n"+
					"interface Goo {}\n"+
					"public class X {\n"+
					"    interface Foo { int m(); }\n"+
					"    interface Marker {}\n"+
					"\n"+
					"    public static void main(String[] args) {\n"+
					"        Foo f1 = null;\n"+
					"        f1 = (Foo & Goo & Serializable & Marker) () -> 3;\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        System.out.println(\"isGoo?\"+(f1 instanceof Goo));\n"+
					"        System.out.println(\"isSerializable?\"+(f1 instanceof Serializable));\n"+
					"        util.Helper.write(f1);\n"+
					"        f1 = (Foo)util.Helper.read();\n"+
					"        System.out.println(f1.m());\n"+
					"        System.out.println(\"isMarker?\"+(f1 instanceof Marker));\n"+
					"        System.out.println(\"isGoo?\"+(f1 instanceof Goo));\n"+
					"        System.out.println(\"isSerializable?\"+(f1 instanceof Serializable));\n"+
					"    }\n"+
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"isMarker?true\nisGoo?true\nisSerializable?true\n3\nisMarker?true\nisGoo?true\nisSerializable?true",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    3\n"+ // BitFlags: 0x01 = FLAG_SERIALIZABLE 0x02 = FLAG_MARKER
				"    2\n"+ // Marker interface count
				"    Goo\n"+
				"    X$Marker\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	// SAM type not first in intersection cast
	public void test016_bug424211() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		AutoCloseable one = ((Serializable & AutoCloseable) (() -> {}));\n"+
					"		one.close();\n"+
					"	}\n"+
					"}"
					},
					"",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()V\n"+
				"    invokestatic X.lambda$0:()V\n"+
				"    ()V\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	// Now SAM type first
	public void test017_bug424211() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		AutoCloseable one = ((AutoCloseable & Serializable) (() -> {}));\n"+
					"		one.close();\n"+
					"	}\n"+
					"}"
					},
					"",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()V\n"+
				"    invokestatic X.lambda$0:()V\n"+
				"    ()V\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	// Not Serializable but a regular marker interface
	public void test018_bug424211() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Marker {}\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		AutoCloseable one = ((Marker & AutoCloseable) (() -> {}));\n"+
					"		one.close();\n"+
					"	}\n"+
					"}"
					},
					"",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()V\n"+
				"    invokestatic X.lambda$0:()V\n"+
				"    ()V\n"+
				"    2\n"+
				"    1\n"+
				"    Marker\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	// Now SAM type not first and serialization occurring
	public void test019_bug424211() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface SAM {int m();}\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		SAM one = ((Serializable & SAM) (() -> 3));\n"+
					"        System.out.println(one.m());\n"+
					"        util.Helper.write(one);\n"+
					"        one = (SAM)util.Helper.read();\n"+
					"        System.out.println(one.m());\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"3\n3",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test020_lambdaNames() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo {int m();}\n"+
					"interface FooN extends Serializable {int m();}\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		AutoCloseable one = () -> {};\n"+
					"       new X().m();\n"+
					"       one.close();\n"+
					"	}\n"+
					"   public void m() { Foo f = () -> 3; System.out.println(f.m());}\n"+
					"   public void n() { FooN f = () -> 3; System.out.println(f.m());}\n"+
					"}"
					},
					"3",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
			"  private static synthetic void lambda$0() throws java.lang.Exception;\n";
		checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput, ClassFileBytesDisassembler.SYSTEM);
		expectedOutput =
			"  private static synthetic int lambda$1();\n";
		checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput, ClassFileBytesDisassembler.SYSTEM);
	}
	
	public void test021_lambdaNamesVariants() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo {int m();}\n"+
					"interface FooSer extends Serializable {int m();}\n"+
					"interface FooI {int m(int i);}\n"+
					"interface FooSerI extends Serializable {int m(int i);}\n"+
					"public class X {\n"+
					"\n"+
					"   Foo instanceField = () -> 1;\n"+
					"   FooSer instanceFieldSer = () -> 2;\n"+
					"   static Foo staticField = () -> 3;\n"+
					"   static FooSer staticFieldSer = () -> 4;\n"+
					"   FooI instanceFieldI = (i) -> 5;\n"+
					"   FooSerI instanceFieldSerI = (i) -> 6;\n"+
					"\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"     int x = 4;\n"+
					"     Foo a = () -> 1;\n"+
					"     FooSer b = () -> 2;\n"+
					"     FooI c = (i) -> 3;\n"+
					"     FooSerI d = (i) -> 4;\n"+
					"     Foo e = () -> x;\n"+
					"     FooSer f = () -> x+1;\n"+
					"     FooI g = (i) -> x+2;\n"+
					"     FooSerI h = (i) -> x+3;\n"+
					"	}\n"+
					"}"
					},
					"",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"static lambda$0()I\n"+
				"static lambda$1()I\n"+
				"static lambda$2()I\n"+
				"static lambda$3()I\n"+
				"static lambda$4(I)I\n"+
				"static lambda$5(I)I\n"+
				"static lambda$6()I\n"+
				"static lambda$7()I\n"+
				"static lambda$8(I)I\n"+
				"static lambda$9(I)I\n"+
				"static lambda$10(I)I\n"+
				"static lambda$11(I)I\n"+
				"static lambda$12(II)I\n"+
				"static lambda$13(II)I\n";
		String actualOutput = printLambdaMethods(OUTPUT_DIR + File.separator + "X.class");
		if (!actualOutput.equals(expectedOutput)) {
			printIt(actualOutput);
			assertEquals(expectedOutput,actualOutput);
		}
	}
	
	public void test022_nestedLambdas() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo extends Serializable {int m();}\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"		Foo f = () -> { return ((Foo)()->33).m();};\n"+
					"       System.out.println(f.m());\n"+
					"       util.Helper.write(f);\n"+
					"       f = (Foo)util.Helper.read();\n"+
					"       System.out.println(f.m());\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"33\n33",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n"+
				"1: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$1:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test023_lambdasInOtherPlaces_Field() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo extends Serializable {int m();}\n"+
					"public class X {\n"+
					"   Foo f = () -> 99;\n" +
					"	public static void main(String argv[]) throws Exception {\n"+
					"     new X().run();\n"+
					"   }\n"+
					"   public void run() {\n"+
					"       System.out.println(f.m());\n"+
					"       util.Helper.write(f);\n"+
					"       f = (Foo)util.Helper.read();\n"+
					"       System.out.println(f.m());\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"99\n99",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test024_lambdasInOtherPlaces_MethodParameter() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo extends Serializable {int m();}\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"       new X().run(()->33);\n"+
					"   }\n"+
					"   public void run(Foo f) {\n"+
					"       System.out.println(f.m());\n"+
					"       util.Helper.write(f);\n"+
					"       f = (Foo)util.Helper.read();\n"+
					"       System.out.println(f.m());\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"33\n33",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	public void test025_lambdasWithGenericInferencing() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"import java.util.function.*;\n"+
					"public class X {\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"       new X().run();\n"+
					"   }\n"+
					"   public void run() {\n"+
					"       IntFunction<Integer> times3 = (IntFunction<Integer> & Serializable) (triple) -> 3 * triple;\n"+
					"       System.out.println(times3.apply(4));\n"+
					"       util.Helper.write(times3);\n"+
					"       times3 = (IntFunction<Integer>)util.Helper.read();\n"+
					"       System.out.println(times3.apply(4));\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"12\n12",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (I)Ljava/lang/Object;\n"+
				"    invokestatic X.lambda$0:(I)Ljava/lang/Integer;\n"+
				"    (I)Ljava/lang/Integer;\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	public void test026_lambdasInOtherPlaces_Clinit() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.Serializable;\n"+
					"interface Foo extends Serializable {int m();}\n"+
					"public class X {\n"+
					"   static {\n"+
					"     Foo f = () -> 99;\n" +
					"   }\n"+
					"	public static void main(String argv[]) throws Exception {\n"+
					"     new X().run();\n"+
					"   }\n"+
					"   public void run() {\n"+
					"       Foo f = ()->99;\n"+
					"       System.out.println(f.m());\n"+
					"       util.Helper.write(f);\n"+
					"       f = (Foo)util.Helper.read();\n"+
					"       System.out.println(f.m());\n"+
					"	}\n"+
					"}",
					"Helper.java",HELPER_CLASS,
					},
					"99\n99",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
		String expectedOutput =
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$0:()I\n"+
				"    ()I\n"+
				"    1\n"+
				"1: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()I\n"+
				"    invokestatic X.lambda$1:()I\n"+
				"    ()I\n"+
				"    1\n";
		String data = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "X.class");
		checkExpected(expectedOutput,data);
	}
	
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=449467 - [1.8][compiler] Invalid lambda deserialization with anonymous class
	public void test449467() throws Exception {
		this.runConformTest(
				new String[]{
					"TestClass.java",
					"import java.io.ByteArrayInputStream;\n"+
					"import java.io.ByteArrayOutputStream;\n"+
					"import java.io.ObjectInputStream;\n"+
					"import java.io.ObjectOutputStream;\n"+
					"import java.io.Serializable;\n"+
					"\n"+
					"public class TestClass implements Serializable {\n"+
					"  String msg = \"HEY!\";\n"+
					"  OtherClass other;\n"+
					"\n"+
					"  public TestClass(StringBuilder sb) {\n"+
					"    other = new OtherClass() {\n"+
					"      {\n"+
					"        other2 = new OtherClass2((Runnable & Serializable) () -> {\n"+
					"          sb.length();\n"+
					"          say();\n"+
					"        });\n"+
					"      }\n"+
					"    };\n"+
					"  }\n"+
					"\n"+
					"  public void say() {\n"+
					"    System.out.println(msg);\n"+
					"  }\n"+
					"\n"+
					"  public static void main(String[] args) throws Exception {\n"+
					"    ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"    try (ObjectOutputStream out = new ObjectOutputStream(buffer)) {\n"+
					"      out.writeObject(new TestClass(new StringBuilder()));\n"+
					"    }\n"+
					"    try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {\n"+
					"      TestClass s = (TestClass) in.readObject();\n"+
					"      s.say();\n"+
					"    }\n"+
					"  }\n"+
					"}\n"+
					"\n"+
					"class OtherClass implements Serializable {\n"+
					"  OtherClass2 other2;\n"+
					"}\n"+
					"\n"+
					"class OtherClass2 implements Serializable {\n"+
					"  Runnable runnable;\n"+
					"\n"+
					"  public OtherClass2(Runnable runnable) {\n"+
					"    this.runnable = runnable;\n"+
					"  }\n"+
					"}\n"
					},
					"HEY!",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.		
	}
	
	public void test449467_2() throws Exception {
		this.runConformTest(
				new String[]{
					"com/foo/TestClass.java",
					"package com.foo;\n"+
					"import java.io.ByteArrayInputStream;\n"+
					"import java.io.ByteArrayOutputStream;\n"+
					"import java.io.ObjectInputStream;\n"+
					"import java.io.ObjectOutputStream;\n"+
					"import java.io.Serializable;\n"+
					"public class TestClass implements Serializable {\n"+
					"  String msg = \"HEY!\";\n"+
					"  OtherClass other;\n"+
					"\n"+
					"  public TestClass(StringBuilder sb) {\n"+
					"    other = new OtherClass() {\n"+
					"      {\n"+
					"        other2 = new OtherClass2((Runnable & Serializable) () -> {\n"+
					"          sb.length();\n"+
					"          say();\n"+
					"        });\n"+
					"      }\n"+
					"    };\n"+
					"  }\n"+
					"\n"+
					"  public void say() {\n"+
					"    System.out.println(msg);\n"+
					"  }\n"+
					"\n"+
					"  public static void main(String[] args) throws Exception {\n"+
					"    ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"    try (ObjectOutputStream out = new ObjectOutputStream(buffer)) {\n"+
					"      out.writeObject(new TestClass(new StringBuilder()));\n"+
					"    }\n"+
					"    try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {\n"+
					"      TestClass s = (TestClass) in.readObject();\n"+
					"      s.say();\n"+
					"    }\n"+
					"  }\n"+
					"}\n"+
					"\n"+
					"class OtherClass implements Serializable {\n"+
					"  OtherClass2 other2;\n"+
					"}\n"+
					"\n"+
					"class OtherClass2 implements Serializable {\n"+
					"  Runnable runnable;\n"+
					"\n"+
					"  public OtherClass2(Runnable runnable) {\n"+
					"    this.runnable = runnable;\n"+
					"  }\n"+
					"}\n"
					},
					"HEY!",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.		
	}
	
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=428552,  [1.8][compiler][codegen] Serialization does not work for method references
	public void test428552() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n" +
					"public class X {\n" +
					"	interface Example extends Serializable {\n" +
					"		String convert(X o);\n" +
					"	}\n" +
					"	public static void main(String[] args) throws IOException {\n" +
					"		Example e=X::toString;\n" +
					"       util.Helper.write(e);\n"+
					"       e = (Example)util.Helper.read();\n"+
					"       System.out.println(e.convert(new X()));\n"+
					"	}\n" +
					"   public String toString() {\n" +
					"       return \"XItIs\";\n" +
					"   }\n" +
					"}\n",
					"Helper.java",HELPER_CLASS,
					},
					"XItIs",
					null,
					true,
					new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=428642
	public void test428642() throws Exception {
		this.runConformTest(
				new String[]{
					"QuickSerializedLambdaTest.java",
					"import java.io.*;\n"+
					"import java.util.function.IntConsumer;\n"+
					"\n"+
					"public class QuickSerializedLambdaTest {\n"+
					"	interface X extends IntConsumer,Serializable{}\n"+
					"	public static void main(String[] args) throws IOException, ClassNotFoundException {\n"+
					"		X x2 = System::exit; // method reference\n"+
					"		ByteArrayOutputStream debug=new ByteArrayOutputStream();\n"+
					"		try(ObjectOutputStream oo=new ObjectOutputStream(debug))\n"+
					"		{\n"+
					"			oo.writeObject(x2);\n"+
					"		}\n"+
					"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray())))\n"+
					"		{\n"+
					"			X x=(X)oi.readObject();\n"+
					"			x.accept(0);// shall exit\n"+
					"		}\n"+
					"		throw new AssertionError(\"should not reach this point\");\n"+
					"	}\n"+
					"}\n",
					"Helper.java",
					"public class Helper {\n"+
					"  public static String tostring(java.lang.invoke.SerializedLambda sl) {\n"+
					"    return sl.toString();\n"+
					"  }\n"+
					"}"
				},
				"",
				null,true,
				new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	public void test428642_2() throws Exception {
		this.runConformTest(
				new String[]{
					"Helper.java",
					"public class Helper {\n"+
					"  public static String tostring(java.lang.invoke.SerializedLambda sl) {\n"+
					"    return sl.toString();\n"+
					"  }\n"+
					"  public static void main(String[]argv) throws Exception {\n"+
					"    foo.QuickSerializedLambdaTest.main(argv);\n"+
					"  }\n"+
					"}",
					"QuickSerializedLambdaTest.java",
					"package foo;\n"+
					"import java.io.*;\n"+
					"import java.util.function.IntConsumer;\n"+
					"\n"+
					"public class QuickSerializedLambdaTest {\n"+
					"	interface X extends IntConsumer,Serializable{}\n"+
					"	public static void main(String[] args) throws IOException, ClassNotFoundException {\n"+
					"		X x1 = i -> System.out.println(i);// lambda expression\n"+
					"		X x2 = System::exit; // method reference\n"+
					"		ByteArrayOutputStream debug=new ByteArrayOutputStream();\n"+
					"		try(ObjectOutputStream oo=new ObjectOutputStream(debug))\n"+
					"		{\n"+
					"			oo.writeObject(x1);\n"+
					"			oo.writeObject(x2);\n"+
					"		}\n"+
					"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray())))\n"+
					"		{\n"+
					"			X x=(X)oi.readObject();\n"+
					"			x.accept(42);// shall print \"42\"\n"+
					"			x=(X)oi.readObject();\n"+
					"			x.accept(0);// shall exit\n"+
					"		}\n"+
					"		throw new AssertionError(\"should not reach this point\");\n"+
					"	}\n"+
					"}\n"
				},
				"42",
				null,true,
				new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=429112, [1.8][compiler] Exception when compiling Serializable array constructor reference
	public void test429112() throws Exception {
		this.runConformTest(
				new String[]{
					"X.java",
					"import java.io.*;\n" +
					"import java.util.function.IntFunction;\n" +
					"public class X {\n" +
					"  interface IF extends IntFunction<Object>, Serializable {}\n" +
					"  public static void main(String[] args) throws IOException, ClassNotFoundException {\n" +
					"    IF factory=String[]::new;\n" +
					"    Object o = factory.apply(1234);\n" +
					"		ByteArrayOutputStream debug=new ByteArrayOutputStream();\n"+
					"		try(ObjectOutputStream oo=new ObjectOutputStream(debug))\n"+
					"		{\n"+
					"			oo.writeObject(factory);\n"+
					"		}\n"+
					"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray())))\n"+
					"		{\n"+
					"			IF x = (IF)oi.readObject();\n"+
					"			Object p = x.apply(1234);\n"+
					"           System.out.println(p.getClass());\n" +
					"           String [] sa = (String []) p;\n" +
					"           System.out.println(sa.length);\n" +
					"		}\n"+
					"	}\n"+
					"}\n",
				},
				"class [Ljava.lang.String;\n" + 
				"1234",
				null,true,
				new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=439889 - [1.8][compiler] [lambda] Deserializing lambda fails with IllegalArgumentException: "Invalid lambda deserialization"
	public void test439889() throws Exception {
		this.runConformTest(
				new String[]{
					"SerializationTest.java",
					"import java.io.*;\n"+
					"\n"+
					"public class SerializationTest implements Serializable {\n"+
					"	interface SerializableRunnable extends Runnable, Serializable {\n"+
					"	}\n"+
					"\n"+
					"	SerializableRunnable runnable;\n"+
					"\n"+
					"	public SerializationTest() {\n"+
					"		final SerializationTest self = this;\n"+
					"		// runnable = () -> self.doSomething();\n"+
					"		runnable = () -> this.doSomething();\n"+ // results in this method handle: #166 invokespecial SerializationTest.lambda$0:()V
					"       }\n"+
					"\n"+
					"	public void doSomething() {\n"+
					"		System.out.println(\"Hello,world!\");\n"+
					"	}\n"+
					"\n"+
					"	public static void main(String[] args) throws Exception {\n"+
					"		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"		try (ObjectOutputStream out = new ObjectOutputStream(buffer) ) {\n"+
					"			out.writeObject(new SerializationTest());\n"+
					"		}\n"+
					"		try (ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream(buffer.toByteArray()))) {\n"+
					"			final SerializationTest s = (SerializationTest) in.readObject();\n"+
					"			s.doSomething();\n"+
					"		}\n"+
					"	}\n"+
					"}\n"
					},
					"Hello,world!",
					null,true,
					new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	public void test439889_2() throws Exception {
		this.runConformTest(
				new String[]{
					"SerializationTest.java",
					"import java.io.*;\n"+
					"\n"+
					"public class SerializationTest implements Serializable {\n"+
					"	interface SerializableRunnable extends Runnable, Serializable {\n"+
					"	}\n"+
					"\n"+
					"	SerializableRunnable runnable;\n"+
					"\n"+
					"	public SerializationTest() {\n"+
					"		final SerializationTest self = this;\n"+
					"		runnable = () -> self.doSomething();\n"+ // results in this method handle: #168 invokestatic SerializationTest.lambda$0:(LSerializationTest;)V
					"		// runnable = () -> this.doSomething();\n"+
					"       }\n"+
					"\n"+
					"	public void doSomething() {\n"+
					"		System.out.println(\"Hello,world!\");\n"+
					"	}\n"+
					"\n"+
					"	public static void main(String[] args) throws Exception {\n"+
					"		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"		try (ObjectOutputStream out = new ObjectOutputStream(buffer) ) {\n"+
					"			out.writeObject(new SerializationTest());\n"+
					"		}\n"+
					"		try (ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream(buffer.toByteArray()))) {\n"+
					"			final SerializationTest s = (SerializationTest) in.readObject();\n"+
					"			s.doSomething();\n"+
					"		}\n"+
					"	}\n"+
					"}\n"
					},
					"Hello,world!",
					null,true,
					new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	public void testNestedLambdas_442416() throws Exception {
		this.runConformTest(
				new String[]{
					"Foo.java",
					"import java.io.*;\n"+
					"public class Foo {\n"+
					"   static byte[] toSer(Object o) {\n"+
					"       try {\n"+
					"			final ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"			try (ObjectOutputStream out = new ObjectOutputStream(buffer) ) {\n"+
					"				out.writeObject(o);\n"+
					"			}\n"+
					"			return buffer.toByteArray();\n"+
					"       } catch (Exception e) {e.printStackTrace();return null;}\n"+
					"   }\n"+
					"   static Object fromSer(byte[] bs) {\n"+
					"       try {\n"+
					"			try (ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream(bs))) {\n"+
					"				final Object s = in.readObject();\n"+
					"				return s;\n"+
					"			}\n"+
					"       } catch (Exception e) {e.printStackTrace();return null;}\n"+
					"   }\n"+
					"	public static void main(String[] args) throws Exception {\n"+
					"       Runnable nested1,nested2;\n"+
					"		Runnable lambda0 = (java.io.Serializable & Runnable) () -> {\n"+
					"			Runnable lambda1 = (java.io.Serializable & Runnable) () -> {\n"+
					"				Runnable lambda2 = (java.io.Serializable & Runnable) () -> {\n"+
					"					System.out.println(\"Hello,world!\");\n"+
					"				};\n"+
					"       		byte[] bs = toSer(lambda2);\n"+
					"				Runnable r = (Runnable)fromSer(bs);\n"+
					"       		r.run();\n"+
					"			};\n"+
					"       	byte[] bs = toSer(lambda1);\n"+
					"			Runnable r = (Runnable)fromSer(bs);\n"+
					"       	r.run();\n"+
					"		};\n"+
					"       byte[] bs = toSer(lambda0);\n"+
					"		Runnable r = (Runnable)fromSer(bs);\n"+
					"       r.run();\n"+
					"	}\n"+
					"}\n",
				},
				"Hello,world!",
				null,true,
				new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}
	
	public void testBindingThis_442418() throws Exception {
		this.runConformTest(
				new String[]{
					"Foo.java",
					"import java.io.*;\n"+
					"public class Foo implements Serializable {\n"+
					"   static byte[] toSer(Object o) {\n"+
					"       try {\n"+
					"			final ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n"+
					"			try (ObjectOutputStream out = new ObjectOutputStream(buffer) ) {\n"+
					"				out.writeObject(o);\n"+
					"			}\n"+
					"			return buffer.toByteArray();\n"+
					"		} catch (Exception e) {e.printStackTrace();return null;}\n"+
					"	}\n"+
					"	static Object fromSer(byte[] bs) {\n"+
					"	try {\n"+
					"			try (ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream(bs))) {\n"+
					"				final Object s = in.readObject();\n"+
					"				return s;\n"+
					"			}\n"+
					"       } catch (Exception e) {e.printStackTrace();return null;}\n"+
					"   }\n"+
					"		void m(int i) {\n"+
					"			System.out.println(i);\n"+
					"		}\n"+
					"		void n(int i) {\n"+
					"			Runnable lambda = (java.io.Serializable & Runnable) () -> { this.m(i); };\n"+
					"			byte[] bs = toSer(lambda);\n"+
					"			Runnable r = (Runnable)fromSer(bs);\n"+
					"			r.run();\n"+
					"		}\n"+
					"	public static void main(String[] args) throws Exception {\n"+
					"		new Foo().n(42);\n"+
					"	}\n"+
					"}\n",
				},
				"42",
				null,true,
				new String[]{"-Ddummy"}); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution.
	}

	public void testbug479119() {
		this.runConformTest(
			new String[]{
				"Testbed.java",
				"import java.io.ObjectStreamClass;\n" + 
				"import java.io.Serializable;\n" + 
				"import java.lang.invoke.SerializedLambda;\n" + 
				"import java.lang.reflect.Method;\n" + 
				"import java.util.function.IntFunction;\n" + 
				"import java.util.stream.Stream;\n" + 
				"public class Testbed {\n" + 
				"	public static void main(String[] args) {\n" + 
				"		System.out.println(getMethod(Testbed::foo).equals(getMethod(Testbed::foo)));\n" + 
				"	}\n" + 
				"	private static void foo() { }\n" + 
				"	static interface MethodRef extends Runnable, Serializable { }\n" + 
				"	private static Method getMethod(MethodRef methodRef) {\n" + 
				"		try {\n" + 
				"			final Method invokeWriteReplaceMethod = ObjectStreamClass.class.getDeclaredMethod(\"invokeWriteReplace\", Object.class);\n" + 
				"			invokeWriteReplaceMethod.setAccessible(true);\n" + 
				"			final SerializedLambda l = (SerializedLambda)invokeWriteReplaceMethod.invoke(\n" + 
				"					ObjectStreamClass.lookupAny(methodRef.getClass()),\n" + 
				"					methodRef\n" + 
				"				);\n" + 
				"			System.out.println(\"Looking for \" + l.getImplClass() + \".\" + l.getImplMethodName());\n" + 
				"			final Method[] methods = Stream.of(Class.forName(l.getImplClass()).getDeclaredMethods()).\n" + 
				"				filter(m -> m.getName().equals(l.getImplMethodName())).\n" + 
				"				toArray(Method[]::new);\n" + 
				"			if(methods.length != 1) throw new AssertionError(\"TODO: check signature\");\n" + 
				"			return methods[0];\n" + 
				"		} catch(Exception e) {\n" + 
				"			throw new RuntimeException(e);\n" + 
				"		}\n" + 
				"	}\n" + 
				"}\n"
		},
		"Looking for Testbed.foo\n" +
		"Looking for Testbed.foo\n" +
		"true",
		null,true,
		new String[]{"-Ddummy"});
		
		String bootstrapEntries = printBootstrapMethodsAttribute(OUTPUT_DIR + File.separator + "Testbed.class");
		String expectedOutput = 
				"0: invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    ()V\n"+
				"    invokestatic Testbed.foo:()V\n"+
				"    ()V\n"+
				"    1\n"+
				"1: invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (Ljava/lang/Object;)Z\n"+
				"    invokestatic Testbed.lambda$0:(Ljava/lang/invoke/SerializedLambda;Ljava/lang/reflect/Method;)Z\n"+
				"    (Ljava/lang/reflect/Method;)Z\n"+
				"2: invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;\n"+
				"  Method arguments:\n"+
				"    (I)Ljava/lang/Object;\n"+
				"    invokestatic Testbed.lambda$1:(I)[Ljava/lang/reflect/Method;\n"+
				"    (I)[Ljava/lang/reflect/Method;\n";

		checkExpected(expectedOutput, bootstrapEntries);
	}

	public void testbug479119a() {
		this.runConformTest(
			new String[]{
				"Testbed.java",
				"import java.io.ObjectStreamClass;\n" + 
				"import java.io.Serializable;\n" + 
				"import java.lang.invoke.SerializedLambda;\n" + 
				"import java.lang.reflect.Constructor;\n" + 
				"import java.lang.reflect.Executable;\n" + 
				"import java.lang.reflect.Method;\n" + 
				"import java.util.function.IntFunction;\n" + 
				"import java.util.stream.Stream;\n" + 
				"public class Testbed {\n" + 
				"	public static void main(String[] args) {\n" + 
				"		System.out.println(getMethod(Testbed::foo).equals(getMethod(Testbed::foo)));\n" + 
				"		System.out.println(getMethod(new Foo()::method).equals(getMethod(new Bar()::method)));\n" + 
				"		System.out.println(getMethod(MethodRefImpl::new).equals(getMethod(MethodRefImpl::new)));\n" + 
				"	}\n" + 
				"	static class MethodRefImpl implements MethodRef {\n" + 
				"		@Override\n" + 
				"		public void run() {}\n" + 
				"	}\n" + 
				"	public static class Base {\n" + 
				"        public void method () {}\n" + 
				"    }\n" + 
				"    public static class Foo extends Base {}\n" + 
				"    public static class Bar extends Base {}\n" + 
				"	private static void foo() { }\n" + 
				"	static interface MethodRef extends Runnable, Serializable { }\n" + 
				"	private static Executable getMethod(MethodRef methodRef) {\n" + 
				"		try {\n" + 
				"			final Method invokeWriteReplaceMethod = ObjectStreamClass.class.getDeclaredMethod(\"invokeWriteReplace\", Object.class);\n" + 
				"			invokeWriteReplaceMethod.setAccessible(true);\n" + 
				"			final SerializedLambda l = (SerializedLambda)invokeWriteReplaceMethod.invoke(\n" + 
				"					ObjectStreamClass.lookupAny(methodRef.getClass()),\n" + 
				"					methodRef\n" + 
				"				);\n" + 
				"			System.out.println(\"Looking for \" + l.getImplClass() + \".\" + l.getImplMethodName());\n" + 
				"			boolean isConstructor = l.getImplMethodName().indexOf(\"<init>\") >= 0;\n" + 
				"			final Executable[] methods = Stream.of(isConstructor ? Class.forName(l.getImplClass()).getDeclaredConstructors() : Class.forName(l.getImplClass()).getDeclaredMethods()).\n" + 
				"				filter(m -> m.getName().equals(isConstructor ? l.getImplClass() : l.getImplMethodName())).\n" + 
				"				toArray(isConstructor ? Constructor[]::new : Method[]::new);\n" + 
				"			if(methods.length != 1) throw new AssertionError(\"TODO: check signature\");\n" + 
				"			return methods[0];\n" + 
				"		} catch(Exception e) {\n" + 
				"			throw new RuntimeException(e);\n" + 
				"		}\n" + 
				"	}\n" + 
				"}\n"
		},
		"Looking for Testbed.foo\n" +
		"Looking for Testbed.foo\n" +
		"true\n" +
		"Looking for Testbed$Base.method\n" +
		"Looking for Testbed$Base.method\n" +
		"true\n" +
		"Looking for Testbed$MethodRefImpl.<init>\n" +
		"Looking for Testbed$MethodRefImpl.<init>\n" +
		"true",
		null,true,
		new String[]{"-Ddummy"});
	}

	// Serializable reference expressions that share the same name
	public void testbug479119b() {
		this.runConformTest(
			new String[]{
				"X.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.IOException;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"public class X {\n" + 
				"    public static interface Consumer<T> extends Serializable {\n" + 
				"        void accept(T t);\n" + 
				"    }\n" + 
				"    public static class Foo {\n" + 
				"    	public void method () {\n" + 
				"        	System.out.println(\"Foo\");\n" + 
				"        }\n" + 
				"    }\n" + 
				"    public static class Bar {\n" + 
				"    	public void method () {\n" + 
				"        	System.out.println(\"Bar\");\n" + 
				"        }\n" + 
				"    }\n" + 
				"    public static void main (String[] args) throws IOException, ClassNotFoundException {\n" + 
				"        Consumer<Foo> foo = Foo::method;\n" + 
				"        Consumer<Bar> bar = Bar::method;\n" + 
				"        Consumer<Foo> baz = (b) -> {b.method();};\n" + 
				"        ByteArrayOutputStream debug=new ByteArrayOutputStream();\n" + 
				"		try(ObjectOutputStream oo=new ObjectOutputStream(debug)) {\n" + 
				"			oo.writeObject(bar);\n" + 
				"		}\n" + 
				"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray()))) {\n" + 
				"			Consumer<Bar> x = (Consumer)oi.readObject();\n" + 
				"			x.accept(new Bar());\n" + 
				"		}\n" + 
				"		debug.reset();\n" + 
				"		try(ObjectOutputStream oo=new ObjectOutputStream(debug)) {\n" + 
				"			oo.writeObject(foo);\n" + 
				"		}\n" + 
				"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray()))) {\n" + 
				"			Consumer<Foo> x = (Consumer)oi.readObject();\n" + 
				"			x.accept(new Foo());\n" + 
				"		}\n" + 
				"    }\n" + 
				"}\n"
		},
		"Bar\n" +
		"Foo",
		null,true,
		new String[]{"-Ddummy"});
	}
	public void testbug479119_comment20() {
		this.runConformTest(
			new String[]{
				"Testbed.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.IOException;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"interface FI extends Serializable{\n" + 
				"	void run(Testbed args);\n" + 
				"}\n" + 
				"interface IF extends Serializable{\n" + 
				"	void run();\n" + 
				"}\n" + 
				"public class Testbed implements Serializable{\n" + 
				"	String f;\n" + 
				"	Testbed(String str) {\n" + 
				"		f = str;\n" + 
				"	}\n" + 
				"	void test() throws IOException, ClassNotFoundException {\n" + 
				"		accept(Testbed::foo);\n" + 
				"		accept(this::foo);		\n" + 
				"	}\n" + 
				"	void foo() {\n" + 
				"		System.out.println(this.f);\n" + 
				"	}\n" + 
				"	void accept(FI fi) {\n" + 
				"		fi.run(this);\n" + 
				"	}\n" + 
				"	void accept(IF i) {\n" + 
				"		i.run();\n" + 
				"	}\n" + 
				"	public static void main(String[] args) throws ClassNotFoundException, IOException {\n" + 
				"		Testbed t = new Testbed(\"IF\");\n" + 
				"		Testbed t2 = new Testbed(\"FI\");\n" + 
				"		IF i = t::foo;\n" + 
				"		FI f = Testbed::foo;\n" + 
				"		ByteArrayOutputStream debug=new ByteArrayOutputStream();\n" + 
				"		try(ObjectOutputStream oo=new ObjectOutputStream(debug))\n" + 
				"		{\n" + 
				"			oo.writeObject(i);\n" + 
				"		}\n" + 
				"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray())))\n" + 
				"		{\n" + 
				"			IF x = (IF)oi.readObject();\n" + 
				"			t.accept(x);\n" + 
				"		}\n" + 
				"		debug=new ByteArrayOutputStream();\n" + 
				"		try(ObjectOutputStream oo=new ObjectOutputStream(debug))\n" + 
				"		{\n" + 
				"			oo.writeObject(f);\n" + 
				"		}\n" + 
				"		try(ObjectInputStream oi=new ObjectInputStream(new ByteArrayInputStream(debug.toByteArray())))\n" + 
				"		{\n" + 
				"			FI x = (FI)oi.readObject();\n" + 
				"			t2.accept(x);\n" + 
				"		}\n" + 
				"	}\n" + 
				"}"
		},
		"IF\n" +
		"FI",
		null,true,
		new String[]{"-Ddummy"});
	}
	public void testbug485333() {
		this.runConformTest(
			new String[]{
				"Test.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"interface Func<T> extends Serializable {\n" + 
				"  T get();\n" + 
				"}\n" + 
				"class Impl implements Serializable {\n" + 
				"  int val = 0;\n" + 
				"  public int next() {\n" + 
				"    val += 1;\n" + 
				"    return val;\n" + 
				"  }\n" + 
				"}\n" + 
				"public class Test {\n" + 
				"  final Impl impl = new Impl();\n" + 
				"  final Func<Integer> func = (Func<Integer> & Cloneable)impl::next;\n" + 
				"  public void test() throws Throwable {\n" + 
				"    byte[] bytes = write(func);//25\n" + 
				"    Func<Integer> func = read(bytes);\n" + 
				"    System.out.println(func.get());\n" + 
				"  }\n" + 
				"  public static void main(String[] args) throws Throwable {\n" + 
				"	new Test().test();\n" + 
				"}\n" + 
				"  @SuppressWarnings(\"unchecked\")\n" + 
				"  private static Func<Integer> read(byte[] bytes) throws Exception {\n" + 
				"    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);\n" + 
				"    try (ObjectInputStream ois = new ObjectInputStream(bis)) {\n" + 
				"      return (Func<Integer>) ois.readObject();\n" + 
				"    }\n" + 
				"  }\n" + 
				"  private static byte[] write(Func<Integer> func) throws Exception {\n" + 
				"    ByteArrayOutputStream bos = new ByteArrayOutputStream();\n" + 
				"    System.out.println(func.get());\n" + 
				"    try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {\n" + 
				"      oos.writeObject(func);//42\n" + 
				"    }\n" + 
				"    return bos.toByteArray();\n" + 
				"  }\n" + 
				"}"
		},
		"1\n" +
		"2",
		null,true,
		new String[]{"-Ddummy"});
	}
	public void testbug494487() {
		Map options = getCompilerOptions();
		options.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE);
		this.runConformTest(
			new String[]{
				"Test.java",
				"import java.io.IOException;\n" + 
				"import java.io.Serializable;\n" + 
				"public class Test {\n" + 
				"  class AnException extends Exception {\n" + 
				"  }\n" + 
				"  class Asd {\n" + 
				"    public Asd(String asd) { data = asd; }\n" + 
				"    private final String data;\n" + 
				"    @Override\n" + 
				"    public String toString() {\n" + 
				"      return data;\n" + 
				"    }\n" + 
				"  }\n" + 
				"  public interface Test1 extends Serializable {\n" + 
				"    void test() throws IOException;\n" + 
				"  }\n" + 
				"  public interface Test2 {\n" + 
				"    void test() throws AnException;\n" + 
				"  }\n" + 
				"  public void test1( Test1 test ) {\n" + 
				"    try {\n" + 
				"      test.test();\n" + 
				"    } catch( IOException e ) {\n" + 
				"      e.printStackTrace();\n" + 
				"    }\n" + 
				"  }\n" + 
				"  public void test2( Test2 test ) {\n" + 
				"    try {\n" + 
				"      test.test();\n" + 
				"    } catch( AnException e ) {\n" + 
				"      System.out.println( e );\n" + 
				"    }\n" + 
				"  }\n" + 
				"  public void lambdas() {\n" + 
				"    test1( () -> System.out.println( \"test a\" ) );\n" + 
				"    test1( () -> System.out.println( \"test b\" ) );\n" + 
				"    test2( () -> System.out.println( \"test c\" ) );\n" + 
				"    test2( () -> System.out.println( \"test d\" ) );\n" + 
				"  }\n" + 
				"  public void print( CharSequence a, String b, long c ) {\n" + 
				"    System.out.println( a );\n" + 
				"    System.out.println( b );\n" + 
				"    System.out.println( c );\n" + 
				"  }\n" + 
				"  public void filler() {\n" + 
				"    System.out.println( \"Now we need to get this class file closer to 3000 bytes boundary\" );\n" + 
				"    filler1();\n" + 
				"    filler2();\n" + 
				"    filler3();\n" + 
				"    filler4();\n" + 
				"    filler5();\n" + 
				"    filler6();\n" + 
				"    filler7();\n" + 
				"    filler8();\n" + 
				"    filler9();\n" + 
				"    filler10();\n" + 
				"    filler11();\n" + 
				"    filler12();\n" + 
				"    filler13();\n" + 
				"    filler14();\n" + 
				"    filler15();\n" + 
				"    filler16();\n" + 
				"    filler17();\n" + 
				"    filler18();\n" + 
				"    filler19();\n" + 
				"    filler20();\n" + 
				"    filler21();\n" + 
				"    filler22();\n" + 
				"    filler23();\n" + 
				"    filler24();\n" + 
				"    filler25();\n" + 
				"    filler26();\n" + 
				"    filler27();\n" + 
				"    filler28();\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler28() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler27() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler26() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler25() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler24() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler23() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler22() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler21() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler20() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler19() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler18() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler17() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler16() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler15() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler14() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler13() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler12() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler11() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler10() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler9() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler8() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler7() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler6() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler5() {\n" + 
				"    print( c.toString(), d.toString(), System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler4() {\n" + 
				"    print( a.toString(), b.toString(), System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler3() {\n" + 
				"    print( \"a\", System.getenv( \"asd\" ), System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler2() {\n" + 
				"    print( \"a\", System.lineSeparator(), System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private void filler1() {\n" + 
				"    print( \"a\", \"b\", System.currentTimeMillis() );\n" + 
				"  }\n" + 
				"  private final Asd a = new Asd(\"a\");\n" + 
				"  private final Asd b = new Asd(\"b\");\n" + 
				"  private final Asd c = new Asd(\"c\");\n" + 
				"  private final Asd d = new Asd(\"d\");\n" + 
				"}\n"
		},
		options);
	}
	public void testbug497879() {
		this.runConformTest(
			new String[]{
				"LambdaSerializationTest.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.IOException;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"import java.util.ArrayList;\n" + 
				"import java.util.List;\n" + 
				"import java.util.function.Supplier;\n" + 
				"public class LambdaSerializationTest {\n" + 
				"    interface SerializableSupplier<T> extends Supplier<T>, Serializable {}\n" + 
				"    public static void constructorReferenceSerialization() throws IOException, ClassNotFoundException {\n" + 
				"        SerializableSupplier<List<?>> function = ArrayList::new; //Collections::emptyList;\n" + 
				"        Object result = serializeDeserialize(function);\n" + 
				"        Class<?>[] infs = result.getClass().getInterfaces();\n" +
				"        for(int i = 0; i < infs.length; i++) {\n" +
				"            System.out.println(infs[i]);\n" +
				"        }\n" +
				"    }\n" + 
				"    private static Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException {\n" + 
				"        try (\n" + 
				"            ByteArrayOutputStream buffer = new ByteArrayOutputStream(); //\n" + 
				"            ObjectOutputStream output = new ObjectOutputStream(buffer)) {\n" + 
				"            output.writeObject(obj);\n" + 
				"            try (ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {\n" + 
				"                return input.readObject();\n" + 
				"            }\n" + 
				"        }\n" + 
				"    }\n" + 
				"    public static void main(String[] args) {\n" + 
				"		try {\n" + 
				"			LambdaSerializationTest.constructorReferenceSerialization();\n" + 
				"		} catch (ClassNotFoundException | IOException e) {\n" + 
				"			// TODO Auto-generated catch block\n" + 
				"			e.printStackTrace();\n" + 
				"		}\n" + 
				"	}\n" + 
				"}"
		},
		"interface LambdaSerializationTest$SerializableSupplier",
		null,true,
		new String[]{"-Ddummy"});
	}
	public void testbug497879a() {
		this.runConformTest(
			new String[]{
				"LambdaSerializationTest.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.IOException;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"import java.util.ArrayList;\n" + 
				"import java.util.List;\n" + 
				"import java.util.function.Supplier;\n" + 
				"public class LambdaSerializationTest {\n" + 
				"    interface SerializableSupplier<T> extends Supplier<T>, Serializable {}\n" + 
				"    static class Junk {\n" + 
				"    	private Junk() {}\n" + 
				"    }\n" + 
				"    public static void constructorReferenceSerialization() throws IOException, ClassNotFoundException {\n" + 
				"        SerializableSupplier<Junk> function = Junk::new;\n" + 
				"        Object result = serializeDeserialize(function);\n" + 
				"        Class<?>[] infs = result.getClass().getInterfaces();\n" +
				"        for(int i = 0; i < infs.length; i++) {\n" +
				"            System.out.println(infs[i]);\n" +
				"        }\n" + 
				"    }\n" + 
				"    private static Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException {\n" + 
				"        try (\n" + 
				"            ByteArrayOutputStream buffer = new ByteArrayOutputStream(); //\n" + 
				"            ObjectOutputStream output = new ObjectOutputStream(buffer)) {\n" + 
				"            output.writeObject(obj);\n" + 
				"            try (ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {\n" + 
				"                return input.readObject();\n" + 
				"            }\n" + 
				"        }\n" + 
				"    }\n" + 
				"    public static void main(String[] args) {\n" + 
				"		try {\n" + 
				"			LambdaSerializationTest.constructorReferenceSerialization();\n" + 
				"		} catch (ClassNotFoundException | IOException e) {\n" + 
				"			// TODO Auto-generated catch block\n" + 
				"			e.printStackTrace();\n" + 
				"		}\n" + 
				"	}\n" + 
				"}"
		},
		"interface LambdaSerializationTest$SerializableSupplier",
		null,true,
		new String[]{"-Ddummy"});
	}
	public void testbug497879b() {
		this.runConformTest(
			new String[]{
				"LambdaSerializationTest.java",
				"import java.io.ByteArrayInputStream;\n" + 
				"import java.io.ByteArrayOutputStream;\n" + 
				"import java.io.IOException;\n" + 
				"import java.io.ObjectInputStream;\n" + 
				"import java.io.ObjectOutputStream;\n" + 
				"import java.io.Serializable;\n" + 
				"import java.util.ArrayList;\n" + 
				"import java.util.List;\n" + 
				"import java.util.function.Supplier;\n" + 
				"public class LambdaSerializationTest {\n" + 
				"    interface SerializableSupplier<T> extends Serializable {\n" +
				"        T get(int count);\n" +
				"    }\n" + 
				"    public static void constructorReferenceSerialization() throws IOException, ClassNotFoundException {\n" + 
				"        SerializableSupplier<List[]> function = ArrayList[]::new;\n" + 
				"        Object result = serializeDeserialize(function);\n" + 
				"        Class<?>[] infs = result.getClass().getInterfaces();\n" +
				"        for(int i = 0; i < infs.length; i++) {\n" +
				"            System.out.println(infs[i]);\n" +
				"        }\n" + 
				"    }\n" + 
				"    private static Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException {\n" + 
				"        try (\n" + 
				"            ByteArrayOutputStream buffer = new ByteArrayOutputStream(); //\n" + 
				"            ObjectOutputStream output = new ObjectOutputStream(buffer)) {\n" + 
				"            output.writeObject(obj);\n" + 
				"            try (ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {\n" + 
				"                return input.readObject();\n" + 
				"            }\n" + 
				"        }\n" + 
				"    }\n" + 
				"    public static void main(String[] args) {\n" + 
				"		try {\n" + 
				"			LambdaSerializationTest.constructorReferenceSerialization();\n" + 
				"		} catch (ClassNotFoundException | IOException e) {\n" + 
				"			// TODO Auto-generated catch block\n" + 
				"			e.printStackTrace();\n" + 
				"		}\n" + 
				"	}\n" + 
				"}"
		},
		"interface LambdaSerializationTest$SerializableSupplier",
		null,true,
		new String[]{"-Ddummy"});
	}
	// ---
	
	private void checkExpected(String expected, String actual) {
		if (!expected.equals(actual)) {
			printIt(actual);
		}
		assertEquals(expected,actual);
	}
	
	/**
	 * Print a piece of text with the necessary extra quotes and newlines so that it can be cut/pasted into
	 * the test source file.
	 */
	private void printIt(String text) {
		String quotedText = text;
		if (!quotedText.startsWith("\"")) {
			quotedText = "\""+quotedText.replaceAll("\n", "\\\\n\"+\n\"");
			quotedText = quotedText.substring(0,quotedText.length()-3);
		}
		System.out.println(quotedText);
	}
	
	/**
	 * Print the bootstrap methods attribute in a very similar fashion to javap for checking.
	 * Unlike javap the constant pool indexes are not included, to make the test a little less
	 * fragile.
	 */
	private String printBootstrapMethodsAttribute(String filepath) {
		IClassFileReader cfr = ToolFactory.createDefaultClassFileReader(filepath, IClassFileReader.CLASSFILE_ATTRIBUTES);
		BootstrapMethodsAttribute bootstrapMethodsAttribute = null;
		IClassFileAttribute[] attrs = cfr.getAttributes();
		for (int i=0,max=attrs.length;i<max;i++) {
			if (new String(attrs[i].getAttributeName()).equals("BootstrapMethods")) {
				bootstrapMethodsAttribute = (BootstrapMethodsAttribute)attrs[i];
			}
		}
		if (bootstrapMethodsAttribute==null) {
			return "";
		}
		IConstantPool cp = cfr.getConstantPool();
		StringBuffer sb = new StringBuffer();
		int bmaLength = bootstrapMethodsAttribute.getBootstrapMethodsLength();
		for (int i=0;i<bmaLength;i++) {
			IBootstrapMethodsEntry entry = bootstrapMethodsAttribute.getBootstrapMethods()[i];
			int mr = entry.getBootstrapMethodReference();
			IConstantPoolEntry2 icpe = (IConstantPoolEntry2)cfr.getConstantPool().decodeEntry(mr);
			
			sb.append(i).append(": ").append(formatReferenceKind(icpe.getReferenceKind()));
			sb.append(" ").append(format(cp,icpe.getReferenceIndex()));	
			sb.append("\n");
			int[] args = entry.getBootstrapArguments();
			sb.append("  Method arguments:\n");
			for (int a=0;a<args.length;a++) {
				sb.append("    ").append(format(cp,args[a])).append("\n");
			}
		}
		return sb.toString();
	}
	
	private String printLambdaMethods(String filepath) {
		IClassFileReader cfr = ToolFactory.createDefaultClassFileReader(filepath, IClassFileReader.METHOD_INFOS);
		IMethodInfo[] methodInfos = cfr.getMethodInfos();
		StringBuffer buf = new StringBuffer();
		for (int i = 0, max = methodInfos.length; i < max; i++) {
			IMethodInfo methodInfo = methodInfos[i];
			if (!new String(methodInfo.getName()).startsWith("lambda")) 
				continue;
			int accessFlags = methodInfo.getAccessFlags();
			if (Modifier.isStatic(accessFlags)) {
				buf.append("static ");
			}
			buf.append(methodInfo.getName());
			buf.append(methodInfo.getDescriptor());
			buf.append("\n");
		}
		return buf.toString();
	}
	
	String formatReferenceKind(int kind) {
		switch (kind) {
			case IConstantPoolConstant.METHOD_TYPE_REF_InvokeStatic:
				return "invokestatic";
			default:
				throw new IllegalStateException("nyi for "+kind);
		}
	}
	
	String format(IConstantPool cp, int entryNumber) {
		IConstantPoolEntry entry = cp.decodeEntry(entryNumber);
		if (entry == null) {
			return "null";
		}
		switch (entry.getKind()) {
			case IConstantPoolConstant.CONSTANT_Integer:
				return Integer.toString(entry.getIntegerValue());
			case IConstantPoolConstant.CONSTANT_Utf8:
				return new String(entry.getUtf8Value());
			case IConstantPoolConstant.CONSTANT_Methodref:
				return new String(entry.getClassName())+"."+new String(entry.getMethodName())+":"+new String(entry.getMethodDescriptor());
			case IConstantPoolConstant.CONSTANT_MethodHandle:
				IConstantPoolEntry2 entry2 = (IConstantPoolEntry2)entry;
				return formatReferenceKind(entry2.getReferenceKind())+" "+format(cp,entry2.getReferenceIndex());
			case IConstantPoolConstant.CONSTANT_MethodType:
				return format(cp,((IConstantPoolEntry2)entry).getDescriptorIndex());
			case IConstantPoolConstant.CONSTANT_Class:
				return new String(entry.getClassInfoName());
			default:
					throw new IllegalStateException("nyi for "+entry.getKind());
		}
	}
	
}

