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 methods returning the wrong type
 28  * @library /java/lang/invoke/common
 29  * @build test.java.lang.invoke.lib.InstructionHelper
 30  * @enablePreview
 31  * @run testng CondyWrongType
 32  * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWrongType
 33  */
 34 
 35 import org.testng.Assert;
 36 import org.testng.annotations.DataProvider;
 37 import org.testng.annotations.Test;
 38 import test.java.lang.invoke.lib.InstructionHelper;
 39 
 40 import java.lang.invoke.MethodHandle;
 41 import java.lang.invoke.MethodHandles;
 42 import java.lang.invoke.MethodType;
 43 import java.lang.invoke.WrongMethodTypeException;
 44 import java.math.BigDecimal;
 45 import java.util.ArrayList;
 46 import java.util.List;
 47 import java.util.Map;
 48 
 49 import static java.lang.invoke.MethodType.methodType;
 50 
 51 public class CondyWrongType {
 52 
 53     @DataProvider
 54     public Object[][] primitivesProvider() throws Exception {
 55         Map<String, Class<?>> typeMap = Map.of(
 56                 "B", byte.class,
 57                 "C", char.class,
 58                 "D", double.class,
 59                 "F", float.class,
 60                 "I", int.class,
 61                 "J", long.class,
 62                 "S", short.class,
 63                 "Z", boolean.class
 64         );
 65 
 66         List<Object[]> cases = new ArrayList<>();
 67         for (String name : typeMap.keySet()) {
 68             MethodHandle zero = MethodHandles.zero(typeMap.get(name));
 69             for (String type : typeMap.keySet()) {
 70                 // Use asType transformation to detect if primitive conversion
 71                 // is supported from the BSM value type to the dynamic constant type
 72                 boolean pass = true;
 73                 try {
 74                     zero.asType(MethodType.methodType(typeMap.get(type)));
 75                 } catch (WrongMethodTypeException e) {
 76                     pass = false;
 77                 }
 78                 cases.add(new Object[]{name, type, pass});
 79             }
 80         }
 81 
 82         return cases.stream().toArray(Object[][]::new);
 83     }
 84 
 85     @Test(dataProvider = "primitivesProvider")
 86     public void testPrimitives(String name, String type, boolean pass) {
 87         test(name, type, pass);
 88     }
 89 
 90     @Test
 91     public void testReferences() {
 92         test("String", "Ljava/math/BigDecimal;", false);
 93         test("BigDecimal", "Ljava/lang/String;", false);
 94     }
 95 
 96     @Test
 97     public void testReferenceAndPrimitives() {
 98         test("String", "B", false);
 99         test("String", "C", false);
100         test("String", "D", false);
101         test("String", "F", false);
102         test("String", "I", false);
103         test("String", "J", false);
104         test("String", "S", false);
105         test("String", "Z", false);
106     }
107 
108     static void test(String name, String type, boolean pass) {
109         MethodHandle mh = caster(name, type);
110         Throwable caught = null;
111         try {
112             mh.invoke();
113         } catch (Throwable t) {
114             caught = t;
115         }
116 
117         if (caught == null) {
118             if (pass) {
119                 return;
120             } else {
121                 Assert.fail("Throwable expected");
122             }
123         } else if (pass) {
124             Assert.fail("Throwable not expected");
125         }
126 
127         Assert.assertTrue(BootstrapMethodError.class.isAssignableFrom(caught.getClass()));
128         caught = caught.getCause();
129         Assert.assertNotNull(caught);
130         Assert.assertTrue(ClassCastException.class.isAssignableFrom(caught.getClass()));
131     }
132 
133     static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
134         switch (name) {
135             case "B":
136                 return (byte) 1;
137             case "C":
138                 return 'A';
139             case "D":
140                 return 1.0;
141             case "F":
142                 return 1.0f;
143             case "I":
144                 return 1;
145             case "J":
146                 return 1L;
147             case "S":
148                 return (short) 1;
149             case "Z":
150                 return true;
151             case "String":
152                 return "string";
153             case "BigDecimal":
154                 return BigDecimal.ONE;
155             default:
156                 throw new UnsupportedOperationException();
157         }
158     }
159 
160     static MethodHandle caster(String name, String type) {
161         try {
162             return InstructionHelper.ldcDynamicConstant(
163                     MethodHandles.lookup(),
164                     name, type,
165                     "bsm",
166                     methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).descriptorString());
167         } catch (Exception e) {
168             throw new Error(e);
169         }
170     }
171 }