changeset 1249:210edd66b011

Add SAM conversion tests to the methodReference tests Reviewed-by: mcimadamore Contributed-by: sue.wei@oracle.com
author jjh
date Wed, 25 Jan 2012 15:11:03 -0800
parents d46f8917fab1
children e4a613c3894f
files test/tools/javac/lambda/sqe/methodReference/BridgeMethod.java test/tools/javac/lambda/sqe/methodReference/SamConversion.java test/tools/javac/lambda/sqe/methodReference/SamConversionComboTest.java
diffstat 3 files changed, 626 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/test/tools/javac/lambda/sqe/methodReference/BridgeMethod.java	Fri Jan 13 12:44:15 2012 -0800
+++ b/test/tools/javac/lambda/sqe/methodReference/BridgeMethod.java	Wed Jan 25 15:11:03 2012 -0800
@@ -22,21 +22,18 @@
  */
 
 /**
- * @ignore
  * @test
  * @summary  Test bridge methods in certain SAM conversion
- *     (the test is too fragile as it relies on the shape of the generated class)
  * @compile BridgeMethod.java
  * @run main BridgeMethod
  */
 
-////   -XDuseProxy must be removed after 7054590 is fixed.
-
 import java.lang.reflect.Method;
 
 public class BridgeMethod {
 
     interface H {Object m();}
+
     interface K<T> {void m(T t);}
 
     interface L extends K<String> {} //generic substitution
@@ -61,32 +58,49 @@
     }
 
     public static void main(String[] args) {
-        L la = BridgeMethod#bar(String); //static reference to bar(String)
+        L la = BridgeMethod#bar; //static reference
         la.m("hi");
         Class<? extends L> c1 = la.getClass();
         Method[] methods = c1.getDeclaredMethods();
+        String parameters = "";
         System.out.println("methods in SAM conversion of L:");
-        for(Method m : methods) //Error: method void m(String) not listed
+        for(Method m : methods) {
             System.out.println(m.toGenericString());
-        assertTrue( methods.length == 2+3 ); //will be uncommented when the Proxy API problem fixed: CR 7067623
+            assertTrue(m.getName().equals("m"));
+            Class[] parameterTypes = m.getParameterTypes();
+            assertTrue(parameterTypes.length == 1);
+            parameters += parameterTypes[0].getName() + " ";
+        }
+        assertTrue(parameters.equals("java.lang.String java.lang.Object "));
 
-        KM km = BridgeMethod#bar(String);
+        KM km = BridgeMethod#bar;
         //km.m("hi"); //will be uncommented when CR7028808 fixed
         Class<? extends KM> c2 = km.getClass();
         methods = c2.getDeclaredMethods();
+        parameters = "";
         System.out.println("methods in SAM conversion of KM:");
-        for(Method m : methods)
+        for(Method m : methods) {
             System.out.println(m.toGenericString());
-        assertTrue( methods.length == 2+3 );
+            assertTrue(m.getName().equals("m"));
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            assertTrue(parameterTypes.length == 1);
+            parameters += parameterTypes[0].getName() + " ";
+        }
+        assertTrue(parameters.equals("java.lang.String java.lang.Object "));
 
-        N n = new BridgeMethod()#moo; //instance reference to moo()
+        N n = new BridgeMethod()#moo; //instance reference
         assertTrue( n.m().equals("moo") );
         assertTrue( ((H)n).m().equals("moo") );
         Class<? extends N> c3 = n.getClass();
         methods = c3.getDeclaredMethods();
+        String returnTypes = "";
         System.out.println("methods in SAM conversion of N:");
-        for(Method m : methods)
+        for(Method m : methods) {
             System.out.println(m.toGenericString());
-        assertTrue( methods.length == 2+3 );
+            assertTrue(m.getName().equals("m"));
+            Class<?> returnType = m.getReturnType();
+            returnTypes += returnType.getName() + " ";
+        }
+        assertTrue(returnTypes.equals("java.lang.String java.lang.Object "));
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/sqe/methodReference/SamConversion.java	Wed Jan 25 15:11:03 2012 -0800
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @summary  Test SAM conversion of method references in contexts of assignment, method/constructor argument,
+ *           return statement, array initializer, lambda expression body, conditional expression and cast.
+ * @compile SamConversion.java
+ * @run main SamConversion
+ */
+
+public class SamConversion {
+    
+    static int assertionCount = 0;
+
+    static interface Foo {
+        Integer m(int i);
+    }
+
+    static interface Bar {
+        int m(Integer i) throws MyException;
+    }
+
+    private static void assertTrue(boolean b) {
+        assertionCount++;
+        if(!b)
+            throw new AssertionError();
+    }
+
+    private static int test1(Foo foo) {
+        return foo.m(1);
+    }
+
+    private static void test2(Bar bar, int result) {
+        try {
+            assertTrue(bar.m(1) == result);
+        } catch (Exception e){
+            assertTrue(false);
+        }
+    }
+
+    private static Bar test3(int i) {
+        switch (i) {
+        case 0:
+            return A#method1;
+        case 1:
+            return new A()#method2;
+        case 2:
+            return A#method3;
+        case 3:
+            return new A()#method4;
+        case 4:
+            return new A()#method5;
+        case 5:
+            return A#method6;
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * Test SAM conversion of method reference in assignment context
+     */
+    private static void testAssignment() {
+        Foo foo = A#method1; //static reference, parameter type matching and return type matching
+        assertTrue(foo.m(1) == 2);
+
+        foo = new A()#method2; //instance reference, parameter type unboxing and return type boxing
+        assertTrue(foo.m(1) == 3);
+
+        foo = A#method3; //static reference, parameter type matching and return type boxing
+        assertTrue(foo.m(1) == 4);
+
+        foo = new A()#method4; //instance reference, parameter type unboxing and return type matching
+        assertTrue(foo.m(1) == 5);
+
+        foo = new A()#method5; //instance reference, parameter type unboxing and return type matching
+        assertTrue(foo.m(1) == 6);
+
+        Bar bar = A#method1;
+        try {
+            assertTrue(bar.m(1) == 2);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+
+        bar = new A()#method2;
+        try {
+            assertTrue(bar.m(1) == 3);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+
+        bar = A#method3;
+        try {
+            assertTrue(bar.m(1) == 4);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+
+        bar = new A()#method4;
+        try {
+            assertTrue(bar.m(1) == 5);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+
+        bar = new A()#method5;
+        try {
+            assertTrue(bar.m(1) == 6);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+
+        bar = new A()#method6;
+        try {
+            bar.m(1);
+            assertTrue(false);
+        } catch (MyException e) {
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+    }
+
+    /**
+     * Test SAM conversion of method reference in method/constructor argument context
+     */
+    private static void testMethodArgument() {
+        assertTrue(test1(A#method1) == 2);
+        assertTrue(test1(new A()#method2) == 3);
+        assertTrue(test1(A#method3) == 4);
+        assertTrue(test1(new A()#method4) == 5);
+        assertTrue(test1(new A()#method5) == 6);
+        test2(A#method1, 2);
+        test2(new A()#method2, 3);
+        test2(A#method3, 4);
+        test2(new A()#method4, 5);
+        test2(new A()#method5, 6);
+        A a = new A(A#method1); //A(Foo f) called
+        assertTrue(a.method2(1) == 11);
+        assertTrue(a.method4(1) == 11);
+        assertTrue(a.method5(1) == 11);
+        A a2 = new A(new A()#method2); //A(Bar b) called
+        assertTrue(a2.method2(1) == 12);
+        assertTrue(a2.method4(1) == 12);
+        assertTrue(a2.method5(1) == 12);
+    }
+
+    /**
+     * Test SAM conversion of method reference in return statement context
+     */
+    private static void testReturnStatement() {
+        Bar bar = test3(0);
+        try {
+            assertTrue(bar.m(1) == 2);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+        bar = test3(1);
+        try {
+            assertTrue(bar.m(1) == 3);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+        bar = test3(2);
+        try {
+            assertTrue(bar.m(1) == 4);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+        bar = test3(3);
+        try {
+            assertTrue(bar.m(1) == 5);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+        bar = test3(4);
+        try {
+            assertTrue(bar.m(1) == 6);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+        bar = test3(5);
+        try {
+            bar.m(1);
+            assertTrue(false);
+        } catch (MyException e) {
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+    }
+
+    /**
+     * Test SAM conversion of method reference in cast context
+     */
+    private static void testCast() {
+        assertTrue(((Foo)A#method1).m(1) == 2);
+        try {
+            assertTrue(((Bar)new A()#method2).m(1) == 3);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+    }
+
+    /**
+     * Test SAM conversion of method reference in array initializer context
+     */
+    private static void testArrayInitializer() {
+        Object[] oarray = {"a", 1, (Foo)A#method3}; //last element need a cast
+        Object[] oarray2 = {"a", 1, (Bar)new A()#method4}; //last element need a cast
+        Foo[] farray = {A#method1, new A()#method2, A#method3, new A()#method4, new A()#method5};
+        Bar[] barray = {A#method1, new A()#method2, A#method3, new A()#method4, new A()#method5, A#method6};
+    }
+
+    /**
+     * Test SAM conversion of method reference in conditional expression context
+     */
+    private static void testConditionalExpression(boolean b) {
+        Foo f = b ? A#method3 : new A()#method5;
+        if(b)
+            assertTrue(f.m(1) == 4);
+        else
+            assertTrue(f.m(1) == 6);
+
+        Bar bar = b ? A#method1 : A#method6;
+        if(b) {
+            try {
+                assertTrue(bar.m(1) == 2);
+            } catch (Exception e) {
+                assertTrue(false);
+            }
+        }
+        else {
+            try {
+                bar.m(1);
+                assertTrue(false);
+            } catch (MyException e) {
+            } catch (Exception e) {
+                assertTrue(false);
+            }
+        }
+    }
+
+    /**
+     * Test SAM conversion of method reference in lambda expression body
+     */
+    private static void testLambdaExpressionBody() {
+        Foo f = n -> ((Foo)A#method3).m(n);
+        assertTrue(f.m(1) == 4);
+
+        Bar b = n -> { return ((Foo)new A()#method5).m(n); };
+        try {
+            assertTrue(b.m(1) == 6);
+        } catch (Exception e) {
+            assertTrue(false);
+        }
+    }
+
+    public static void main(String[] args) {
+        testAssignment();
+        testMethodArgument();
+        testReturnStatement();
+        testCast();
+        testArrayInitializer();
+        testConditionalExpression(true);
+        testConditionalExpression(false);
+        testLambdaExpressionBody();
+        
+        assertTrue(assertionCount == 38);
+    }
+    
+    static class MyException extends Exception {}
+
+    static class A {
+
+        int value = 0;
+
+        A() {
+        }
+
+        A(Foo f) {
+            value = f.m(9);
+        }
+
+        A(Bar b) {
+            try {
+                value = b.m(9);
+            } catch (MyException e){}
+        }
+
+        static Integer method1(int n) {
+            return n + 1;
+        }
+
+        int method2(Integer n) {
+            return value == 0 ? n + 2 : n + value;
+        }
+
+        static int method3(int n) {
+            return n + 3;
+        }
+
+        Integer method4(Integer n) {
+            return value == 0 ? n + 4 : n + value;
+        }
+
+        Integer method5(Integer n) {
+            return value == 0 ? new Integer(n + 5) : new Integer(n + value);
+        }
+
+        static int method6(Integer n) throws MyException{
+            throw new MyException();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/sqe/methodReference/SamConversionComboTest.java	Wed Jan 25 15:11:03 2012 -0800
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @summary  Test SAM conversion of method references in combinations of different contexts,
+ *           lambda body types(statement/expression), boxing/unboxing etc, to verify
+ *           SAM conversion being conducted successfully as expected.
+ */
+
+import com.sun.source.util.JavacTask;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+import javax.tools.StandardJavaFileManager;
+
+public class SamConversionComboTest {
+
+    enum FInterface {
+        A("A", "interface A { Integer m(int i); }"),
+        B("B", "interface B { int m(Integer i); }"),
+        C("C", "interface C { int m(int i) throws Exception; }");
+
+        String interfaceType;
+        String interfaceDef;
+
+        FInterface(String interfaceType, String interfaceDef) {
+            this.interfaceType = interfaceType;
+            this.interfaceDef = interfaceDef;
+        }
+    }
+
+    enum Context {
+        ASSIGNMENT("#FType f = #MR;"),
+        METHOD_CALL("void method1(#FType f) { }\n" +
+                    "void method2() {\n" +
+                    "    method1(#MR);\n" +
+                    "}"),
+        CONSTRUCTOR("X x = new X(#MR);"),
+        RETURN_OF_METHOD("#FType method1() {\n" +
+                         "    return #MR;\n" +
+                         "}"),
+        ARRAY_INITIALIZER("#FType[] oarray = {#MR};"),
+        LAMBDA_BODY("#FType f = n -> ((#FType)#MR).m(n);"),
+        CAST("void test() throws Exception { int n = ((#FType)#MR).m(1); }"),
+        CONDITIONAL_EXPRESSION("#FType f = 2 > 1 ? #MR : null;");
+
+        String context;
+
+        Context(String context) {
+            this.context = context;
+        }
+
+        String getContext(FInterface f, MethodReference mr) {
+            return context.replace("#FType", f.interfaceType).replace("#MR", mr.mrValue);
+        }
+    }
+
+    enum MethodReference {
+        METHOD1("X#method1"),
+        METHOD2("new X()#method2"),
+        METHOD3("X#method3"),
+        METHOD4("new X()#method4"),
+        METHOD5("new X()#method5"),
+        METHOD6("X#method6"),
+        METHOD7("X#method7"),
+        METHOD8("X#method8");
+
+        String mrValue;
+
+        MethodReference(String mr) {
+            mrValue = mr;
+        }
+    }
+
+    enum MethodDef {
+        METHOD1("    static Integer method1(int n) {\n" +
+                "        return n + 1;\n" +
+                "    }\n", 0),
+        METHOD2("    int method2(Integer n) {\n" +
+                "        return value == 0 ? n + 2 : n + value;\n" +
+                "    }\n", 1),
+        METHOD3("    static int method3(int n) {\n" +
+                "        return n + 3;\n" +
+                "    }\n", 2),
+        METHOD4("    Integer method4(Integer n) {\n" +
+                "        return value == 0 ? n + 4 : n + value;\n" +
+                "    }\n", 3),
+        METHOD5("    Integer method5(Integer n) {\n" +
+                "        return value == 0 ? new Integer(n + 5) : new Integer(n + value);\n" +
+                "    }\n", 4),
+        METHOD6("    static int method6(Integer n) throws Exception{\n" +
+                "        throw new Exception();\n" +
+                "    }\n", 5),
+        METHOD7("    static int method7(String s){\n" +
+                "        return s.length();\n" +
+                "    }\n", 6),
+        METHOD8("    static String method8(Integer n){\n" +
+                "        return n + \"\";\n" +
+                "    }\n", 7);
+
+        String methodStr;
+        int index;
+
+        MethodDef(String ms, int i) {
+            methodStr = ms;
+            index = i;
+        }
+
+        MethodReference getMethodReference() {
+            return MethodReference.values()[index];
+        }
+    }
+    
+    SourceFile samSourceFile = new SourceFile("FInterface.java", "#C") {
+        public String toString() {
+            String interfaces = "";
+            for(FInterface fi : FInterface.values())
+                interfaces += fi.interfaceDef + "\n";
+            return template.replace("#C", interfaces);
+        }
+    };
+
+    String clientTemplate = "class Client {\n" +
+                            "    #Context\n" +
+                            "}\n\n" +
+
+                            "class X {\n" +
+                            "    int value = 0;\n\n" +
+
+                            "    X() {\n" +
+                            "    }\n\n" +
+
+                            "    X(A a) {\n" +
+                            "        value = a.m(9);\n" +
+                            "    }\n\n" +
+
+                            "    X(B b) {\n" +
+                            "        value = b.m(9);\n" +
+                            "    }\n\n" +
+
+                            "    X(C c) {\n" +
+                            "        try {\n" +
+                            "            value = c.m(9);\n" +
+                            "        } catch (Exception e){}\n" +
+                            "    }\n\n" +
+
+                            "#MethodDef" +
+                            "}";
+
+    SourceFile clientSourceFile = new SourceFile("Client.java", clientTemplate) {
+        public String toString() {
+            return template.replace("#Context", context.getContext(fInterface, methodReference)).replace("#MethodDef", methodDef.methodStr);
+        }
+    };
+
+    boolean checkSamConversion() {
+        if(methodDef == MethodDef.METHOD7 || methodDef == MethodDef.METHOD8)//method signature mismatch
+            return false;
+        if(context != Context.CONSTRUCTOR && fInterface != FInterface.C && methodDef == MethodDef.METHOD6)
+        //method that throws exceptions not thrown by the interface method is a mismatch
+            return false;
+        if(context == Context.CONSTRUCTOR &&        
+           methodReference != MethodReference.METHOD1 && 
+           methodReference != MethodReference.METHOD2 && 
+           methodReference != MethodReference.METHOD3 && 
+           methodReference != MethodReference.METHOD6)//ambiguous reference
+               return false;        
+        return true;
+    }
+
+    void test() throws Exception {    
+        System.out.println("\n====================================");
+        System.out.println(fInterface + ", " +  context + ", " + methodReference);
+        System.out.println(samSourceFile + "\n" + clientSourceFile);
+
+        DiagnosticChecker dc = new DiagnosticChecker();
+        JavacTask ct = (JavacTask)comp.getTask(null, fm, dc, null, null, Arrays.asList(samSourceFile, clientSourceFile));
+        ct.analyze();
+        if (dc.errorFound == checkSamConversion()) {
+            throw new AssertionError(samSourceFile + "\n\n" + clientSourceFile);
+        }
+        count++;
+    }
+
+    abstract class SourceFile extends SimpleJavaFileObject {
+
+        protected String template;
+
+        public SourceFile(String filename, String template) {
+            super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
+            this.template = template;
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return toString();
+        }
+
+        public abstract String toString();
+    }
+
+    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        boolean errorFound = false;
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                errorFound = true;
+            }
+        }
+    }
+
+    FInterface fInterface;
+    Context context;
+    MethodDef methodDef;
+    MethodReference methodReference;
+    static int count = 0;
+
+    static JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+    static StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+
+    SamConversionComboTest(FInterface f, Context c, MethodDef md) {
+        fInterface = f;
+        context = c;
+        methodDef = md;
+        methodReference = md.getMethodReference();
+    }
+
+    public static void main(String[] args) throws Exception {
+        for(Context ct : Context.values()) {
+            for (FInterface fi : FInterface.values()) {
+                for (MethodDef md: MethodDef.values()) {
+                    new SamConversionComboTest(fi, ct, md).test();
+                }
+            }
+        }
+        System.out.println("total tests: " + count);
+    }
+}