/******************************************************************************* * Copyright (c) 2011, 2014 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.tests.compiler.regression; import java.util.Map; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import junit.framework.Test; /** * Regression test for MethodHandle.invokeExact(..)/invokeGeneric(..) invocation */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class MethodHandleTest extends AbstractRegressionTest { public MethodHandleTest(String name) { super(name); } public static Test suite() { return buildMinimalComplianceTestSuite(testClass(), F_1_7); } public static Class testClass() { return MethodHandleTest.class; } static { // TESTS_NAMES = new String [] { "test009" }; } public void test001() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "\n" + "public class X {\n" + " public static void main(String[] args) throws Throwable {\n" + " MethodHandles.Lookup lookup = MethodHandles.lookup();\n" + "\n" + " MethodType mt = MethodType.methodType(String.class, String.class, char.class);\n" + " MethodHandle mh = lookup.findStatic(X.class, \"append\", mt);\n" + " String s = (String) mh.invokeExact(\"follo\",'w');\n" + " System.out.println(s);\n" + "\n" + " mt = MethodType.methodType(int.class, Object[].class);\n" + " mh = lookup.findVirtual(X.class, \"arrayLength\", mt);\n" + " int i = (int) mh.invokeExact(new X(), new Object[] {1, 'A', \"foo\"});\n" + " System.out.println(i);\n" + "\n" + " mt = MethodType.methodType(void.class, String.class);\n" + " mh = lookup.findStatic(X.class, \"hello\", mt);\n" + " mh.invokeExact(\"world\");\n" + "\n" + " mt = MethodType.methodType(Object.class, String.class, int.class);\n" + " mh = lookup.findVirtual(X.class, \"foo\", mt);\n" + " Object o = mh.invoke(new X(), (Object)\"foo:\", i);\n" + "\n" + " mt = MethodType.methodType(void.class);\n" + " mh = lookup.findStatic(X.class, \"bar\", mt);\n" + " mh.invokeExact();\n" + " }\n" + " public static void bar() {\n" + " System.out.println(\"bar\");\n" + " }\n" + " public Object foo(String s, int i) {\n" + " System.out.println(s + i);\n" + " return s + i;\n" + " }\n" + " public static String append(String s, char c) {\n" + " return s + c;\n" + " }\n" + " public int arrayLength(Object[] array) {\n" + " return array.length;\n" + " }\n" + " public static void hello(String name) {\n" + " System.out.println(\"Hello, \"+ name);\n" + " }\n" + "}" }, "follow\n" + "3\n" + "Hello, world\n" + "foo:3\n" + "bar"); } public void test002() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "import java.lang.invoke.WrongMethodTypeException;\n" + "\n" + "public class X {\n" + " public static void foo() {\n" + " }\n" + " public static void main(String[] args) {\n" + " try {\n" + " MethodHandle handle = MethodHandles.lookup().findStatic(X.class, \"foo\", MethodType.methodType(void.class));\n" + " try {\n" + " handle.invoke(null);\n" + " } catch (WrongMethodTypeException ok) {\n" + " System.out.println(\"This is ok\");\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " }\n" + "}" }, "This is ok"); } public void test003() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "import java.lang.invoke.WrongMethodTypeException;\n" + "\n" + "public class X {\n" + " public static <T> T foo(T param){\n" + " return null;\n" + " }\n" + " public static void main(String[] args) {\n" + " try {\n" + " MethodHandle handle = MethodHandles.lookup().findStatic(X.class, \"foo\", MethodType.methodType(Object.class, Object.class));\n" + " try {\n" + " handle.invoke(null);\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " }\n" + "}" }, ""); } public void test004() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "import java.lang.invoke.WrongMethodTypeException;\n" + "\n" + "public class X {\n" + " public static <T> T foo(T param){\n" + " return null;\n" + " }\n" + " public static void main(String[] args) {\n" + " try {\n" + " MethodHandle handle = MethodHandles.lookup().findStatic(X.class, \"foo\", MethodType.methodType(Object.class, Object.class));\n" + " try {\n" + " handle.invoke(new Object());\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " }\n" + "}" }, ""); } public void test005() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "import java.lang.invoke.WrongMethodTypeException;\n" + "\n" + "public class X {\n" + " public static <T> T foo(T param){\n" + " return null;\n" + " }\n" + " public static void main(String[] args) {\n" + " try {\n" + " MethodHandle handle = MethodHandles.lookup().findStatic(X.class, \"foo\", MethodType.methodType(Object.class, Object.class));\n" + " try {\n" + " Object o = handle.invoke(new Object());\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " } catch (Throwable e) {\n" + " e.printStackTrace();\n" + " }\n" + " }\n" + "}" }, ""); } public void test006() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "import java.lang.invoke.WrongMethodTypeException;\n" + "\n" + "public class X {\n" + " public static void main(String[] args) throws Throwable {\n" + " MethodHandles.Lookup lookup = MethodHandles.lookup();\n" + "\n" + " MethodType mt = MethodType.methodType(String.class, String.class, char.class);\n" + " MethodHandle mh = lookup.findStatic(X.class, \"append\", mt);\n" + " String s = (String) mh.invokeExact(\"follo\",'w');\n" + " System.out.println(s);\n" + " MethodType mt2 = MethodType.methodType(String.class, String.class, char.class);\n" + " MethodHandle mh2 = lookup.findStatic(X.class, \"append\", mt2);\n" + " try {\n" + " mh2.invokeExact(\"follo\",'w');\n" + " } catch(WrongMethodTypeException e) {\n" + " System.out.println(\"Expected exception\");\n" + " }\n" + " }\n" + " public static String append(String s, char c) {\n" + " return s + c;\n" + " }\n" + "}" }, "follow\n" + "Expected exception"); } public void test007() { this.runConformTest( new String[] { "X.java", "import static java.lang.invoke.MethodHandles.lookup;\n" + "import static java.lang.invoke.MethodType.methodType;\n" + "import java.lang.invoke.MethodHandle;\n" + "\n" + "public class X {\n" + " public static void main(String[] args) throws Throwable {\n" + " MethodHandle fooMH = lookup().findStatic(X.class, \"foo\", methodType(String.class));\n" + " String s = (String) fooMH.invokeExact();\n" + " System.out.println(s);\n" + " fooMH.asType(methodType(void.class)).invokeExact();\n" + " }\n" + " public static String foo() {\n" + " System.out.println(\"Inside foo\");\n" + " return \"foo\";\n" + " }\n" + "}" }, "Inside foo\n" + "foo\n" + "Inside foo"); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=386259, wrong unnecessary cast warning. public void test009() { Map customOptions = getCompilerOptions(); customOptions.put(CompilerOptions.OPTION_ReportUnnecessaryTypeCheck, CompilerOptions.ERROR); runNegativeTest( // test directory preparation true /* flush output directory */, new String[] { /* test files */ "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "public class X {\n" + " public static void main(String[] args) throws Throwable {\n" + " String str = \"test\";\n" + " MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, \"toString\", \n" + " MethodType.methodType(String.class));\n" + " String actual = (String) mh.invoke(str);\n" + " assert \"test\".equals(actual);\n" + " Zork z;\n" + " }\n" + "}\n" }, // compiler options null /* no class libraries */, customOptions /* custom options */, // compiler results "----------\n" + /* expected compiler log */ "1. ERROR in X.java (at line 11)\n" + " Zork z;\n" + " ^^^^\n" + "Zork cannot be resolved to a type\n" + "----------\n", // javac options JavacTestOptions.Excuse.EclipseWarningConfiguredAsError /* javac test options */); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=386259 variation. public void test010() { Map customOptions = getCompilerOptions(); customOptions.put(CompilerOptions.OPTION_ReportUnnecessaryTypeCheck, CompilerOptions.ERROR); runNegativeTest( // test directory preparation true /* flush output directory */, new String[] { /* test files */ "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.invoke.MethodType;\n" + "public class X {\n" + " public static void main(String[] args) throws Throwable {\n" + " String str = \"test\";\n" + " MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, \"toString\", \n" + " MethodType.methodType(String.class));\n" + " Object actual = (Object) mh.invoke(str);\n" + " assert \"test\".equals(actual);\n" + " Zork z;\n" + " }\n" + "}\n" }, // compiler options null /* no class libraries */, customOptions /* custom options */, // compiler results "----------\n" + "1. ERROR in X.java (at line 9)\n" + " Object actual = (Object) mh.invoke(str);\n" + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + "Unnecessary cast from Object to Object\n" + "----------\n" + "2. ERROR in X.java (at line 11)\n" + " Zork z;\n" + " ^^^^\n" + "Zork cannot be resolved to a type\n" + "----------\n", // javac options JavacTestOptions.Excuse.EclipseWarningConfiguredAsError /* javac test options */); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=466748 public void test011() { this.runConformTest( new String[] { "X.java", "import java.lang.invoke.MethodHandle;\n" + "import java.lang.invoke.MethodHandles;\n" + "import java.lang.reflect.Method;\n" + "\n" + "public class X {\n" + " public static void test1(Integer i){\n" + " System.out.println(\"test1:\" + i);\n" + " }\n" + " public static void test2(int i){\n" + " System.out.println(\"test2:\" + i);\n" + " }\n" + "\n" + " public static void main(String[] args) throws Throwable{\n" + " Method m1 = X.class.getMethod(\"test1\", Integer.class);\n" + " Method m2 = X.class.getMethod(\"test2\", int.class);\n" + "\n" + " MethodHandle test1Handle = MethodHandles.lookup().unreflect(m1);\n" + " MethodHandle test2Handle = MethodHandles.lookup().unreflect(m2);\n" + " \n" + " Integer arg_Integer = 1;\n" + " int arg_int = 1;\n" + " \n" + " // results in a java.lang.VerifyError - but should work without error\n" + " test1Handle.invokeExact(Integer.class.cast(arg_int));\n" + " \n" + " // The following line also results in a java.lang.VerifyError, but should actually throw a ClassCastException\n" + " try {\n" + " test2Handle.invokeExact(int.class.cast(arg_Integer)); \n" + " } catch(ClassCastException e) {\n" + " System.out.println(\"SUCCESS\");\n" + " }\n" + " }\n" + "}" }, "test1:1\n" + "SUCCESS"); } }