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 bootstrap arguments for condy
 28  * @library /java/lang/invoke/common
 29  * @enablePreview
 30  * @run testng CondyStaticArgumentsTest
 31  * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyStaticArgumentsTest
 32  */
 33 
 34 import org.testng.Assert;
 35 import org.testng.annotations.Test;
 36 import test.java.lang.invoke.lib.InstructionHelper;
 37 
 38 import java.lang.constant.*;
 39 import java.lang.invoke.*;
 40 import java.lang.reflect.Method;
 41 import java.math.BigDecimal;
 42 import java.math.MathContext;
 43 import java.util.StringJoiner;
 44 import java.util.stream.Stream;
 45 
 46 import static java.lang.invoke.MethodType.methodType;
 47 
 48 public class CondyStaticArgumentsTest {
 49     static final MethodHandles.Lookup L = MethodHandles.lookup();
 50     private static final DirectMethodHandleDesc bigDecimalMhDesc = directMhDesc("bigDecimal");
 51     private static final DirectMethodHandleDesc mathContextMhDesc = directMhDesc("mathContext");
 52 
 53     static class BSMInfo {
 54         final String methodName;
 55         final MethodHandle handle;
 56         final String descriptor;
 57 
 58         BSMInfo(String name) {
 59             methodName = name;
 60 
 61             Method m = Stream.of(CondyStaticArgumentsTest.class.getDeclaredMethods())
 62                     .filter(x -> x.getName().equals(methodName)).findFirst()
 63                     .get();
 64             try {
 65                 handle = MethodHandles.lookup().unreflect(m);
 66             } catch (Exception e) {
 67                 throw new Error(e);
 68             }
 69             descriptor = handle.type().toMethodDescriptorString();
 70         }
 71 
 72         static BSMInfo of(String name) {
 73             return new BSMInfo(name);
 74         }
 75     }
 76 
 77     static String basicArgs(MethodHandles.Lookup l, String name, Class<?> type,
 78                             int i, long j, float f, double d,
 79                             Class<?> c, String s,
 80                             MethodType mt, MethodHandle mh) {
 81         return new StringJoiner("-")
 82                 .add(name)
 83                 .add(type.getSimpleName())
 84                 .add(Integer.toString(i))
 85                 .add(Long.toString(j))
 86                 .add(Float.toString(f))
 87                 .add(Double.toString(d))
 88                 .add(c.getSimpleName())
 89                 .add(s)
 90                 .add(mt.toString())
 91                 .add(Integer.toString(mh.type().parameterCount()))
 92                 .toString();
 93     }
 94 
 95     @Test
 96     public void testBasicArgs() throws Throwable {
 97         BSMInfo bi = BSMInfo.of("basicArgs");
 98         MethodHandleInfo mhi = MethodHandles.lookup().revealDirect(bi.handle);
 99 
100         MethodHandle mh = InstructionHelper.ldcDynamicConstant(
101                 L, "constant-name", String.class,
102                 bi.methodName, bi.handle.type(),
103                 1, 2L, 3.0f, 4.0d,
104                 ClassDesc.ofDescriptor(Number.class.descriptorString()),
105                 "something",
106                 MethodTypeDesc.ofDescriptor("(IJFD)V"),
107                 MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.valueOf(mhi.getReferenceKind()),
108                         ClassDesc.ofDescriptor(mhi.getDeclaringClass().descriptorString()),
109                         mhi.getName(), MethodTypeDesc.ofDescriptor(mhi.getMethodType().descriptorString()))
110         );
111 
112         Assert.assertEquals(mh.invoke(), "constant-name-String-1-2-3.0-4.0-Number-something-(int,long,float,double)void-11");
113     }
114 
115     static MathContext mathContext(MethodHandles.Lookup l, String value, Class<?> type) {
116         switch (value) {
117             case "UNLIMITED":
118                 return MathContext.UNLIMITED;
119             case "DECIMAL32":
120                 return MathContext.DECIMAL32;
121             case "DECIMAL64":
122                 return MathContext.DECIMAL64;
123             case "DECIMAL128":
124                 return MathContext.DECIMAL128;
125             default:
126                 throw new UnsupportedOperationException();
127         }
128     }
129 
130     static BigDecimal bigDecimal(MethodHandles.Lookup l, String name, Class<?> type,
131                                  String value, MathContext mc) {
132         return new BigDecimal(value, mc);
133     }
134 
135     static String condyWithCondy(MethodHandles.Lookup l, String name, Class<?> type,
136                                  BigDecimal d) {
137         return new StringJoiner("-")
138                 .add(name)
139                 .add(type.getSimpleName())
140                 .add(d.toString())
141                 .add(Integer.toString(d.precision()))
142                 .toString();
143     }
144 
145     @Test
146     public void testCondyWithCondy() throws Throwable {
147         BSMInfo bi = BSMInfo.of("condyWithCondy");
148 
149         MethodHandle mh = InstructionHelper.ldcDynamicConstant(
150                 L, "big-decimal-math-context", String.class,
151                 bi.methodName, bi.handle.type(),
152                 DynamicConstantDesc.ofNamed(
153                         bigDecimalMhDesc,
154                         "big-decimal",
155                         InstructionHelper.classDesc(BigDecimal.class),
156                         "3.14159265358979323846",
157                         DynamicConstantDesc.ofNamed(
158                                 mathContextMhDesc,
159                                 "DECIMAL32",
160                                 InstructionHelper.classDesc(MathContext.class)
161                         )
162                 )
163         );
164         Assert.assertEquals(mh.invoke(), "big-decimal-math-context-String-3.141593-7");
165     }
166 
167 
168     static ConstantCallSite indyWithCondy(MethodHandles.Lookup l, String name, MethodType type,
169                                           BigDecimal d) {
170         String s = new StringJoiner("-")
171                 .add(name)
172                 .add(type.toMethodDescriptorString())
173                 .add(d.toString())
174                 .add(Integer.toString(d.precision()))
175                 .toString();
176         return new ConstantCallSite(MethodHandles.constant(String.class, s));
177     }
178 
179     @Test
180     public void testIndyWithCondy() throws Throwable {
181         BSMInfo bi = BSMInfo.of("indyWithCondy");
182 
183         MethodHandle mh = InstructionHelper.invokedynamic(
184                 L, "big-decimal-math-context", methodType(String.class),
185                 bi.methodName, bi.handle.type(),
186                 DynamicConstantDesc.ofNamed(
187                         bigDecimalMhDesc,
188                         "big-decimal",
189                         InstructionHelper.classDesc(BigDecimal.class),
190                         "3.14159265358979323846",
191                         DynamicConstantDesc.ofNamed(
192                                 mathContextMhDesc,
193                                 "DECIMAL32",
194                                 InstructionHelper.classDesc(MathContext.class)
195                         )
196                 ));
197         Assert.assertEquals(mh.invoke(), "big-decimal-math-context-()Ljava/lang/String;-3.141593-7");
198     }
199 
200     private static DirectMethodHandleDesc directMhDesc(String methodName) {
201         MethodHandleInfo mhi = MethodHandles.lookup().revealDirect(BSMInfo.of(methodName).handle);
202         return MethodHandleDesc.of(
203                 DirectMethodHandleDesc.Kind.valueOf(mhi.getReferenceKind()),
204                 ClassDesc.ofDescriptor(mhi.getDeclaringClass().descriptorString()),
205                 mhi.getName(),
206                 mhi.getMethodType().descriptorString()
207         );
208     }
209 }