1 /* 2 * Copyright (c) 2015, 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 package compiler.calls.common; 25 26 import jdk.internal.org.objectweb.asm.ClassReader; 27 import jdk.internal.org.objectweb.asm.ClassVisitor; 28 import jdk.internal.org.objectweb.asm.ClassWriter; 29 import jdk.internal.org.objectweb.asm.Handle; 30 import jdk.internal.org.objectweb.asm.Label; 31 import jdk.internal.org.objectweb.asm.MethodVisitor; 32 import jdk.internal.org.objectweb.asm.Opcodes; 33 34 import java.io.FileInputStream; 35 import java.io.IOException; 36 import java.lang.invoke.CallSite; 37 import java.lang.invoke.MethodHandles; 38 import java.lang.invoke.MethodType; 39 import java.net.URISyntaxException; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.nio.file.StandardOpenOption; 44 45 /** 46 * A class which patch InvokeDynamic class bytecode with invokydynamic 47 instruction, rewriting "caller" method to call "callee" method using 48 invokedynamic 49 */ 50 public class InvokeDynamicPatcher extends ClassVisitor { 51 52 private static final String CLASS = InvokeDynamic.class.getName() 53 .replace('.', '/'); 54 private static final String CALLER_METHOD_NAME = "caller"; 55 private static final String CALLEE_METHOD_NAME = "callee"; 56 private static final String NATIVE_CALLEE_METHOD_NAME = "calleeNative"; 57 private static final String BOOTSTRAP_METHOD_NAME = "bootstrapMethod"; 58 private static final String CALL_NATIVE_FIELD = "nativeCallee"; 59 private static final String CALL_NATIVE_FIELD_DESC = "Z"; 60 private static final String CALLEE_METHOD_DESC 61 = "(L" + CLASS + ";IJFDLjava/lang/String;)Z"; 62 private static final String ASSERTTRUE_METHOD_DESC 63 = "(ZLjava/lang/String;)V"; 64 private static final String ASSERTS_CLASS = "jdk/test/lib/Asserts"; 65 private static final String ASSERTTRUE_METHOD_NAME = "assertTrue"; 66 67 public static void main(String args[]) { 68 ClassReader cr; 69 Path filePath; 70 try { 71 filePath = Paths.get(InvokeDynamic.class.getProtectionDomain().getCodeSource() 72 .getLocation().toURI()).resolve(CLASS + ".class"); 73 } catch (URISyntaxException ex) { 74 throw new Error("TESTBUG: Can't get code source" + ex, ex); 75 } 76 try (FileInputStream fis = new FileInputStream(filePath.toFile())) { 77 cr = new ClassReader(fis); 78 } catch (IOException e) { 79 throw new Error("Error reading file", e); 80 } 81 ClassWriter cw = new ClassWriter(cr, 82 ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 83 cr.accept(new InvokeDynamicPatcher(Opcodes.ASM5, cw), 0); 84 try { 85 Files.write(filePath, cw.toByteArray(), 86 StandardOpenOption.WRITE); 87 } catch (IOException e) { 88 throw new Error(e); 89 } 90 } 91 92 public InvokeDynamicPatcher(int api, ClassWriter cw) { 93 super(api, cw); 94 } 95 96 @Override 97 public MethodVisitor visitMethod(final int access, final String name, 98 final String desc, final String signature, 99 final String[] exceptions) { 100 /* a code generate looks like 101 * 0: aload_0 102 * 1: ldc #125 // int 1 103 * 3: ldc2_w #126 // long 2l 104 * 6: ldc #128 // float 3.0f 105 * 8: ldc2_w #129 // double 4.0d 106 * 11: ldc #132 // String 5 107 * 13: aload_0 108 * 14: getfield #135 // Field nativeCallee:Z 109 * 17: ifeq 28 110 * 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z 111 * 25: goto 33 112 * 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z 113 * 33: ldc #185 // String Call insuccessfull 114 * 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V 115 * 38: return 116 * 117 * or, using java-like pseudo-code 118 * if (this.nativeCallee == false) { 119 * invokedynamic-call-return-value = invokedynamic-of-callee 120 * } else { 121 * invokedynamic-call-return-value = invokedynamic-of-nativeCallee 122 * } 123 * Asserts.assertTrue(invokedynamic-call-return-value, error-message); 124 * return; 125 */ 126 if (name.equals(CALLER_METHOD_NAME)) { 127 MethodVisitor mv = cv.visitMethod(access, name, desc, 128 signature, exceptions); 129 Label nonNativeLabel = new Label(); 130 Label checkLabel = new Label(); 131 MethodType mtype = MethodType.methodType(CallSite.class, 132 MethodHandles.Lookup.class, String.class, MethodType.class); 133 Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, CLASS, 134 BOOTSTRAP_METHOD_NAME, mtype.toMethodDescriptorString()); 135 mv.visitCode(); 136 // push callee parameters onto stack 137 mv.visitVarInsn(Opcodes.ALOAD, 0);//push "this" 138 mv.visitLdcInsn(1); 139 mv.visitLdcInsn(2L); 140 mv.visitLdcInsn(3.0f); 141 mv.visitLdcInsn(4.0d); 142 mv.visitLdcInsn("5"); 143 // params loaded. let's decide what method to call 144 mv.visitVarInsn(Opcodes.ALOAD, 0); // push "this" 145 // get nativeCallee field 146 mv.visitFieldInsn(Opcodes.GETFIELD, CLASS, CALL_NATIVE_FIELD, 147 CALL_NATIVE_FIELD_DESC); 148 // if nativeCallee == false goto nonNativeLabel 149 mv.visitJumpInsn(Opcodes.IFEQ, nonNativeLabel); 150 // invokedynamic nativeCalleeMethod using bootstrap method 151 mv.visitInvokeDynamicInsn(NATIVE_CALLEE_METHOD_NAME, 152 CALLEE_METHOD_DESC, bootstrap); 153 // goto checkLabel 154 mv.visitJumpInsn(Opcodes.GOTO, checkLabel); 155 // label: nonNativeLabel 156 mv.visitLabel(nonNativeLabel); 157 // invokedynamic calleeMethod using bootstrap method 158 mv.visitInvokeDynamicInsn(CALLEE_METHOD_NAME, CALLEE_METHOD_DESC, 159 bootstrap); 160 mv.visitLabel(checkLabel); 161 mv.visitLdcInsn(CallsBase.CALL_ERR_MSG); 162 mv.visitMethodInsn(Opcodes.INVOKESTATIC, ASSERTS_CLASS, 163 ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC, false); 164 // label: return 165 mv.visitInsn(Opcodes.RETURN); 166 mv.visitMaxs(0, 0); 167 mv.visitEnd(); 168 return null; 169 } 170 return super.visitMethod(access, name, desc, signature, exceptions); 171 } 172 }