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 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyReturnPrimitiveTest 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.Method; 44 import java.util.concurrent.atomic.AtomicInteger; 45 46 @Test 47 public class CondyReturnPrimitiveTest { 48 // Counter for number of BSM calls 49 // Use of an AtomicInteger is not strictly necessary in this test 50 // since the BSM is not be called concurrently, but in general 51 // a BSM can be called concurrently for linking different or the *same* 52 // constant so care should be taken if a BSM operates on shared state 53 static final AtomicInteger callCount = new AtomicInteger(); 54 // Generated class with methods containing condy ldc 55 Class<?> gc; 56 57 // Bootstrap method used to represent primitive values 58 // that cannot be represented directly in the constant pool, 59 // such as byte, and for completeness of testing primitive values 60 // that can be represented directly, such as double or long that 61 // take two slots intConversion(MethodHandles.Lookup l, String constantName, Class<?> constantType, int value)62 public static Object intConversion(MethodHandles.Lookup l, 63 String constantName, 64 Class<?> constantType, 65 int value) { 66 callCount.getAndIncrement(); 67 68 switch (constantName) { 69 case "B": 70 return (byte) value; 71 case "C": 72 return (char) value; 73 case "D": 74 return (double) value; 75 case "F": 76 return (float) value; 77 case "I": 78 return value; 79 case "J": 80 return (long) value; 81 case "S": 82 return (short) value; 83 case "Z": 84 return value > 0; 85 case "nullRef": 86 return null; 87 case "string": 88 return "string"; 89 case "stringArray": 90 return new String[]{"string", "string"}; 91 default: 92 throw new UnsupportedOperationException(); 93 } 94 } 95 96 @BeforeClass generateClass()97 public void generateClass() throws Exception { 98 String genClassName = CondyReturnPrimitiveTest.class.getSimpleName() + "$Code"; 99 String bsmClassName = CondyReturnPrimitiveTest.class.getCanonicalName().replace('.', '/'); 100 String bsmMethodName = "intConversion"; 101 String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, 102 String.class, Class.class, int.class).toMethodDescriptorString(); 103 104 byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0) 105 .withSuperclass("java/lang/Object") 106 .withMethod("<init>", "()V", M -> 107 M.withFlags(Flag.ACC_PUBLIC) 108 .withCode(TypedCodeBuilder::new, C -> 109 C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_() 110 )) 111 .withMethod("B", "()B", M -> 112 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 113 .withCode(TypedCodeBuilder::new, C -> 114 C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor, 115 S -> S.add(Byte.MAX_VALUE)) 116 .ireturn() 117 )) 118 .withMethod("C", "()C", M -> 119 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 120 .withCode(TypedCodeBuilder::new, C -> 121 C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor, 122 S -> S.add(Character.MAX_VALUE)) 123 .ireturn() 124 )) 125 .withMethod("D", "()D", M -> 126 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 127 .withCode(TypedCodeBuilder::new, C -> 128 C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor, 129 S -> S.add(Integer.MAX_VALUE)) 130 .dreturn() 131 )) 132 .withMethod("D_AsType", "()D", M -> 133 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 134 .withCode(TypedCodeBuilder::new, C -> 135 C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor, 136 S -> S.add(Integer.MAX_VALUE)) 137 .dreturn() 138 )) 139 .withMethod("F", "()F", M -> 140 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 141 .withCode(TypedCodeBuilder::new, C -> 142 C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor, 143 S -> S.add(Integer.MAX_VALUE)) 144 .freturn() 145 )) 146 .withMethod("F_AsType", "()F", M -> 147 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 148 .withCode(TypedCodeBuilder::new, C -> 149 C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor, 150 S -> S.add(Integer.MAX_VALUE)) 151 .freturn() 152 )) 153 .withMethod("I", "()I", M -> 154 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 155 .withCode(TypedCodeBuilder::new, C -> 156 C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor, 157 S -> S.add(Integer.MAX_VALUE)) 158 .ireturn() 159 )) 160 .withMethod("J", "()J", M -> 161 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 162 .withCode(TypedCodeBuilder::new, C -> 163 C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor, 164 S -> S.add(Integer.MAX_VALUE)) 165 .lreturn() 166 )) 167 .withMethod("J_AsType", "()J", M -> 168 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 169 .withCode(TypedCodeBuilder::new, C -> 170 C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor, 171 S -> S.add(Integer.MAX_VALUE)) 172 .lreturn() 173 )) 174 .withMethod("S", "()S", M -> 175 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 176 .withCode(TypedCodeBuilder::new, C -> 177 C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor, 178 S -> S.add(Short.MAX_VALUE)) 179 .ireturn() 180 )) 181 .withMethod("Z_F", "()Z", M -> 182 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 183 .withCode(TypedCodeBuilder::new, C -> 184 C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor, 185 S -> S.add(0)) 186 .ireturn() 187 )) 188 .withMethod("Z_T", "()Z", M -> 189 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 190 .withCode(TypedCodeBuilder::new, C -> 191 C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor, 192 S -> S.add(1)) 193 .ireturn() 194 )) 195 .withMethod("null", "()Ljava/lang/Object;", M -> 196 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 197 .withCode(TypedCodeBuilder::new, C -> 198 C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor, 199 S -> S.add(Integer.MAX_VALUE)) 200 .areturn() 201 )) 202 .withMethod("string", "()Ljava/lang/String;", M -> 203 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 204 .withCode(TypedCodeBuilder::new, C -> 205 C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor, 206 S -> S.add(Integer.MAX_VALUE)) 207 .areturn() 208 )) 209 .withMethod("stringArray", "()[Ljava/lang/String;", M -> 210 M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 211 .withCode(TypedCodeBuilder::new, C -> 212 C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor, 213 S -> S.add(Integer.MAX_VALUE)) 214 .areturn() 215 )) 216 .build(); 217 218 gc = MethodHandles.lookup().defineClass(byteArray); 219 } 220 221 @Test testPrimitives()222 public void testPrimitives() throws Exception { 223 testConstants(); 224 int expectedCallCount = callCount.get(); 225 226 // Ensure when run a second time that the bootstrap method is not 227 // invoked and the constants are cached 228 testConstants(); 229 Assert.assertEquals(callCount.get(), expectedCallCount); 230 } 231 232 @Test testRefs()233 public void testRefs() throws Exception { 234 testConstant("string", "string"); 235 testConstant("stringArray", new String[]{"string", "string"}); 236 } 237 testConstants()238 void testConstants() throws Exception { 239 // Note: for the _asType methods the BSM returns an int which is 240 // then converted by an asType transformation 241 242 testConstant("B", Byte.MAX_VALUE); 243 testConstant("C", Character.MAX_VALUE); 244 testConstant("D", (double) Integer.MAX_VALUE); 245 testConstant("D_AsType", (double) Integer.MAX_VALUE); 246 testConstant("F", (float) Integer.MAX_VALUE); 247 testConstant("F_AsType", (float) Integer.MAX_VALUE); 248 testConstant("I", Integer.MAX_VALUE); 249 testConstant("J", (long) Integer.MAX_VALUE); 250 testConstant("J_AsType", (long) Integer.MAX_VALUE); 251 testConstant("S", Short.MAX_VALUE); 252 testConstant("Z_F", false); 253 testConstant("Z_T", true); 254 testConstant("null", null); 255 } 256 testConstant(String name, Object expected)257 void testConstant(String name, Object expected) throws Exception { 258 Method m = gc.getDeclaredMethod(name); 259 Assert.assertEquals(m.invoke(null), expected); 260 } 261 } 262