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