1/* 2 * Copyright 2003,2004 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockito.cglib.proxy; 17 18import java.lang.reflect.Method; 19import java.util.*; 20 21import org.mockito.asm.Label; 22import org.mockito.asm.Type; 23import org.mockito.cglib.core.*; 24 25class MethodInterceptorGenerator 26implements CallbackGenerator 27{ 28 public static final MethodInterceptorGenerator INSTANCE = new MethodInterceptorGenerator(); 29 30 static final String EMPTY_ARGS_NAME = "CGLIB$emptyArgs"; 31 static final String FIND_PROXY_NAME = "CGLIB$findMethodProxy"; 32 static final Class[] FIND_PROXY_TYPES = { Signature.class }; 33 34 private static final Type ABSTRACT_METHOD_ERROR = 35 TypeUtils.parseType("AbstractMethodError"); 36 private static final Type METHOD = 37 TypeUtils.parseType("java.lang.reflect.Method"); 38 private static final Type REFLECT_UTILS = 39 TypeUtils.parseType("org.mockito.cglib.core.ReflectUtils"); 40 private static final Type METHOD_PROXY = 41 TypeUtils.parseType("org.mockito.cglib.proxy.MethodProxy"); 42 private static final Type METHOD_INTERCEPTOR = 43 TypeUtils.parseType("org.mockito.cglib.proxy.MethodInterceptor"); 44 private static final Signature GET_DECLARED_METHODS = 45 TypeUtils.parseSignature("java.lang.reflect.Method[] getDeclaredMethods()"); 46 private static final Signature GET_DECLARING_CLASS = 47 TypeUtils.parseSignature("Class getDeclaringClass()"); 48 private static final Signature FIND_METHODS = 49 TypeUtils.parseSignature("java.lang.reflect.Method[] findMethods(String[], java.lang.reflect.Method[])"); 50 private static final Signature MAKE_PROXY = 51 new Signature("create", METHOD_PROXY, new Type[]{ 52 Constants.TYPE_CLASS, 53 Constants.TYPE_CLASS, 54 Constants.TYPE_STRING, 55 Constants.TYPE_STRING, 56 Constants.TYPE_STRING 57 }); 58 private static final Signature INTERCEPT = 59 new Signature("intercept", Constants.TYPE_OBJECT, new Type[]{ 60 Constants.TYPE_OBJECT, 61 METHOD, 62 Constants.TYPE_OBJECT_ARRAY, 63 METHOD_PROXY 64 }); 65 private static final Signature FIND_PROXY = 66 new Signature(FIND_PROXY_NAME, METHOD_PROXY, new Type[]{ Constants.TYPE_SIGNATURE }); 67 private static final Signature TO_STRING = 68 TypeUtils.parseSignature("String toString()"); 69 private static final Transformer METHOD_TO_CLASS = new Transformer(){ 70 public Object transform(Object value) { 71 return ((MethodInfo)value).getClassInfo(); 72 } 73 }; 74 private static final Signature CSTRUCT_SIGNATURE = 75 TypeUtils.parseConstructor("String, String"); 76 77 private String getMethodField(Signature impl) { 78 return impl.getName() + "$Method"; 79 } 80 private String getMethodProxyField(Signature impl) { 81 return impl.getName() + "$Proxy"; 82 } 83 84 public void generate(ClassEmitter ce, Context context, List methods) { 85 Map sigMap = new HashMap(); 86 for (Iterator it = methods.iterator(); it.hasNext();) { 87 MethodInfo method = (MethodInfo)it.next(); 88 Signature sig = method.getSignature(); 89 Signature impl = context.getImplSignature(method); 90 91 String methodField = getMethodField(impl); 92 String methodProxyField = getMethodProxyField(impl); 93 94 sigMap.put(sig.toString(), methodProxyField); 95 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodField, METHOD, null); 96 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodProxyField, METHOD_PROXY, null); 97 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, EMPTY_ARGS_NAME, Constants.TYPE_OBJECT_ARRAY, null); 98 CodeEmitter e; 99 100 // access method 101 e = ce.begin_method(Constants.ACC_FINAL, 102 impl, 103 method.getExceptionTypes()); 104 superHelper(e, method); 105 e.return_value(); 106 e.end_method(); 107 108 // around method 109 e = context.beginMethod(ce, method); 110 Label nullInterceptor = e.make_label(); 111 context.emitCallback(e, context.getIndex(method)); 112 e.dup(); 113 e.ifnull(nullInterceptor); 114 115 e.load_this(); 116 e.getfield(methodField); 117 118 if (sig.getArgumentTypes().length == 0) { 119 e.getfield(EMPTY_ARGS_NAME); 120 } else { 121 e.create_arg_array(); 122 } 123 124 e.getfield(methodProxyField); 125 e.invoke_interface(METHOD_INTERCEPTOR, INTERCEPT); 126 e.unbox_or_zero(sig.getReturnType()); 127 e.return_value(); 128 129 e.mark(nullInterceptor); 130 superHelper(e, method); 131 e.return_value(); 132 e.end_method(); 133 } 134 generateFindProxy(ce, sigMap); 135 } 136 137 private static void superHelper(CodeEmitter e, MethodInfo method) 138 { 139 if (TypeUtils.isAbstract(method.getModifiers())) { 140 e.throw_exception(ABSTRACT_METHOD_ERROR, method.toString() + " is abstract" ); 141 } else { 142 e.load_this(); 143 e.load_args(); 144 e.super_invoke(method.getSignature()); 145 } 146 } 147 148 public void generateStatic(CodeEmitter e, Context context, List methods) throws Exception { 149 /* generates: 150 static { 151 Class thisClass = Class.forName("NameOfThisClass"); 152 Class cls = Class.forName("java.lang.Object"); 153 String[] sigs = new String[]{ "toString", "()Ljava/lang/String;", ... }; 154 Method[] methods = cls.getDeclaredMethods(); 155 methods = ReflectUtils.findMethods(sigs, methods); 156 METHOD_0 = methods[0]; 157 CGLIB$ACCESS_0 = MethodProxy.create(cls, thisClass, "()Ljava/lang/String;", "toString", "CGLIB$ACCESS_0"); 158 ... 159 } 160 */ 161 162 e.push(0); 163 e.newarray(); 164 e.putfield(EMPTY_ARGS_NAME); 165 166 Local thisclass = e.make_local(); 167 Local declaringclass = e.make_local(); 168 EmitUtils.load_class_this(e); 169 e.store_local(thisclass); 170 171 Map methodsByClass = CollectionUtils.bucket(methods, METHOD_TO_CLASS); 172 for (Iterator i = methodsByClass.keySet().iterator(); i.hasNext();) { 173 ClassInfo classInfo = (ClassInfo)i.next(); 174 175 List classMethods = (List)methodsByClass.get(classInfo); 176 e.push(2 * classMethods.size()); 177 e.newarray(Constants.TYPE_STRING); 178 for (int index = 0; index < classMethods.size(); index++) { 179 MethodInfo method = (MethodInfo)classMethods.get(index); 180 Signature sig = method.getSignature(); 181 e.dup(); 182 e.push(2 * index); 183 e.push(sig.getName()); 184 e.aastore(); 185 e.dup(); 186 e.push(2 * index + 1); 187 e.push(sig.getDescriptor()); 188 e.aastore(); 189 } 190 191 EmitUtils.load_class(e, classInfo.getType()); 192 e.dup(); 193 e.store_local(declaringclass); 194 e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHODS); 195 e.invoke_static(REFLECT_UTILS, FIND_METHODS); 196 197 for (int index = 0; index < classMethods.size(); index++) { 198 MethodInfo method = (MethodInfo)classMethods.get(index); 199 Signature sig = method.getSignature(); 200 Signature impl = context.getImplSignature(method); 201 e.dup(); 202 e.push(index); 203 e.array_load(METHOD); 204 e.putfield(getMethodField(impl)); 205 206 e.load_local(declaringclass); 207 e.load_local(thisclass); 208 e.push(sig.getDescriptor()); 209 e.push(sig.getName()); 210 e.push(impl.getName()); 211 e.invoke_static(METHOD_PROXY, MAKE_PROXY); 212 e.putfield(getMethodProxyField(impl)); 213 } 214 e.pop(); 215 } 216 } 217 218 public void generateFindProxy(ClassEmitter ce, final Map sigMap) { 219 final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, 220 FIND_PROXY, 221 null); 222 e.load_arg(0); 223 e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); 224 ObjectSwitchCallback callback = new ObjectSwitchCallback() { 225 public void processCase(Object key, Label end) { 226 e.getfield((String)sigMap.get(key)); 227 e.return_value(); 228 } 229 public void processDefault() { 230 e.aconst_null(); 231 e.return_value(); 232 } 233 }; 234 EmitUtils.string_switch(e, 235 (String[])sigMap.keySet().toArray(new String[0]), 236 Constants.SWITCH_STYLE_HASH, 237 callback); 238 e.end_method(); 239 } 240} 241