| /* |
| * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package selectionresolution; |
| |
| import jdk.internal.org.objectweb.asm.ClassVisitor; |
| import jdk.internal.org.objectweb.asm.Handle; |
| import jdk.internal.org.objectweb.asm.MethodVisitor; |
| |
| import java.lang.invoke.CallSite; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| |
| import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DUP; |
| import static jdk.internal.org.objectweb.asm.Opcodes.POP; |
| import static jdk.internal.org.objectweb.asm.Opcodes.NEW; |
| import static jdk.internal.org.objectweb.asm.Opcodes.SWAP; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; |
| |
| class Method { |
| public static final String defaultMethodName = "m"; |
| public static final String defaultMethodDescriptor = "()Ljava/lang/Integer;"; |
| public static final String methodDescriptorTemplate = "(L%s;)Ljava/lang/Integer;"; |
| private final ClassConstruct ownerClass; |
| private final String ownerClassName; |
| private final ClassVisitor cv; |
| private final MethodVisitor mv; |
| private final boolean isInterface; |
| private final ClassBuilder.ExecutionMode execMode; |
| |
| public Method(ClassConstruct ownerClass, ClassVisitor cv, String name, String descriptor, int access, |
| ClassBuilder.ExecutionMode execMode) { |
| this.ownerClassName = ownerClass.getName(); |
| this.ownerClass = ownerClass; |
| this.isInterface = ownerClass.isInterface(); |
| this.execMode = execMode; |
| this.cv = cv; |
| mv = cv.visitMethod(access, name, descriptor, null, null); |
| mv.visitCode(); |
| } |
| /** |
| * Add code for the m()Ljava/lang/Integer; method, always returns null |
| */ |
| public void makeDefaultMethod() { |
| mv.visitTypeInsn(NEW, "java/lang/Integer"); |
| mv.visitInsn(DUP); |
| mv.visitLdcInsn(ownerClass.getIndex()); |
| mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V"); |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| public void makePrivateCallMethod(String className) { |
| makeSuperCallMethod(INVOKESPECIAL, className); |
| } |
| |
| public void makeSuperCallMethod(int invokeInstruction, String className) { |
| mv.visitVarInsn(ALOAD, 0); |
| makeCall(invokeInstruction, className); |
| mv.visitInsn(POP); |
| done(); |
| } |
| |
| public void defaultInvoke(int instr, String className, String objectRef) { |
| switch (instr) { |
| case INVOKEVIRTUAL: |
| defaultInvokeVirtual(className, objectRef); |
| break; |
| case INVOKEINTERFACE: |
| defaultInvokeInterface(className, objectRef); |
| break; |
| case INVOKESTATIC: |
| defaultInvokeStatic(className); |
| break; |
| case INVOKESPECIAL: |
| defaultInvokeSpecial(className, objectRef); |
| break; |
| default: |
| break; |
| } |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| public void defaultInvokeVirtual(String className, String objectRef) { |
| String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/")); |
| makeNewObject(objectRef, objectRefPackageName); |
| makeCall(INVOKEVIRTUAL, className, false); |
| } |
| |
| public void defaultInvokeInterface(String className, String objectRef) { |
| String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/")); |
| makeNewObject(objectRef, objectRefPackageName); |
| makeCall(INVOKEINTERFACE, className, true); |
| } |
| |
| public void defaultInvokeSpecial(String className, String objectRef) { |
| String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/")); |
| makeNewObject(objectRef, objectRefPackageName); |
| makeCall(INVOKESPECIAL, className, false); |
| } |
| |
| public void defaultInvokeStatic(String className) { |
| makeCall(INVOKESTATIC, className); |
| } |
| |
| private Method makeCall(int invokeInstruction, String className) { |
| return makeCall(invokeInstruction, className, isInterface); |
| } |
| |
| private Method makeCall(int invokeInstruction, String className, boolean isInterface) { |
| switch(execMode) { |
| case DIRECT: { |
| mv.visitMethodInsn(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor, isInterface); |
| break; |
| } |
| case INDY: { |
| Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor); |
| Handle bsm = generateBootstrapMethod(m); |
| mv.visitInvokeDynamicInsn(defaultMethodName, defaultMethodDescriptor, bsm); |
| break; |
| } |
| case MH_INVOKE_EXACT: |
| case MH_INVOKE_GENERIC: { |
| String invokerName = execMode == ClassBuilder.ExecutionMode.MH_INVOKE_GENERIC |
| ? "invoke" : "invokeExact"; |
| |
| Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor); |
| mv.visitLdcInsn(m); |
| mv.visitInsn(SWAP); |
| mv.visitMethodInsn(INVOKEVIRTUAL, |
| "java/lang/invoke/MethodHandle", |
| invokerName, |
| String.format(methodDescriptorTemplate, className), |
| false); |
| break; |
| } |
| default: |
| throw new Error("Unknown execution mode: " + execMode); |
| |
| } |
| return this; |
| } |
| |
| private Handle generateBootstrapMethod(Handle h) { |
| String bootstrapName = "bootstrapMethod"; |
| MethodType bootstrapType = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class); |
| |
| MethodVisitor bmv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, bootstrapName, bootstrapType.toMethodDescriptorString(), null, null); |
| bmv.visitCode(); |
| |
| String constCallSite = "java/lang/invoke/ConstantCallSite"; |
| bmv.visitTypeInsn(NEW, constCallSite); |
| bmv.visitInsn(DUP); |
| |
| bmv.visitLdcInsn(h); |
| |
| bmv.visitMethodInsn(INVOKESPECIAL, constCallSite, "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false); |
| bmv.visitInsn(ARETURN); |
| |
| bmv.visitMaxs(0,0); |
| bmv.visitEnd(); |
| |
| return new Handle(H_INVOKESTATIC, ownerClassName, bootstrapName, bootstrapType.toMethodDescriptorString()); |
| } |
| |
| |
| private static Handle convertToHandle(int invokeInstruction, String className, String methodName, String methodDesc) { |
| int tag; |
| switch (invokeInstruction) { |
| case INVOKEVIRTUAL: tag = H_INVOKEVIRTUAL; break; |
| case INVOKEINTERFACE: tag = H_INVOKEINTERFACE; break; |
| case INVOKESPECIAL: tag = H_INVOKESPECIAL; break; |
| case INVOKESTATIC: tag = H_INVOKESTATIC; break; |
| default: |
| throw new Error("Unknown invoke instruction: "+invokeInstruction); |
| } |
| |
| return new Handle(tag, className, methodName, methodDesc); |
| } |
| |
| private void makeNewObject(String objectRef, String objectRefPackageName) { |
| String className = objectRef.substring(objectRef.lastIndexOf("/") + 1); |
| makeStaticCall( objectRefPackageName + "/Helper", |
| "get" + className, |
| "()L" + objectRef + ";"); |
| mv.visitVarInsn(ASTORE, 1); |
| mv.visitVarInsn(ALOAD, 1); |
| } |
| |
| public void makeTestCall(String className) { |
| mv.visitTypeInsn(NEW, className); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); |
| mv.visitVarInsn(ASTORE, 1); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, className, "test", "()Ljava/lang/Integer;", false); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(2, 2); |
| mv.visitEnd(); |
| } |
| |
| public Method makeStaticCall(String classname, String method, String descriptor) { |
| mv.visitMethodInsn(INVOKESTATIC, classname, method, descriptor, isInterface); |
| return this; |
| } |
| |
| public void makeConstructor(String extending) { |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitMethodInsn(INVOKESPECIAL, extending == null ? "java/lang/Object" : extending, "<init>", "()V", isInterface); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| public void makeInstantiateMethod(String className) { |
| mv.visitTypeInsn(NEW, className); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| public void done() { |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| } |