import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.lang.reflect.*;


public class JDK8175806 { 

public interface I {
    void m() throws Exception;
}

public interface IFactory {
    I get();
}

public static class A implements IFactory {
    public I get() {
        return this::clone;
    }
}

public static class Sup {
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public static class B extends Sup implements IFactory {
    public I get() {
        return this::clone;
    }
}

/** arg[0]: path to store generated classes in */
public static void main(String... args) throws Exception {
    Path classes = Paths.get(args[0]);
    if (!Files.isDirectory(classes)) {
        throw new IllegalArgumentException();
    }
    
    System.setProperty("jdk.internal.lambda.dumpProxyClasses", classes.toString());

    tryDirect(new A());
    tryDirect(new B());
    
    tryReflective(new A(), classes, "JDK8175806$A$$Lambda$1");
    tryReflective(new B(), classes, "JDK8175806$B$$Lambda$2");
} 

private static void tryDirect(IFactory f) {
    try {
        I i = f.get();
        System.out.printf("Got I directly from %s: %s%n", f, i);
    } catch (Throwable t) {
        System.out.printf("Failed to get I directly from %s%n", f); 
        t.printStackTrace();
    }
    System.out.println();
}

private static void tryReflective(IFactory f, Path classes, String lambdaClassName) {
    try {
        ClassLoader loader = new URLClassLoader(new URL[]{ classes.toUri().toURL() });
        Class<?> lambdaClass = loader.loadClass(lambdaClassName);
        Constructor<?> lambdaCons = lambdaClass.getDeclaredConstructor(f.getClass());
        lambdaCons.setAccessible(true);
        I i = (I) lambdaCons.newInstance(f);
        System.out.printf("Got I reflectively from %s via %s: %s%n", f, lambdaClassName, i);
    } catch (Throwable t) {
        System.out.printf("Failed to get I reflectively from %s via %s%n", f, lambdaClassName);
        t.printStackTrace();
    }
    System.out.println();
}

}
