1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @summary Test core reflection, dynamic proxy and lambdas that generates
  27  *          classes dynamically that reference Q-type and L-type
  28  * @compile -XDallowWithFieldOperator Point.java Line.java MutablePath.java
  29  * @compile -XDallowWithFieldOperator NonFlattenValue.java
  30  * @run testng/othervm -XX:+EnableValhalla QTypeDescriptorTest
  31  * @run testng/othervm -XX:+EnableValhalla -Dsun.reflect.noInflation=true QTypeDescriptorTest
  32  */
  33 
  34 import java.lang.invoke.MethodHandle;
  35 import java.lang.invoke.MethodHandles;
  36 import java.lang.invoke.MethodType;
  37 import java.lang.reflect.*;
  38 import java.util.function.*;
  39 
  40 import org.testng.annotations.DataProvider;
  41 import org.testng.annotations.Test;
  42 import static org.testng.Assert.*;
  43 
  44 public class QTypeDescriptorTest {
  45     static final Point P0 = Point.makePoint(10, 20);
  46     static final Point P1 = Point.makePoint(30, 40);
  47     static final NonFlattenValue NFV = NonFlattenValue.make(30, 40);
  48 
  49     @Test
  50     public static void testLambda() {
  51         newArray(Point[]::new, 2);
  52         newArray(Point[][]::new, 1);
  53 
  54         newArray(NonFlattenValue[]::new, 3);
  55         newArray(MutablePath[]::new, 4);
  56 
  57         Function<Point[], T> f =
  58             (points) -> { return new T(points); };
  59         f.apply(new Point[] { P0, P1});
  60     }
  61 
  62     @Test
  63     public static void testMethodInvoke() throws Exception {
  64         Class<?> pointQType = Point.class.asValueType();
  65         Class<?> nonFlattenValueQType = NonFlattenValue.class.asValueType();
  66         Method m = QTypeDescriptorTest.class
  67             .getDeclaredMethod("toLine", pointQType, nonFlattenValueQType);
  68         makeLine(m, P0, NFV);
  69 
  70         m = QTypeDescriptorTest.class
  71                 .getDeclaredMethod("toLine", Point[].class);
  72         makeLine(m, (Object) new Point[] { P0, P1});
  73     }
  74 
  75     private static void makeLine(Method m, Object... args) throws Exception {
  76         Line l = (Line) m.invoke(null, args);
  77         assertEquals(l.p1, P0);
  78         assertEquals(l.p2, NFV.pointValue());
  79     }
  80 
  81     @Test
  82     public static void testStaticMethod() throws Throwable {
  83         // static method in an inline type with no parameter and void return type
  84         Runnable r = () -> ValueTest.run();
  85         r.run();
  86 
  87         // via Method::invoke
  88         Method m = ValueTest.class.getMethod("run");
  89         m.invoke(null);
  90 
  91         // via MethodHandle
  92         MethodHandle mh = MethodHandles.lookup()
  93             .findStatic(ValueTest.class, "run", MethodType.methodType(void.class));
  94         mh.invokeExact();
  95 
  96         mh = MethodHandles.lookup().unreflect(m);
  97         mh.invokeExact();
  98     }
  99 
 100     @Test
 101     public static void testConstructor() throws Exception {
 102         Constructor<T> ctor = T.class.getDeclaredConstructor(Point[].class);
 103         Point[] points = new Point[] { P0, P1 };
 104         T test = (T) ctor.newInstance((Object)points);
 105         assertEquals(test.points[0], P0);
 106         assertEquals(test.points[1], P1);
 107     }
 108 
 109     @Test
 110     public static void testProxy() throws Exception {
 111         InvocationHandler handler = new InvocationHandler() {
 112             @Override
 113             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 114                 if (method.getName().equals("toLine")) {
 115                     return toLine((Point)args[0], (NonFlattenValue)args[1]);
 116                 }
 117                 throw new UnsupportedOperationException(method.toString());
 118             }
 119         };
 120 
 121         Class<?>[] intfs = new Class<?>[] { I.class };
 122         I intf = (I) Proxy.newProxyInstance(QTypeDescriptorTest.class.getClassLoader(), intfs, handler);
 123         Line l = intf.toLine(P0, NFV);
 124         assertEquals(l.p1, P0);
 125         assertEquals(l.p2, NFV.pointValue());
 126     }
 127 
 128     @DataProvider
 129     static Object[][] descriptors() {
 130         Class<?> pointLType = Point.class.asBoxType();
 131         Class<?> pointQType = Point.class.asValueType();
 132         Class<?> nonFlattenValueLType = NonFlattenValue.class.asBoxType();
 133         Class<?> nonFlattenValueQType = NonFlattenValue.class.asValueType();
 134         return new Object[][]{
 135             { QTypeDescriptorTest.class, "toLine", new Class<?>[] {pointQType, nonFlattenValueQType}, true},
 136             { QTypeDescriptorTest.class, "toLine", new Class<?>[] {pointLType, nonFlattenValueQType}, false},
 137             { QTypeDescriptorTest.class, "toLine", new Class<?>[] { Point[].class },                  true},
 138             { NonFlattenValue.class, "point",      null,                                              true},
 139             { NonFlattenValue.class, "pointValue", null,                                              true},
 140             { NonFlattenValue.class, "has",        new Class<?>[] {pointQType, pointLType},           true},
 141             { NonFlattenValue.class, "has",        new Class<?>[] {pointQType, pointQType},           false},
 142         };
 143     }
 144 
 145     @Test(dataProvider = "descriptors")
 146     public static void testDescriptors(Class<?> defc, String name, Class<?>[] params, boolean found) throws Exception {
 147         try {
 148             defc.getDeclaredMethod(name, params);
 149             if (!found) throw new AssertionError("Expected NoSuchMethodException");
 150         } catch (NoSuchMethodException e) {
 151             if (found) throw e;
 152         }
 153     }
 154 
 155     @DataProvider
 156     static Object[][] methodTypes() {
 157         Class<?> pointLType = Point.class.asBoxType();
 158         Class<?> pointQType = Point.class.asValueType();
 159         ClassLoader loader = QTypeDescriptorTest.class.getClassLoader();
 160         return new Object[][]{
 161             { "point",      MethodType.methodType(pointLType),                            true },
 162             { "pointValue", MethodType.methodType(pointQType),                            true },
 163             { "has",        MethodType.methodType(boolean.class, pointQType, pointLType), true },
 164             { "point",      MethodType.methodType(pointQType),                            false },
 165             { "pointValue", MethodType.methodType(pointLType),                            false },
 166             { "has",        MethodType.methodType(boolean.class, pointLType, pointQType), false },
 167             { "point",      MethodType.fromMethodDescriptorString("()LPoint;", loader),         true },
 168             { "point",      MethodType.fromMethodDescriptorString("()QPoint;", loader),         false },
 169             { "pointValue", MethodType.fromMethodDescriptorString("()QPoint;", loader),         true },
 170             { "pointValue", MethodType.fromMethodDescriptorString("()LPoint;", loader),         false },
 171             { "has",        MethodType.fromMethodDescriptorString("(QPoint;LPoint;)Z", loader), true },
 172             { "has",        MethodType.fromMethodDescriptorString("(LPoint;LPoint;)Z", loader), false },
 173         };
 174     }
 175 
 176     @Test(dataProvider = "methodTypes")
 177     public static void methodHandleLookup(String name, MethodType mtype, boolean found) throws Throwable {
 178         try {
 179             MethodHandles.lookup().findVirtual(NonFlattenValue.class, name, mtype);
 180             if (!found) throw new AssertionError("Expected NoSuchMethodException");
 181         } catch (NoSuchMethodException e) {
 182             if (found) throw e;
 183         }
 184     }
 185 
 186     private static <T> T[] newArray(IntFunction<T[]> arrayCreator, int size) {
 187         return arrayCreator.apply(size);
 188     }
 189 
 190     private static Line toLine(Point p, NonFlattenValue nfv) {
 191         return Line.makeLine(p, nfv.pointValue());
 192     }
 193 
 194     private static Line toLine(Point[] points) {
 195         assertTrue(points.length == 2);
 196         return Line.makeLine(points[0], points[1]);
 197     }
 198 
 199     static class T {
 200         final Point[] points;
 201         T(Point[] points) {
 202             this.points = points;
 203         }
 204     }
 205 
 206     interface I {
 207         Line toLine(Point p, NonFlattenValue nfv);
 208     }
 209 
 210     static inline class ValueTest {
 211         private final int value;
 212         public ValueTest() { this.value = 0; }
 213 
 214         public static void run() {
 215             Runnable r = () -> {
 216                 System.out.println("called ValueTest::run");
 217             };
 218             r.run();
 219         }
 220     }
 221 
 222 }