1 /*
  2  * Copyright (c) 2017, 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  * @bug 8186046 8195694
 27  * @summary Test dynamic constant bootstraps
 28  * @library /java/lang/invoke/common
 29  * @build test.java.lang.invoke.lib.InstructionHelper
 30  * @enablePreview
 31  * @run testng ConstantBootstrapsTest
 32  * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 ConstantBootstrapsTest
 33  */
 34 
 35 import org.testng.annotations.Test;
 36 import test.java.lang.invoke.lib.InstructionHelper;
 37 
 38 import java.lang.constant.ConstantDescs;
 39 import java.lang.constant.DirectMethodHandleDesc;
 40 import java.lang.constant.MethodHandleDesc;
 41 import java.lang.invoke.*;
 42 import java.math.BigInteger;
 43 import java.util.Collection;
 44 import java.util.List;
 45 import java.util.Map;
 46 
 47 import static org.testng.Assert.assertEquals;
 48 import static org.testng.Assert.assertNull;
 49 
 50 @Test
 51 public class ConstantBootstrapsTest {
 52     static final MethodHandles.Lookup L = MethodHandles.lookup();
 53 
 54     static MethodType lookupMT(Class<?> ret, Class<?>... params) {
 55         return MethodType.methodType(ret, MethodHandles.Lookup.class, String.class, Class.class).
 56                 appendParameterTypes(params);
 57     }
 58 
 59     public void testNullConstant() throws Throwable {
 60         var handle = InstructionHelper.ldcDynamicConstant(L, "_", Object.class,
 61                 ConstantBootstraps.class, "nullConstant", lookupMT(Object.class));
 62         assertNull(handle.invoke());
 63 
 64         handle = InstructionHelper.ldcDynamicConstant(L, "_", MethodType.class,
 65                 ConstantBootstraps.class, "nullConstant", lookupMT(Object.class));
 66         assertNull(handle.invoke());
 67     }
 68 
 69     @Test(expectedExceptions = IllegalArgumentException.class)
 70     public void testNullConstantPrimitiveClass() {
 71         ConstantBootstraps.nullConstant(MethodHandles.lookup(), null, int.class);
 72     }
 73 
 74 
 75     public void testPrimitiveClass() throws Throwable {
 76         var pm = Map.of(
 77                 "I", int.class,
 78                 "J", long.class,
 79                 "S", short.class,
 80                 "B", byte.class,
 81                 "C", char.class,
 82                 "F", float.class,
 83                 "D", double.class,
 84                 "Z", boolean.class,
 85                 "V", void.class
 86         );
 87 
 88         for (var desc : pm.keySet()) {
 89             var handle = InstructionHelper.ldcDynamicConstant(L, desc, Class.class,
 90                     ConstantBootstraps.class, "primitiveClass", lookupMT(Class.class));
 91             assertEquals(handle.invoke(), pm.get(desc));
 92         }
 93     }
 94 
 95     @Test(expectedExceptions = NullPointerException.class)
 96     public void testPrimitiveClassNullName() {
 97         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), null, Class.class);
 98     }
 99 
100     @Test(expectedExceptions = NullPointerException.class)
101     public void testPrimitiveClassNullType() {
102         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "I", null);
103     }
104 
105     @Test(expectedExceptions = IllegalArgumentException.class)
106     public void testPrimitiveClassEmptyName() {
107         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "", Class.class);
108     }
109 
110     @Test(expectedExceptions = IllegalArgumentException.class)
111     public void testPrimitiveClassWrongNameChar() {
112         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "L", Class.class);
113     }
114 
115     @Test(expectedExceptions = IllegalArgumentException.class)
116     public void testPrimitiveClassWrongNameString() {
117         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "Ljava/lang/Object;", Class.class);
118     }
119 
120 
121     public void testEnumConstant() throws Throwable {
122         for (var v : StackWalker.Option.values()) {
123             var handle = InstructionHelper.ldcDynamicConstant(L, v.name(), StackWalker.Option.class,
124                     ConstantBootstraps.class, "enumConstant", lookupMT(Enum.class));
125             assertEquals(handle.invoke(), v);
126         }
127     }
128 
129     @Test(expectedExceptions = IllegalArgumentException.class)
130     public void testEnumConstantUnknown() {
131         ConstantBootstraps.enumConstant(MethodHandles.lookup(), "DOES_NOT_EXIST", StackWalker.Option.class);
132     }
133 
134 
135     public void testGetStaticDecl() throws Throwable {
136         var handle = InstructionHelper.ldcDynamicConstant(L, "TYPE", Class.class,
137                 ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class, Class.class),
138                 InstructionHelper.classDesc(Integer.class));
139         assertEquals(handle.invoke(), int.class);
140     }
141 
142     public void testGetStaticSelf() throws Throwable {
143         var handle = InstructionHelper.ldcDynamicConstant(L, "MAX_VALUE", int.class,
144                 ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class));
145         assertEquals(handle.invoke(), Integer.MAX_VALUE);
146 
147 
148         handle = InstructionHelper.ldcDynamicConstant(L, "ZERO", BigInteger.class,
149                 ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class));
150         assertEquals(handle.invoke(), BigInteger.ZERO);
151     }
152 
153 
154     public void testInvoke() throws Throwable {
155         var handle = InstructionHelper.ldcDynamicConstant(
156                 L, "_", List.class,
157                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
158                 MethodHandleDesc.of(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, ConstantDescs.CD_List, "of",
159                         MethodType.methodType(List.class, Object[].class).toMethodDescriptorString()),
160                 1, 2, 3, 4
161         );
162         assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
163     }
164 
165     public void testInvokeAsType() throws Throwable {
166         var handle = InstructionHelper.ldcDynamicConstant(
167                 L, "_", int.class,
168                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
169                 MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, ConstantDescs.CD_Integer, "valueOf",
170                         MethodType.methodType(Integer.class, String.class).toMethodDescriptorString()),
171                 "42"
172         );
173         assertEquals(handle.invoke(), 42);
174     }
175 
176     public void testInvokeAsTypeVariableArity() throws Throwable {
177         // The constant type is Collection but the invoke return type is List
178         var handle = InstructionHelper.ldcDynamicConstant(
179                 L, "_", Collection.class,
180                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
181                 MethodHandleDesc.of(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, ConstantDescs.CD_List, "of",
182                         MethodType.methodType(List.class, Object[].class).toMethodDescriptorString()),
183                 1, 2, 3, 4
184         );
185         assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
186     }
187 
188     @Test(expectedExceptions = ClassCastException.class)
189     public void testInvokeAsTypeClassCast() throws Throwable {
190         ConstantBootstraps.invoke(MethodHandles.lookup(), "_", String.class,
191                 MethodHandles.lookup().findStatic(Integer.class, "valueOf", MethodType.methodType(Integer.class, String.class)),
192                 "42");
193     }
194 
195     @Test(expectedExceptions = WrongMethodTypeException.class)
196     public void testInvokeAsTypeWrongReturnType() throws Throwable {
197         ConstantBootstraps.invoke(MethodHandles.lookup(), "_", short.class,
198                 MethodHandles.lookup().findStatic(Integer.class, "parseInt", MethodType.methodType(int.class, String.class)),
199                 "42");
200     }
201 
202 
203     static class X {
204         public String f;
205         public static String sf;
206     }
207 
208     public void testVarHandleField() throws Throwable {
209         var handle = InstructionHelper.ldcDynamicConstant(
210                 L, "f", VarHandle.class,
211                 ConstantBootstraps.class, "fieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
212                 InstructionHelper.classDesc(X.class),
213                 InstructionHelper.classDesc(String.class)
214         );
215 
216         var vhandle = (VarHandle) handle.invoke();
217         assertEquals(vhandle.varType(), String.class);
218         assertEquals(vhandle.coordinateTypes(), List.of(X.class));
219     }
220 
221     public void testVarHandleStaticField() throws Throwable {
222         var handle = InstructionHelper.ldcDynamicConstant(
223                 L, "sf", VarHandle.class,
224                 ConstantBootstraps.class, "staticFieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
225                 InstructionHelper.classDesc(X.class),
226                 InstructionHelper.classDesc(String.class)
227         );
228 
229         var vhandle = (VarHandle) handle.invoke();
230         assertEquals(vhandle.varType(), String.class);
231         assertEquals(vhandle.coordinateTypes(), List.of());
232     }
233 
234     public void testVarHandleArray() throws Throwable {
235         var handle = InstructionHelper.ldcDynamicConstant(
236                 L, "_", VarHandle.class,
237                 ConstantBootstraps.class, "arrayVarHandle", lookupMT(VarHandle.class, Class.class),
238                 InstructionHelper.classDesc(String[].class)
239         );
240 
241         var vhandle = (VarHandle) handle.invoke();
242         assertEquals(vhandle.varType(), String.class);
243         assertEquals(vhandle.coordinateTypes(), List.of(String[].class, int.class));
244     }
245 }