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 /test/lib/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
  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
  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
 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
 233     public void testRefs() throws Exception {
 234         testConstant("string", "string");
 235         testConstant("stringArray", new String[]{"string", "string"});
 236     }
 237 
 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 
 257     void testConstant(String name, Object expected) throws Exception {
 258         Method m = gc.getDeclaredMethod(name);
 259         Assert.assertEquals(m.invoke(null), expected);
 260     }
 261 }