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 }