• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package org.mockito.cglib.proxy;
17 
18 import java.lang.reflect.Method;
19 import java.util.*;
20 
21 import org.mockito.asm.Label;
22 import org.mockito.asm.Type;
23 import org.mockito.cglib.core.*;
24 
25 class MethodInterceptorGenerator
26 implements 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 
getMethodField(Signature impl)77     private String getMethodField(Signature impl) {
78         return impl.getName() + "$Method";
79     }
getMethodProxyField(Signature impl)80     private String getMethodProxyField(Signature impl) {
81         return impl.getName() + "$Proxy";
82     }
83 
generate(ClassEmitter ce, Context context, List methods)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 
superHelper(CodeEmitter e, MethodInfo method)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 
generateStatic(CodeEmitter e, Context context, List methods)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 
generateFindProxy(ClassEmitter ce, final Map sigMap)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