1/*
2 * Copyright (c) 2016, 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 selectionresolution;
25
26import jdk.internal.org.objectweb.asm.ClassVisitor;
27import jdk.internal.org.objectweb.asm.Handle;
28import jdk.internal.org.objectweb.asm.MethodVisitor;
29
30import java.lang.invoke.CallSite;
31import java.lang.invoke.MethodHandles;
32import java.lang.invoke.MethodType;
33
34import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
35import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
36import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
37import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
38import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
39import static jdk.internal.org.objectweb.asm.Opcodes.POP;
40import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
41import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
42import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
43import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
44import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
45import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
46import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
47import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
48import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL;
49import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
50import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE;
51import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
52
53class Method {
54    public static final String defaultMethodName        = "m";
55    public static final String defaultMethodDescriptor  = "()Ljava/lang/Integer;";
56    public static final String methodDescriptorTemplate = "(L%s;)Ljava/lang/Integer;";
57    private final ClassConstruct ownerClass;
58    private final String ownerClassName;
59    private final ClassVisitor cv;
60    private final MethodVisitor mv;
61    private final ClassBuilder.ExecutionMode execMode;
62
63    public Method(ClassConstruct ownerClass, ClassVisitor cv, String name, String descriptor, int access,
64                  ClassBuilder.ExecutionMode execMode) {
65        this.ownerClassName = ownerClass.getName();
66        this.ownerClass = ownerClass;
67        this.execMode = execMode;
68        this.cv = cv;
69        mv = cv.visitMethod(access, name, descriptor, null, null);
70        mv.visitCode();
71    }
72    /**
73     * Add code for the m()Ljava/lang/Integer; method, always returns null
74     */
75    public void makeDefaultMethod() {
76        mv.visitTypeInsn(NEW, "java/lang/Integer");
77        mv.visitInsn(DUP);
78        mv.visitLdcInsn(ownerClass.getIndex());
79        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V");
80        mv.visitInsn(ARETURN);
81        mv.visitMaxs(0, 0);
82        mv.visitEnd();
83    }
84
85    public void makePrivateCallMethod(String className) {
86        makeSuperCallMethod(INVOKESPECIAL, className);
87    }
88
89    public void makeSuperCallMethod(int invokeInstruction, String className) {
90        mv.visitVarInsn(ALOAD, 0);
91        makeCall(invokeInstruction, className, false);
92        mv.visitInsn(POP);
93        done();
94    }
95
96    public void defaultInvoke(int instr, String className, String objectRef, boolean isInterface) {
97        switch (instr) {
98            case INVOKEVIRTUAL:
99                defaultInvokeVirtual(className, objectRef);
100                break;
101            case INVOKEINTERFACE:
102                defaultInvokeInterface(className, objectRef);
103                break;
104            case INVOKESTATIC:
105                defaultInvokeStatic(className, isInterface);
106                break;
107            case INVOKESPECIAL:
108                defaultInvokeSpecial(className, objectRef, isInterface);
109                break;
110            default:
111                break;
112        }
113        mv.visitInsn(ARETURN);
114        mv.visitMaxs(0, 0);
115        mv.visitEnd();
116    }
117
118    private void defaultInvokeVirtual(String className, String objectRef) {
119        String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
120        makeNewObject(objectRef, objectRefPackageName);
121        makeCall(INVOKEVIRTUAL, className, false);
122    }
123
124    private void defaultInvokeInterface(String className, String objectRef) {
125        String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
126        makeNewObject(objectRef, objectRefPackageName);
127        makeCall(INVOKEINTERFACE, className, true);
128    }
129
130    private void defaultInvokeSpecial(String className, String objectRef, boolean isInterface) {
131        String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
132        makeNewObject(objectRef, objectRefPackageName);
133        makeCall(INVOKESPECIAL, className, isInterface);
134    }
135
136    private void defaultInvokeStatic(String className, boolean isInterface) {
137        makeCall(INVOKESTATIC, className, isInterface);
138    }
139
140    private Method makeCall(int invokeInstruction, String className, boolean isInterface) {
141        switch(execMode) {
142            case DIRECT: {
143                mv.visitMethodInsn(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor, isInterface);
144                break;
145            }
146            case INDY: {
147                Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor);
148                Handle bsm = generateBootstrapMethod(m);
149                mv.visitInvokeDynamicInsn(defaultMethodName, defaultMethodDescriptor, bsm);
150                break;
151            }
152            case MH_INVOKE_EXACT:
153            case MH_INVOKE_GENERIC: {
154                String invokerName = execMode == ClassBuilder.ExecutionMode.MH_INVOKE_GENERIC
155                        ? "invoke" : "invokeExact";
156
157                Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor);
158                mv.visitLdcInsn(m);
159                mv.visitInsn(SWAP);
160                mv.visitMethodInsn(INVOKEVIRTUAL,
161                        "java/lang/invoke/MethodHandle",
162                        invokerName,
163                        String.format(methodDescriptorTemplate, className),
164                        false);
165                break;
166            }
167            default:
168                throw new Error("Unknown execution mode: " + execMode);
169
170        }
171        return this;
172    }
173
174    private Handle generateBootstrapMethod(Handle h) {
175        String bootstrapName = "bootstrapMethod";
176        MethodType bootstrapType = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
177
178        MethodVisitor bmv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, bootstrapName, bootstrapType.toMethodDescriptorString(), null, null);
179        bmv.visitCode();
180
181        String constCallSite = "java/lang/invoke/ConstantCallSite";
182        bmv.visitTypeInsn(NEW, constCallSite);
183        bmv.visitInsn(DUP);
184
185        bmv.visitLdcInsn(h);
186
187        bmv.visitMethodInsn(INVOKESPECIAL, constCallSite, "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
188        bmv.visitInsn(ARETURN);
189
190        bmv.visitMaxs(0,0);
191        bmv.visitEnd();
192
193        return new Handle(H_INVOKESTATIC, ownerClassName, bootstrapName, bootstrapType.toMethodDescriptorString());
194    }
195
196
197    private static Handle convertToHandle(int invokeInstruction, String className, String methodName, String methodDesc) {
198        int tag;
199        switch (invokeInstruction) {
200            case INVOKEVIRTUAL:   tag = H_INVOKEVIRTUAL;   break;
201            case INVOKEINTERFACE: tag = H_INVOKEINTERFACE; break;
202            case INVOKESPECIAL:   tag = H_INVOKESPECIAL;   break;
203            case INVOKESTATIC:    tag = H_INVOKESTATIC;    break;
204            default:
205                throw new Error("Unknown invoke instruction: "+invokeInstruction);
206        }
207
208        return new Handle(tag, className, methodName, methodDesc);
209    }
210
211    private void makeNewObject(String objectRef, String objectRefPackageName) {
212        String className = objectRef.substring(objectRef.lastIndexOf("/") + 1);
213        makeStaticCall( objectRefPackageName + "/Helper",
214                        "get" + className,
215                        "()L" + objectRef + ";", false);
216        mv.visitVarInsn(ASTORE, 1);
217        mv.visitVarInsn(ALOAD, 1);
218    }
219
220    public void makeTestCall(String className) {
221        mv.visitTypeInsn(NEW, className);
222        mv.visitInsn(DUP);
223        mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
224        mv.visitVarInsn(ASTORE, 1);
225        mv.visitVarInsn(ALOAD, 1);
226        mv.visitMethodInsn(INVOKEVIRTUAL, className, "test", "()Ljava/lang/Integer;", false);
227        mv.visitInsn(RETURN);
228        mv.visitMaxs(2, 2);
229        mv.visitEnd();
230    }
231
232    public Method makeStaticCall(String classname, String method, String descriptor, boolean isInterface) {
233        mv.visitMethodInsn(INVOKESTATIC, classname, method, descriptor, isInterface);
234        return this;
235    }
236
237    public void makeConstructor(String extending, boolean isInterface) {
238        mv.visitVarInsn(ALOAD, 0);
239        mv.visitMethodInsn(INVOKESPECIAL, extending == null ? "java/lang/Object" : extending, "<init>", "()V", isInterface);
240        mv.visitInsn(RETURN);
241        mv.visitMaxs(0, 0);
242        mv.visitEnd();
243    }
244
245    public void makeInstantiateMethod(String className) {
246        mv.visitTypeInsn(NEW, className);
247        mv.visitInsn(DUP);
248        mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
249        mv.visitInsn(ARETURN);
250        mv.visitMaxs(0, 0);
251        mv.visitEnd();
252    }
253
254    public void done() {
255        mv.visitInsn(RETURN);
256        mv.visitMaxs(0, 0);
257        mv.visitEnd();
258    }
259}
260