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