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 }