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 27 * @summary Test nested dynamic constant declarations that are recursive 28 * @compile CondyNestedTest_Code.jcod 29 * @run testng CondyNestedTest 30 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNestedTest 31 */ 32 33 import org.testng.Assert; 34 import org.testng.annotations.BeforeClass; 35 import org.testng.annotations.Test; 36 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 40 public class CondyNestedTest { 41 42 static final Class[] THROWABLES = {InvocationTargetException.class, StackOverflowError.class}; 43 44 Class<?> c; 45 46 // Add the following annotations to the test description if uncommenting the 47 // following code 48 // 49 // * @library /lib/testlibrary/bytecode 50 // * @build jdk.experimental.bytecode.BasicClassBuilder 51 // 52 // static final MethodHandles.Lookup L = MethodHandles.lookup(); 53 // 54 // /** 55 // * Generate class file bytes for a class named CondyNestedTest_Code 56 // * whose bytes are converted to a jcod file: 57 // * 58 // * java -jar asmtools.jar jdec CondyNestedTest_Code.class > 59 // * CondyNestedTest_Code.jcod 60 // * 61 // * which was then edited so that dynamic constant declarations are 62 // * recursive both for an ldc or invokedynamic (specifically declaring a 63 // * BSM+attributes whose static argument is a dynamic constant 64 // * that refers to the same BSM+attributes). 65 // */ 66 // public static byte[] generator() throws Exception { 67 // String genClassName = L.lookupClass().getSimpleName() + "_Code"; 68 // String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString(); 69 // String bsmIndyDescriptor = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString(); 70 // 71 // byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0) 72 // .withSuperclass("java/lang/Object") 73 // .withMethod("<init>", "()V", M -> 74 // M.withFlags(Flag.ACC_PUBLIC) 75 // .withCode(TypedCodeBuilder::new, C -> 76 // C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_() 77 // )) 78 // .withMethod("main", "([Ljava/lang/String;)V", M -> 79 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 80 // .withCode(TypedCodeBuilder::new, C -> { 81 // C.aload_0().iconst_0().aaload(); 82 // C.invokevirtual("java/lang/String", "intern", "()Ljava/lang/String;", false); 83 // C.astore_1(); 84 // 85 // C.aload_1(); 86 // C.ldc("condy_bsm_condy_bsm"); 87 // C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE1"); 88 // C.invokestatic(genClassName, "condy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_(); 89 // 90 // C.label("CASE1"); 91 // C.aload_1(); 92 // C.ldc("indy_bsmIndy_condy_bsm"); 93 // C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE2"); 94 // C.invokestatic(genClassName, "indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", false).return_(); 95 // 96 // C.label("CASE2"); 97 // C.aload_1(); 98 // C.ldc("indy_bsm_condy_bsm"); 99 // C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE3"); 100 // C.invokestatic(genClassName, "indy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_(); 101 // 102 // C.label("CASE3"); 103 // C.return_(); 104 // })) 105 // .withMethod("bsm", bsmDescriptor, M -> 106 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 107 // .withCode(TypedCodeBuilder::new, C -> { 108 // C.aload_2(); 109 // C.instanceof_("java/lang/invoke/MethodType"); 110 // C.iconst_0(); 111 // C.ifcmp(TypeTag.I, MacroCodeBuilder.CondKind.EQ, "CONDY"); 112 // C.new_("java/lang/invoke/ConstantCallSite").dup(); 113 // C.ldc("java/lang/String", PoolHelper::putClass); 114 // C.aload_1(); 115 // C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); 116 // C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false); 117 // C.areturn(); 118 // C.label("CONDY"); 119 // C.aload_1().areturn(); 120 // })) 121 // .withMethod("bsmIndy", bsmIndyDescriptor, M -> 122 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 123 // .withCode(TypedCodeBuilder::new, C -> { 124 // C.new_("java/lang/invoke/ConstantCallSite").dup(); 125 // C.ldc("java/lang/String", PoolHelper::putClass); 126 // C.aload_1(); 127 // C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); 128 // C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false); 129 // C.areturn(); 130 // })) 131 // .withMethod("condy_bsm_condy_bsm", "()Ljava/lang/Object;", M -> 132 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 133 // .withCode(TypedCodeBuilder::new, C -> 134 // C.ldc("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor, 135 // S -> S.add(null, (P, v) -> { 136 // return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor, 137 // S2 -> S2.add("DUMMY_ARG", PoolHelper::putString)); 138 // })) 139 // .areturn())) 140 // .withMethod("indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", M -> 141 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 142 // .withCode(TypedCodeBuilder::new, C -> 143 // C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsmIndy", bsmIndyDescriptor, 144 // S -> S.add(null, (P, v) -> { 145 // return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor, 146 // S2 -> S2.add("DUMMY_ARG", PoolHelper::putString)); 147 // })) 148 // .areturn())) 149 // .withMethod("indy_bsm_condy_bsm", "()Ljava/lang/Object;", M -> 150 // M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC) 151 // .withCode(TypedCodeBuilder::new, C -> 152 // C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsm", bsmDescriptor, 153 // S -> S.add(null, (P, v) -> { 154 // return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor, 155 // S2 -> S2.add("DUMMY_ARG", PoolHelper::putString)); 156 // })) 157 // .areturn())) 158 // .build(); 159 // 160 // File f = new File(genClassName + ".class"); 161 // if (f.getParentFile() != null) { 162 // f.getParentFile().mkdirs(); 163 // } 164 // new FileOutputStream(f).write(byteArray); 165 // return byteArray; 166 // 167 // } 168 test(Method m, Class<? extends Throwable>... ts)169 static void test(Method m, Class<? extends Throwable>... ts) { 170 Throwable caught = null; 171 try { 172 m.invoke(null); 173 } 174 catch (Throwable t) { 175 caught = t; 176 } 177 178 if (caught == null) { 179 Assert.fail("Throwable expected"); 180 } 181 182 String actualMessage = null; 183 for (int i = 0; i < ts.length; i++) { 184 actualMessage = caught.getMessage(); 185 Assert.assertNotNull(caught); 186 Assert.assertTrue(ts[i].isAssignableFrom(caught.getClass())); 187 caught = caught.getCause(); 188 } 189 } 190 191 @BeforeClass findClass()192 public void findClass() throws Exception { 193 c = Class.forName("CondyNestedTest_Code"); 194 } 195 196 /** 197 * Testing an ldc of a dynamic constant, C say, with a BSM whose static 198 * argument is C. 199 */ 200 @Test testCondyBsmCondyBsm()201 public void testCondyBsmCondyBsm() throws Exception { 202 test("condy_bsm_condy_bsm", THROWABLES); 203 } 204 205 /** 206 * Testing an invokedynamic with a BSM whose static argument is a constant 207 * dynamic, C say, with a BSM whose static argument is C. 208 */ 209 @Test testIndyBsmIndyCondyBsm()210 public void testIndyBsmIndyCondyBsm() throws Exception { 211 test("indy_bsmIndy_condy_bsm", THROWABLES); 212 } 213 214 /** 215 * Testing an invokedynamic with a BSM, B say, whose static argument is 216 * a dynamic constant, C say, that uses BSM B. 217 */ 218 @Test testIndyBsmCondyBsm()219 public void testIndyBsmCondyBsm() throws Exception { 220 test("indy_bsm_condy_bsm", THROWABLES); 221 } 222 test(String methodName, Class<? extends Throwable>... ts)223 void test(String methodName, Class<? extends Throwable>... ts) throws Exception { 224 Method m = c.getMethod(methodName); 225 m.setAccessible(true); 226 test(m, ts); 227 } 228 229 } 230