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 }