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