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
24package compiler.calls.common;
25
26import jdk.internal.org.objectweb.asm.ClassReader;
27import jdk.internal.org.objectweb.asm.ClassVisitor;
28import jdk.internal.org.objectweb.asm.ClassWriter;
29import jdk.internal.org.objectweb.asm.Handle;
30import jdk.internal.org.objectweb.asm.Label;
31import jdk.internal.org.objectweb.asm.MethodVisitor;
32import jdk.internal.org.objectweb.asm.Opcodes;
33
34import java.io.FileInputStream;
35import java.io.IOException;
36import java.lang.invoke.CallSite;
37import java.lang.invoke.MethodHandles;
38import java.lang.invoke.MethodType;
39import java.net.URISyntaxException;
40import java.nio.file.Files;
41import java.nio.file.Path;
42import java.nio.file.Paths;
43import 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 */
50public 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}
173