/* * Copyright (c) 2014, 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. * */ 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.*; 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", "", "(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, "", "(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, "", "()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, "", "()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, "", "()V", false); mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } public void done() { mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } }