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 8174954 27 * @library /test/lib 28 * @modules java.base/jdk.internal.org.objectweb.asm 29 * @compile -XDignore.symbol.file BSMCalledTwice.java 30 * @run main BSMCalledTwice 31 */ 32 33 import java.io.File; 34 import java.io.FileOutputStream; 35 import java.util.*; 36 import static jdk.internal.org.objectweb.asm.Opcodes.*; 37 import jdk.internal.org.objectweb.asm.*; 38 import jdk.test.lib.process.ProcessTools; 39 import jdk.test.lib.process.OutputAnalyzer; 40 41 // BSMCalledTwice generates a class file named "TestC.class" that contains 42 // bytecodes that represent the following program 43 // 44 // public class TestC { 45 // public static void main(java.lang.String[] arg) { 46 // for (int i=0; i < 2; i++) { 47 // try { 48 // String f = "friend"; 49 // 50 // // The "hello " + f in the following statement produces an 51 // // invokedynamic with a BSM of 52 // // StringConcatFactory.java/makeConcatWithConstants. 53 // // The ASM below erroneously puts 2 static arguments, "hello " 54 // // and "goodbye" on the stack for the BSM. Causing a exception to 55 // // be thrown when creatingthe CallSite object. 56 // System.out.println("hello " + f); <--------------- invokedynamic 57 // 58 // } catch (Error e) { 59 // System.out.println("Caught Error:"); 60 // System.out.println(e.getMessage()); 61 // e.printStackTrace(); 62 // } 63 // } 64 // } 65 // } 66 // 67 public class BSMCalledTwice implements Opcodes { 68 static final String classTestCName = "TestC"; 69 count_makeSite(String text)70 public static int count_makeSite(String text) { 71 int count = 0; 72 String text_ptr = text; 73 while (text_ptr.indexOf("makeSite") != -1) { 74 text_ptr = text_ptr.substring(text_ptr.indexOf("makeSite") + 1); 75 count++; 76 } 77 return count; 78 } 79 main(String[] args)80 public static void main(String[] args) throws Exception { 81 ClassLoader cl = new ClassLoader() { 82 public Class<?> loadClass(String name) throws ClassNotFoundException { 83 if (findLoadedClass(name) != null) { 84 return findLoadedClass(name); 85 } 86 87 if (classTestCName.equals(name)) { 88 byte[] classFile = null; 89 try { 90 classFile = dumpTestC(); 91 } catch (Exception e) { 92 } 93 return defineClass(classTestCName, classFile, 0, classFile.length); 94 } 95 return super.loadClass(name); 96 } 97 }; 98 99 cl.loadClass(classTestCName); 100 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "-cp", ".", classTestCName); 101 OutputAnalyzer output = new OutputAnalyzer(pb.start()); 102 String test_output = output.getOutput(); 103 if (test_output == null) { 104 throw new RuntimeException("Test failed, null test output"); 105 } 106 // "makeSite" is currently listed twice in the exception stacks for each 107 // failing call to the BootstrapMethod. So more that two calls means 108 // that the BootstrapMethod was called more than once. 109 int count = count_makeSite(test_output); 110 if (count < 1 || count > 2) { 111 throw new RuntimeException("Test failed, bad number of calls to BootstrapMethod"); 112 } 113 output.shouldHaveExitValue(0); 114 } 115 dumpTestC()116 public static byte[] dumpTestC () throws Exception { 117 ClassWriter cw = new ClassWriter(0); 118 FieldVisitor fv; 119 MethodVisitor mv; 120 AnnotationVisitor av0; 121 122 cw.visit(53, ACC_PUBLIC + ACC_SUPER, classTestCName, null, "java/lang/Object", null); 123 124 cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", 125 "java/lang/invoke/MethodHandles", "Lookup", 126 ACC_PUBLIC + ACC_FINAL + ACC_STATIC); 127 128 { 129 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 130 mv.visitCode(); 131 mv.visitVarInsn(ALOAD, 0); 132 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 133 mv.visitInsn(RETURN); 134 mv.visitMaxs(1, 1); 135 mv.visitEnd(); 136 } 137 { 138 mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); 139 mv.visitCode(); 140 Label l0 = new Label(); 141 Label l1 = new Label(); 142 Label l2 = new Label(); 143 mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Error"); 144 mv.visitInsn(ICONST_0); 145 mv.visitVarInsn(ISTORE, 1); 146 Label l3 = new Label(); 147 mv.visitLabel(l3); 148 mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {Opcodes.INTEGER}, 0, null); 149 mv.visitVarInsn(ILOAD, 1); 150 mv.visitInsn(ICONST_2); 151 Label l4 = new Label(); 152 mv.visitJumpInsn(IF_ICMPGE, l4); 153 mv.visitLabel(l0); 154 mv.visitLdcInsn("friend"); 155 mv.visitVarInsn(ASTORE, 2); 156 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 157 mv.visitVarInsn(ALOAD, 2); 158 mv.visitInvokeDynamicInsn("makeConcatWithConstants", 159 "(Ljava/lang/String;)Ljava/lang/String;", 160 new Handle(Opcodes.H_INVOKESTATIC, 161 "java/lang/invoke/StringConcatFactory", 162 "makeConcatWithConstants", 163 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"), 164 new Object[]{"hello \u0001", "goodbye"}); 165 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 166 mv.visitLabel(l1); 167 Label l5 = new Label(); 168 mv.visitJumpInsn(GOTO, l5); 169 mv.visitLabel(l2); 170 mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Error"}); 171 mv.visitVarInsn(ASTORE, 2); 172 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 173 mv.visitLdcInsn("Caught Error:"); 174 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 175 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 176 mv.visitVarInsn(ALOAD, 2); 177 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Error", "getMessage", "()Ljava/lang/String;", false); 178 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 179 mv.visitVarInsn(ALOAD, 2); 180 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Error", "printStackTrace", "()V", false); 181 mv.visitLabel(l5); 182 mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 183 mv.visitIincInsn(1, 1); 184 mv.visitJumpInsn(GOTO, l3); 185 mv.visitLabel(l4); 186 mv.visitFrame(Opcodes.F_CHOP,1, null, 0, null); 187 mv.visitInsn(RETURN); 188 mv.visitMaxs(2, 3); 189 mv.visitEnd(); 190 } 191 cw.visitEnd(); 192 193 try(FileOutputStream fos = new FileOutputStream(new File("TestC.class"))) { 194 fos.write(cw.toByteArray()); 195 } 196 return cw.toByteArray(); 197 } 198 } 199