1 /*
   2  * Copyright (c) 2017, 2018, 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 8195694
  27  * @summary Test dynamic constant bootstraps
  28  * @library /lib/testlibrary/bytecode /java/lang/invoke/common
  29  * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
  30  * @run testng ConstantBootstrapsTest
  31  */
  32 
  33 import jdk.experimental.bytecode.PoolHelper;
  34 import org.testng.annotations.Test;
  35 import test.java.lang.invoke.lib.InstructionHelper;
  36 
  37 import java.lang.invoke.ConstantBootstraps;
  38 import java.lang.invoke.MethodHandle;
  39 import java.lang.invoke.MethodHandleInfo;
  40 import java.lang.invoke.MethodHandles;
  41 import java.lang.invoke.MethodType;
  42 import java.lang.invoke.VarHandle;
  43 import java.lang.invoke.WrongMethodTypeException;
  44 import java.math.BigInteger;
  45 import java.util.Collection;
  46 import java.util.List;
  47 import java.util.Map;
  48 
  49 import static org.testng.Assert.assertEquals;
  50 import static org.testng.Assert.assertNull;
  51 
  52 @Test
  53 public class ConstantBootstrapsTest {
  54     static final MethodHandles.Lookup L = MethodHandles.lookup();
  55 
  56     static MethodType lookupMT(Class<?> ret, Class<?>... params) {
  57         return MethodType.methodType(ret, MethodHandles.Lookup.class, String.class, Class.class).
  58                 appendParameterTypes(params);
  59     }
  60 
  61     public void testNullConstant() throws Throwable {
  62         var handle = InstructionHelper.ldcDynamicConstant(L, "_", Object.class,
  63                                                           ConstantBootstraps.class, "nullConstant", lookupMT(Object.class),
  64                                                           S -> {});
  65         assertNull(handle.invoke());
  66 
  67         handle = InstructionHelper.ldcDynamicConstant(L, "_", MethodType.class,
  68                                                       ConstantBootstraps.class, "nullConstant", lookupMT(Object.class),
  69                                                       S -> {});
  70         assertNull(handle.invoke());
  71     }
  72 
  73     @Test(expectedExceptions = IllegalArgumentException.class)
  74     public void testNullConstantPrimitiveClass() {
  75         ConstantBootstraps.nullConstant(MethodHandles.lookup(), null, int.class);
  76     }
  77 
  78 
  79     public void testPrimitiveClass() throws Throwable {
  80         var pm = Map.of(
  81                 "I", int.class,
  82                 "J", long.class,
  83                 "S", short.class,
  84                 "B", byte.class,
  85                 "C", char.class,
  86                 "F", float.class,
  87                 "D", double.class,
  88                 "Z", boolean.class,
  89                 "V", void.class
  90         );
  91 
  92         for (var desc : pm.keySet()) {
  93             var handle = InstructionHelper.ldcDynamicConstant(L, desc, Class.class,
  94                                                               ConstantBootstraps.class, "primitiveClass", lookupMT(Class.class),
  95                                                               S -> {});
  96             assertEquals(handle.invoke(), pm.get(desc));
  97         }
  98     }
  99 
 100     @Test(expectedExceptions = NullPointerException.class)
 101     public void testPrimitiveClassNullName() {
 102         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), null, Class.class);
 103     }
 104 
 105     @Test(expectedExceptions = NullPointerException.class)
 106     public void testPrimitiveClassNullType() {
 107         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "I", null);
 108     }
 109 
 110     @Test(expectedExceptions = IllegalArgumentException.class)
 111     public void testPrimitiveClassEmptyName() {
 112         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "", Class.class);
 113     }
 114 
 115     @Test(expectedExceptions = IllegalArgumentException.class)
 116     public void testPrimitiveClassWrongNameChar() {
 117         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "L", Class.class);
 118     }
 119 
 120     @Test(expectedExceptions = IllegalArgumentException.class)
 121     public void testPrimitiveClassWrongNameString() {
 122         ConstantBootstraps.primitiveClass(MethodHandles.lookup(), "Ljava/lang/Object;", Class.class);
 123     }
 124 
 125 
 126     public void testEnumConstant() throws Throwable {
 127         for (var v : StackWalker.Option.values()) {
 128             var handle = InstructionHelper.ldcDynamicConstant(L, v.name(), StackWalker.Option.class,
 129                                                               ConstantBootstraps.class, "enumConstant", lookupMT(Enum.class),
 130                                                               S -> { });
 131             assertEquals(handle.invoke(), v);
 132         }
 133     }
 134 
 135     @Test(expectedExceptions = IllegalArgumentException.class)
 136     public void testEnumConstantUnknown() {
 137         ConstantBootstraps.enumConstant(MethodHandles.lookup(), "DOES_NOT_EXIST", StackWalker.Option.class);
 138     }
 139 
 140 
 141     public void testGetStaticDecl() throws Throwable {
 142         var handle = InstructionHelper.ldcDynamicConstant(L, "TYPE", Class.class,
 143                                                           ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class, Class.class),
 144                                                           S -> { S.add("java/lang/Integer", PoolHelper::putClass); });
 145         assertEquals(handle.invoke(), int.class);
 146     }
 147 
 148     public void testGetStaticSelf() throws Throwable {
 149         var handle = InstructionHelper.ldcDynamicConstant(L, "MAX_VALUE", int.class,
 150                                                           ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class),
 151                                                           S -> { });
 152         assertEquals(handle.invoke(), Integer.MAX_VALUE);
 153 
 154 
 155         handle = InstructionHelper.ldcDynamicConstant(L, "ZERO", BigInteger.class,
 156                                                       ConstantBootstraps.class, "getStaticFinal", lookupMT(Object.class),
 157                                                       S -> { });
 158         assertEquals(handle.invoke(), BigInteger.ZERO);
 159     }
 160 
 161 
 162     public void testInvoke() throws Throwable {
 163         var handle = InstructionHelper.ldcDynamicConstant(
 164                 L, "_", List.class,
 165                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
 166                 S -> {
 167                     S.add("", (P, Z) -> {
 168                         return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/util/List", "of",
 169                                            MethodType.methodType(List.class, Object[].class).toMethodDescriptorString(),
 170                                            true);
 171                     });
 172                     S.add(1).add(2).add(3).add(4);
 173                 });
 174         assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
 175     }
 176 
 177     public void testInvokeAsType() throws Throwable {
 178         var handle = InstructionHelper.ldcDynamicConstant(
 179                 L, "_", int.class,
 180                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
 181                 S -> {
 182                     S.add("", (P, Z) -> {
 183                         return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/lang/Integer", "valueOf",
 184                                            MethodType.methodType(Integer.class, String.class).toMethodDescriptorString(),
 185                                            false);
 186                     });
 187                     S.add("42");
 188                 });
 189         assertEquals(handle.invoke(), 42);
 190     }
 191 
 192     public void testInvokeAsTypeVariableArity() throws Throwable {
 193         // The constant type is Collection but the invoke return type is List
 194         var handle = InstructionHelper.ldcDynamicConstant(
 195                 L, "_", Collection.class,
 196                 ConstantBootstraps.class, "invoke", lookupMT(Object.class, MethodHandle.class, Object[].class),
 197                 S -> {
 198                     S.add("", (P, Z) -> {
 199                         return P.putHandle(MethodHandleInfo.REF_invokeStatic, "java/util/List", "of",
 200                                            MethodType.methodType(List.class, Object[].class).toMethodDescriptorString(),
 201                                            true);
 202                     });
 203                     S.add(1).add(2).add(3).add(4);
 204                 });
 205         assertEquals(handle.invoke(), List.of(1, 2, 3, 4));
 206     }
 207 
 208     @Test(expectedExceptions = ClassCastException.class)
 209     public void testInvokeAsTypeClassCast() throws Throwable {
 210         ConstantBootstraps.invoke(MethodHandles.lookup(), "_", String.class,
 211                                   MethodHandles.lookup().findStatic(Integer.class, "valueOf", MethodType.methodType(Integer.class, String.class)),
 212                                   "42");
 213     }
 214 
 215     @Test(expectedExceptions = WrongMethodTypeException.class)
 216     public void testInvokeAsTypeWrongReturnType() throws Throwable {
 217         ConstantBootstraps.invoke(MethodHandles.lookup(), "_", short.class,
 218                                   MethodHandles.lookup().findStatic(Integer.class, "parseInt", MethodType.methodType(int.class, String.class)),
 219                                   "42");
 220     }
 221 
 222 
 223     static class X {
 224         public String f;
 225         public static String sf;
 226     }
 227 
 228     public void testVarHandleField() throws Throwable {
 229         var handle = InstructionHelper.ldcDynamicConstant(
 230                 L, "f", VarHandle.class,
 231                 ConstantBootstraps.class, "fieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
 232                 S -> {
 233                     S.add(X.class.getName().replace('.', '/'), PoolHelper::putClass).
 234                             add("java/lang/String", PoolHelper::putClass);
 235                 });
 236 
 237         var vhandle = (VarHandle) handle.invoke();
 238         assertEquals(vhandle.varType(), String.class);
 239         assertEquals(vhandle.coordinateTypes(), List.of(X.class));
 240     }
 241 
 242     public void testVarHandleStaticField() throws Throwable {
 243         var handle = InstructionHelper.ldcDynamicConstant(
 244                 L, "sf", VarHandle.class,
 245                 ConstantBootstraps.class, "staticFieldVarHandle", lookupMT(VarHandle.class, Class.class, Class.class),
 246                 S -> {
 247                     S.add(X.class.getName().replace('.', '/'), PoolHelper::putClass).
 248                             add("java/lang/String", PoolHelper::putClass);
 249                 });
 250 
 251         var vhandle = (VarHandle) handle.invoke();
 252         assertEquals(vhandle.varType(), String.class);
 253         assertEquals(vhandle.coordinateTypes(), List.of());
 254     }
 255 
 256     public void testVarHandleArray() throws Throwable {
 257         var handle = InstructionHelper.ldcDynamicConstant(
 258                 L, "_", VarHandle.class,
 259                 ConstantBootstraps.class, "arrayVarHandle", lookupMT(VarHandle.class, Class.class),
 260                 S -> {
 261                     S.add(String[].class.getName().replace('.', '/'), PoolHelper::putClass);
 262                 });
 263 
 264         var vhandle = (VarHandle) handle.invoke();
 265         assertEquals(vhandle.varType(), String.class);
 266         assertEquals(vhandle.coordinateTypes(), List.of(String[].class, int.class));
 267     }
 268 }