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