1 /* 2 * Copyright (c) 2017, 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 8186211 27 * @summary Test basic invocation of multiple ldc's of the same dynamic constant that fail resolution 28 * @library /lib/testlibrary/bytecode /java/lang/invoke/common 29 * @build jdk.experimental.bytecode.BasicClassBuilder 30 * @run testng CondyRepeatFailedResolution 31 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution 32 */ 33 34 import jdk.experimental.bytecode.BasicClassBuilder; 35 import jdk.experimental.bytecode.Flag; 36 import jdk.experimental.bytecode.TypedCodeBuilder; 37 import org.testng.Assert; 38 import org.testng.annotations.BeforeClass; 39 import org.testng.annotations.Test; 40 41 import java.lang.invoke.MethodHandles; 42 import java.lang.invoke.MethodType; 43 import java.lang.reflect.InvocationTargetException; 44 import java.lang.reflect.Method; 45 46 @Test 47 public class CondyRepeatFailedResolution { 48 // Counter used to determine if a given BSM is invoked more than once 49 static int bsm_called = 0; 50 51 // Generated class with methods containing condy ldc 52 Class<?> gc; 53 54 // Bootstrap method used to represent primitive values 55 // that cannot be represented directly in the constant pool, 56 // such as byte, and for completeness of testing primitive values 57 // that can be represented directly, such as double or long that 58 // take two slots 59 public static Object intConversion(MethodHandles.Lookup l, 60 String constantName, 61 Class<?> constantType, 62 int value) throws Throwable { 63 ++bsm_called; 64 // replace constantName with a bogus value to trigger failed resolution 65 constantName = "Foo"; 66 67 switch (constantName) { 68 case "B": 69 return (byte) value; 70 case "C": 71 return (char) value; 72 case "D": 73 return (double) value; 74 case "F": 75 return (float) value; 76 case "I": 77 return value; 78 case "J": 79 return (long) value; 80 case "S": 81 return (short) value; 82 case "Z": 83 return value > 0; 84 case "nullRef": 85 return null; 86 case "string": 87 return "string"; 88 case "stringArray": 89 return new String[]{"string", "string"}; 90 default: 91 throw new BootstrapMethodError("Failure to generate a dynamic constant"); 92 } 93 } 94 95 @BeforeClass 96 public void generateClass() throws Exception { 97 String genClassName = CondyRepeatFailedResolution.class.getSimpleName() + "$Code"; 98 String bsmClassName = CondyRepeatFailedResolution.class.getCanonicalName().replace('.', '/'); 99 String bsmMethodName = "intConversion"; 100 String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, 101 String.class, Class.class, int.class).toMethodDescriptorString(); 102 103 byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0) 104 .withSuperclass("java/lang/Object") 105 .withMethod("<init>", "()V", M -> 106 M.withFlags(Flag.ACC_PUBLIC) 107 .withCode(TypedCodeBuilder::new, C -> 108 C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_() 109 )) 110 .withMethod("B", "()B", M -> 111 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 112 .withCode(TypedCodeBuilder::new, C -> 113 C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor, 114 S -> S.add(Byte.MAX_VALUE)) 115 .ireturn() 116 )) 117 .withMethod("C", "()C", M -> 118 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 119 .withCode(TypedCodeBuilder::new, C -> 120 C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor, 121 S -> S.add(Character.MAX_VALUE)) 122 .ireturn() 123 )) 124 .withMethod("D", "()D", M -> 125 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 126 .withCode(TypedCodeBuilder::new, C -> 127 C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor, 128 S -> S.add(Integer.MAX_VALUE)) 129 .dreturn() 130 )) 131 .withMethod("D_AsType", "()D", M -> 132 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 133 .withCode(TypedCodeBuilder::new, C -> 134 C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor, 135 S -> S.add(Integer.MAX_VALUE)) 136 .dreturn() 137 )) 138 .withMethod("F", "()F", M -> 139 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 140 .withCode(TypedCodeBuilder::new, C -> 141 C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor, 142 S -> S.add(Integer.MAX_VALUE)) 143 .freturn() 144 )) 145 .withMethod("F_AsType", "()F", M -> 146 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 147 .withCode(TypedCodeBuilder::new, C -> 148 C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor, 149 S -> S.add(Integer.MAX_VALUE)) 150 .freturn() 151 )) 152 .withMethod("I", "()I", M -> 153 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 154 .withCode(TypedCodeBuilder::new, C -> 155 C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor, 156 S -> S.add(Integer.MAX_VALUE)) 157 .ireturn() 158 )) 159 .withMethod("J", "()J", M -> 160 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 161 .withCode(TypedCodeBuilder::new, C -> 162 C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor, 163 S -> S.add(Integer.MAX_VALUE)) 164 .lreturn() 165 )) 166 .withMethod("J_AsType", "()J", M -> 167 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 168 .withCode(TypedCodeBuilder::new, C -> 169 C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor, 170 S -> S.add(Integer.MAX_VALUE)) 171 .lreturn() 172 )) 173 .withMethod("S", "()S", M -> 174 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 175 .withCode(TypedCodeBuilder::new, C -> 176 C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor, 177 S -> S.add(Short.MAX_VALUE)) 178 .ireturn() 179 )) 180 .withMethod("Z_F", "()Z", M -> 181 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 182 .withCode(TypedCodeBuilder::new, C -> 183 C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor, 184 S -> S.add(0)) 185 .ireturn() 186 )) 187 .withMethod("Z_T", "()Z", M -> 188 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 189 .withCode(TypedCodeBuilder::new, C -> 190 C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor, 191 S -> S.add(1)) 192 .ireturn() 193 )) 194 .withMethod("null", "()Ljava/lang/Object;", M -> 195 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 196 .withCode(TypedCodeBuilder::new, C -> 197 C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor, 198 S -> S.add(Integer.MAX_VALUE)) 199 .areturn() 200 )) 201 .withMethod("string", "()Ljava/lang/String;", M -> 202 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 203 .withCode(TypedCodeBuilder::new, C -> 204 C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor, 205 S -> S.add(Integer.MAX_VALUE)) 206 .areturn() 207 )) 208 .withMethod("stringArray", "()[Ljava/lang/String;", M -> 209 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 210 .withCode(TypedCodeBuilder::new, C -> 211 C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor, 212 S -> S.add(Integer.MAX_VALUE)) 213 .areturn() 214 )) 215 .build(); 216 217 gc = MethodHandles.lookup().defineClass(byteArray); 218 } 219 220 @Test 221 public void testPrimitives() throws Exception { 222 testConstants(); 223 } 224 225 @Test 226 public void testRefs() throws Exception { 227 testConstant("string", "string"); 228 testConstant("stringArray", new String[]{"string", "string"}); 229 } 230 231 void testConstants() throws Exception { 232 // Note: for the _asType methods the BSM returns an int which is 233 // then converted by an asType transformation 234 235 testConstant("B", Byte.MAX_VALUE); 236 testConstant("C", Character.MAX_VALUE); 237 testConstant("D", (double) Integer.MAX_VALUE); 238 testConstant("D_AsType", (double) Integer.MAX_VALUE); 239 testConstant("F", (float) Integer.MAX_VALUE); 240 testConstant("F_AsType", (float) Integer.MAX_VALUE); 241 testConstant("I", Integer.MAX_VALUE); 242 testConstant("J", (long) Integer.MAX_VALUE); 243 testConstant("J_AsType", (long) Integer.MAX_VALUE); 244 testConstant("S", Short.MAX_VALUE); 245 testConstant("Z_F", false); 246 testConstant("Z_T", true); 247 testConstant("null", null); 248 } 249 250 void testConstant(String name, Object expected) throws Exception { 251 Method m = gc.getDeclaredMethod(name); 252 253 bsm_called = 0; 254 try { 255 Object r1 = m.invoke(null); 256 Assert.fail("InvocationTargetException expected to be thrown after first invocation"); 257 } catch (InvocationTargetException e1) { 258 // bsm_called should have been incremented prior to the exception 259 Assert.assertEquals(bsm_called, 1); 260 Assert.assertTrue(e1.getCause() instanceof BootstrapMethodError); 261 // Try invoking method again to ensure that the bootstrap 262 // method is not invoked twice and a resolution failure 263 // results. 264 try { 265 Object r2 = m.invoke(null); 266 Assert.fail("InvocationTargetException expected to be thrown after second invocation"); 267 } catch (InvocationTargetException e2) { 268 // bsm_called should remain at 1 since the bootstrap 269 // method should not have been invoked. 270 Assert.assertEquals(bsm_called, 1); 271 Assert.assertTrue(e2.getCause() instanceof BootstrapMethodError); 272 } catch (Throwable t2) { 273 Assert.fail("InvocationTargetException expected to be thrown"); 274 } 275 } catch (Throwable t1) { 276 Assert.fail("InvocationTargetException expected to be thrown"); 277 } 278 } 279 }