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 8186046 27 * @summary Test for condy BSMs returning primitive values or null 28 * @library /lib/testlibrary/bytecode 29 * @build jdk.experimental.bytecode.BasicClassBuilder 30 * @run testng CondyReturnPrimitiveTest 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.Method; 43 import java.util.concurrent.atomic.AtomicInteger; 44 45 @Test 46 public class CondyReturnPrimitiveTest { 47 // Counter for number of BSM calls 48 // Use of an AtomicInteger is not strictly necessary in this test 49 // since the BSM is not be called concurrently, but in general 50 // a BSM can be called concurrently for linking different or the *same* 51 // constant so care should be taken if a BSM operates on shared state 52 static final AtomicInteger callCount = new AtomicInteger(); 53 // Generated class with methods containing condy ldc 54 Class<?> gc; 55 56 // Bootstrap method used to represent primitive values 57 // that cannot be represented directly in the constant pool, 58 // such as byte, and for completeness of testing primitive values 59 // that can be represented directly, such as double or long that 60 // take two slots 61 public static Object intConversion(MethodHandles.Lookup l, 62 String constantName, 63 Class<?> constantType, 64 int value) { 65 callCount.getAndIncrement(); 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 UnsupportedOperationException(); 92 } 93 } 94 95 @BeforeClass 96 public void generateClass() throws Exception { 97 String genClassName = CondyReturnPrimitiveTest.class.getSimpleName() + "$Code"; 98 String bsmClassName = CondyReturnPrimitiveTest.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 int expectedCallCount = callCount.get(); 224 225 // Ensure when run a second time that the bootstrap method is not 226 // invoked and the constants are cached 227 testConstants(); 228 Assert.assertEquals(callCount.get(), expectedCallCount); 229 } 230 231 @Test 232 public void testRefs() throws Exception { 233 testConstant("string", "string"); 234 testConstant("stringArray", new String[]{"string", "string"}); 235 } 236 237 void testConstants() throws Exception { 238 // Note: for the _asType methods the BSM returns an int which is 239 // then converted by an asType transformation 240 241 testConstant("B", Byte.MAX_VALUE); 242 testConstant("C", Character.MAX_VALUE); 243 testConstant("D", (double) Integer.MAX_VALUE); 244 testConstant("D_AsType", (double) Integer.MAX_VALUE); 245 testConstant("F", (float) Integer.MAX_VALUE); 246 testConstant("F_AsType", (float) Integer.MAX_VALUE); 247 testConstant("I", Integer.MAX_VALUE); 248 testConstant("J", (long) Integer.MAX_VALUE); 249 testConstant("J_AsType", (long) Integer.MAX_VALUE); 250 testConstant("S", Short.MAX_VALUE); 251 testConstant("Z_F", false); 252 testConstant("Z_T", true); 253 testConstant("null", null); 254 } 255 256 void testConstant(String name, Object expected) throws Exception { 257 Method m = gc.getDeclaredMethod(name); 258 Assert.assertEquals(m.invoke(null), expected); 259 } 260 }