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