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 import java.lang.Enum.EnumDesc;
  25 import java.lang.constant.MethodTypeDesc;
  26 import java.lang.invoke.MethodHandle;
  27 import java.lang.invoke.MethodHandles;
  28 import java.lang.invoke.VarHandle;
  29 import java.lang.invoke.VarHandle.VarHandleDesc;
  30 import java.lang.constant.ClassDesc;
  31 import java.lang.constant.ConstantDesc;
  32 import java.lang.constant.ConstantDescs;
  33 import java.lang.constant.DirectMethodHandleDesc;
  34 import java.lang.constant.DynamicConstantDesc;
  35 import java.lang.constant.MethodHandleDesc;
  36 
  37 import org.testng.annotations.Test;
  38 
  39 import static java.lang.constant.ConstantDescs.CD_MethodHandle;
  40 import static java.lang.constant.ConstantDescs.CD_Object;
  41 import static java.lang.constant.ConstantDescs.CD_String;
  42 import static java.lang.constant.ConstantDescs.CD_VarHandle;
  43 import static java.lang.constant.ConstantDescs.CD_int;
  44 import static org.testng.Assert.assertEquals;
  45 import static org.testng.Assert.assertFalse;
  46 import static org.testng.Assert.assertNotEquals;
  47 import static org.testng.Assert.assertNotSame;
  48 import static org.testng.Assert.assertNull;
  49 import static org.testng.Assert.assertSame;
  50 import static org.testng.Assert.assertTrue;
  51 
  52 /**
  53  * @test
  54  * @compile CondyDescTest.java
  55  * @run testng CondyDescTest
  56  * @summary unit tests for java.lang.constant.CondyDescTest
  57  */
  58 @Test
  59 public class CondyDescTest extends SymbolicDescTest {
  60     private final static ConstantDesc[] EMPTY_ARGS = new ConstantDesc[0];
  61     private final static ClassDesc CD_ConstantBootstraps = ClassDesc.of("java.lang.invoke.ConstantBootstraps");
  62 
  63     private static<T> void testDCR(DynamicConstantDesc<T> r, T c) throws ReflectiveOperationException {
  64         assertEquals(r, DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs()));
  65         assertEquals(r.resolveConstantDesc(LOOKUP), c);
  66     }
  67 
  68     private void testVarHandleDesc(DynamicConstantDesc<VarHandle> r, VarHandle vh) throws ReflectiveOperationException  {
  69         testSymbolicDesc(r);
  70         assertEquals(r.resolveConstantDesc(LOOKUP), vh);
  71         assertEquals(vh.describeConstable().orElseThrow(), r);
  72     }
  73 
  74     private static<E extends Enum<E>> void testEnumDesc(EnumDesc<E> r, E e) throws ReflectiveOperationException {
  75         testSymbolicDesc(r);
  76 
  77         assertEquals(r, EnumDesc.of(r.constantType(), r.constantName()));
  78         assertEquals(r.resolveConstantDesc(LOOKUP), e);
  79     }
  80 
  81     public void testNullConstant() throws ReflectiveOperationException {
  82         DynamicConstantDesc<?> r = (DynamicConstantDesc<?>) ConstantDescs.NULL;
  83         assertEquals(r, DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs()));
  84         assertNull(r.resolveConstantDesc(LOOKUP));
  85     }
  86 
  87     static String concatBSM(MethodHandles.Lookup lookup, String name, Class<?> type, String a, String b) {
  88         return a + b;
  89     }
  90 
  91     public void testDynamicConstant() throws ReflectiveOperationException {
  92         DirectMethodHandleDesc bsmDesc = ConstantDescs.ofConstantBootstrap(ClassDesc.of("CondyDescTest"), "concatBSM",
  93                                                                             CD_String, CD_String, CD_String);
  94         DynamicConstantDesc<String> r = DynamicConstantDesc.of(bsmDesc, "foo", "bar");
  95         testDCR(r, "foobar");
  96     }
  97 
  98     public void testNested() throws Throwable {
  99         DirectMethodHandleDesc invoker = ConstantDescs.ofConstantBootstrap(CD_ConstantBootstraps, "invoke", CD_Object, CD_MethodHandle, CD_Object.arrayType());
 100         DirectMethodHandleDesc format = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_String, "format",
 101                                                                   MethodTypeDesc.of(CD_String, CD_String, CD_Object.arrayType()));
 102 
 103         String s = (String) ((MethodHandle) invoker.resolveConstantDesc(LOOKUP))
 104                                    .invoke(LOOKUP, "", String.class,
 105                                            format.resolveConstantDesc(LOOKUP), "%s%s", "moo", "cow");
 106         assertEquals(s, "moocow");
 107 
 108         DynamicConstantDesc<String> desc = DynamicConstantDesc.of(invoker, format, "%s%s", "moo", "cow");
 109         testDCR(desc, "moocow");
 110 
 111         DynamicConstantDesc<String> desc2 = DynamicConstantDesc.of(invoker, format, "%s%s", desc, "cow");
 112         testDCR(desc2, "moocowcow");
 113     }
 114 
 115     enum MyEnum { A, B, C }
 116 
 117     public void testEnumDesc() throws ReflectiveOperationException {
 118         ClassDesc enumClass = ClassDesc.of("CondyDescTest").inner("MyEnum");
 119 
 120         testEnumDesc(EnumDesc.of(enumClass, "A"), MyEnum.A);
 121         testEnumDesc(EnumDesc.of(enumClass, "B"), MyEnum.B);
 122         testEnumDesc(EnumDesc.of(enumClass, "C"), MyEnum.C);
 123 
 124         DynamicConstantDesc<MyEnum> denum = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_ENUM_CONSTANT, "A", enumClass, EMPTY_ARGS);
 125         assertEquals(MyEnum.A, denum.resolveConstantDesc(LOOKUP));
 126 
 127         EnumDesc<MyEnum> enumDesc = (EnumDesc<MyEnum>)DynamicConstantDesc.<MyEnum>ofCanonical(ConstantDescs.BSM_ENUM_CONSTANT, "A", enumClass, EMPTY_ARGS);
 128         assertEquals(MyEnum.A, enumDesc.resolveConstantDesc(LOOKUP));
 129     }
 130 
 131     static class MyClass {
 132         static int sf;
 133         int f;
 134     }
 135 
 136     public void testVarHandles() throws ReflectiveOperationException {
 137         ClassDesc testClass = ClassDesc.of("CondyDescTest").inner("MyClass");
 138         MyClass instance = new MyClass();
 139 
 140         // static varHandle
 141         VarHandleDesc vhc = VarHandleDesc.ofStaticField(testClass, "sf", CD_int);
 142         VarHandle varHandle = LOOKUP.findStaticVarHandle(MyClass.class, "sf", int.class);
 143         testVarHandleDesc(vhc, varHandle);
 144 
 145         assertEquals(varHandle.varType(), int.class);
 146         varHandle.set(8);
 147         assertEquals(8, (int) varHandle.get());
 148         assertEquals(MyClass.sf, 8);
 149 
 150         // static varHandle
 151         vhc = VarHandleDesc.ofField(testClass, "f", CD_int);
 152         varHandle = LOOKUP.findVarHandle(MyClass.class, "f", int.class);
 153         testVarHandleDesc(vhc, varHandle);
 154 
 155         assertEquals(varHandle.varType(), int.class);
 156         varHandle.set(instance, 9);
 157         assertEquals(9, (int) varHandle.get(instance));
 158         assertEquals(instance.f, 9);
 159 
 160         vhc = VarHandleDesc.ofArray(CD_int.arrayType());
 161         varHandle = MethodHandles.arrayElementVarHandle(int[].class);
 162         testVarHandleDesc(vhc, varHandle);
 163 
 164         int[] ints = new int[3];
 165         varHandle.set(ints, 0, 1);
 166         varHandle.set(ints, 1, 2);
 167         varHandle.set(ints, 2, 3);
 168 
 169         assertEquals(1, varHandle.get(ints, 0));
 170         assertEquals(2, varHandle.get(ints, 1));
 171         assertEquals(3, varHandle.get(ints, 2));
 172         assertEquals(1, ints[0]);
 173         assertEquals(2, ints[1]);
 174         assertEquals(3, ints[2]);
 175 
 176         // static var handle obtained using the DynamicConstantDesc
 177         DynamicConstantDesc<VarHandle> dcd = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, "sf", CD_VarHandle, new ConstantDesc[] {testClass, CD_int });
 178         VarHandle vh = dcd.resolveConstantDesc(LOOKUP);
 179         testVarHandleDesc(dcd, vh);
 180 
 181         VarHandleDesc vhd = (VarHandleDesc) DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, "sf", CD_VarHandle, new ConstantDesc[] {testClass, CD_int });
 182         vh = vhd.resolveConstantDesc(LOOKUP);
 183         testVarHandleDesc(vhd, vh);
 184 
 185         dcd = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_FIELD, "f", CD_VarHandle, new ConstantDesc[] {testClass, CD_int });
 186         vh = dcd.resolveConstantDesc(LOOKUP);
 187         testVarHandleDesc(dcd, vh);
 188 
 189         vhd = (VarHandleDesc) DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_FIELD, "f", CD_VarHandle, new ConstantDesc[] {testClass, CD_int });
 190         vh = vhd.resolveConstantDesc(LOOKUP);
 191         testVarHandleDesc(vhd, vh);
 192 
 193         dcd = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_ARRAY, "_", CD_VarHandle, new ConstantDesc[] {CD_int.arrayType() });
 194         vh = dcd.resolveConstantDesc(LOOKUP);
 195         testVarHandleDesc(dcd, vh);
 196 
 197         vhd = (VarHandleDesc)DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_ARRAY, "_", CD_VarHandle, new ConstantDesc[] {CD_int.arrayType() });
 198         vh = vhd.resolveConstantDesc(LOOKUP);
 199         testVarHandleDesc(vhd, vh);
 200     }
 201 
 202     private<T> void assertLifted(ConstantDesc prototype,
 203                                  DynamicConstantDesc<T> nonCanonical,
 204                                  ConstantDesc canonical) {
 205         Class<?> clazz = prototype.getClass();
 206 
 207         assertNotSame(canonical, nonCanonical);
 208         assertTrue(clazz.isAssignableFrom(canonical.getClass()));
 209         assertFalse(clazz.isAssignableFrom(nonCanonical.getClass()));
 210         assertEquals(prototype, canonical);
 211         assertEquals(canonical, prototype);
 212         if (prototype instanceof DynamicConstantDesc) {
 213             assertEquals(canonical, nonCanonical);
 214             assertEquals(nonCanonical, canonical);
 215             assertEquals(prototype, nonCanonical);
 216             assertEquals(nonCanonical, prototype);
 217         }
 218     }
 219 
 220     public void testLifting() {
 221         DynamicConstantDesc<Object> unliftedNull = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_NULL_CONSTANT, "_", CD_Object, EMPTY_ARGS);
 222         assertEquals(ConstantDescs.NULL, unliftedNull);
 223         assertNotSame(ConstantDescs.NULL, unliftedNull);
 224         assertSame(ConstantDescs.NULL, DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_NULL_CONSTANT, "_", CD_Object, EMPTY_ARGS));
 225         assertSame(ConstantDescs.NULL, DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_NULL_CONSTANT, "_", CD_String, EMPTY_ARGS));
 226         assertSame(ConstantDescs.NULL, DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_NULL_CONSTANT, "wahoo", CD_Object, EMPTY_ARGS));
 227 
 228         assertLifted(CD_int,
 229                      DynamicConstantDesc.ofNamed(ConstantDescs.BSM_PRIMITIVE_CLASS, "I", ConstantDescs.CD_Class, EMPTY_ARGS),
 230                      DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_PRIMITIVE_CLASS, "I", ConstantDescs.CD_Class, EMPTY_ARGS));
 231 
 232         ClassDesc enumClass = ClassDesc.of("CondyDescTest").inner("MyEnum");
 233         assertLifted(EnumDesc.of(enumClass, "A"),
 234                      DynamicConstantDesc.ofNamed(ConstantDescs.BSM_ENUM_CONSTANT, "A", enumClass, EMPTY_ARGS),
 235                      DynamicConstantDesc.<MyEnum>ofCanonical(ConstantDescs.BSM_ENUM_CONSTANT, "A", enumClass, EMPTY_ARGS));
 236 
 237 
 238         ClassDesc testClass = ClassDesc.of("CondyDescTest").inner("MyClass");
 239 
 240         assertLifted(VarHandleDesc.ofStaticField(testClass, "sf", CD_int),
 241                      DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, "sf", CD_VarHandle, new ConstantDesc[] {testClass, CD_int }),
 242                      DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, "sf", CD_VarHandle, new ConstantDesc[] {testClass, CD_int }));
 243         assertLifted(VarHandleDesc.ofField(testClass, "f", CD_int),
 244                      DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_FIELD, "f", CD_VarHandle, new ConstantDesc[] {testClass, CD_int }),
 245                      DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_FIELD, "f", CD_VarHandle, new ConstantDesc[] {testClass, CD_int }));
 246 
 247         assertLifted(VarHandleDesc.ofArray(CD_int.arrayType()),
 248                      DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_ARRAY, "_", CD_VarHandle, new ConstantDesc[] {CD_int.arrayType() }),
 249                      DynamicConstantDesc.ofCanonical(ConstantDescs.BSM_VARHANDLE_ARRAY, "_", CD_VarHandle, new ConstantDesc[] {CD_int.arrayType() }));
 250     }
 251 }