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