• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2003 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.core;
17 
18 import java.io.*;
19 import java.util.*;
20 
21 import org.mockito.asm.*;
22 
23 /**
24  * @author Juozas Baliuka, Chris Nokleberg
25  */
26 public class ClassEmitter extends ClassAdapter {
27     private ClassInfo classInfo;
28     private Map fieldInfo;
29 
30     private static int hookCounter;
31     private MethodVisitor rawStaticInit;
32     private CodeEmitter staticInit;
33     private CodeEmitter staticHook;
34     private Signature staticHookSig;
35 
ClassEmitter(ClassVisitor cv)36     public ClassEmitter(ClassVisitor cv) {
37         super(null);
38         setTarget(cv);
39     }
40 
ClassEmitter()41     public ClassEmitter() {
42         super(null);
43     }
44 
setTarget(ClassVisitor cv)45     public void setTarget(ClassVisitor cv) {
46         this.cv = cv;
47         fieldInfo = new HashMap();
48 
49         // just to be safe
50         staticInit = staticHook = null;
51         staticHookSig = null;
52     }
53 
getNextHook()54     synchronized private static int getNextHook() {
55         return ++hookCounter;
56     }
57 
getClassInfo()58     public ClassInfo getClassInfo() {
59         return classInfo;
60     }
61 
begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source)62     public void begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source) {
63         final Type classType = Type.getType("L" + className.replace('.', '/') + ";");
64         classInfo = new ClassInfo() {
65             public Type getType() {
66                 return classType;
67             }
68             public Type getSuperType() {
69                 return (superType != null) ? superType : Constants.TYPE_OBJECT;
70             }
71             public Type[] getInterfaces() {
72                 return interfaces;
73             }
74             public int getModifiers() {
75                 return access;
76             }
77         };
78         cv.visit(version,
79                  access,
80                  classInfo.getType().getInternalName(),
81                  null,
82                  classInfo.getSuperType().getInternalName(),
83                  TypeUtils.toInternalNames(interfaces));
84         if (source != null)
85             cv.visitSource(source, null);
86         init();
87     }
88 
getStaticHook()89     public CodeEmitter getStaticHook() {
90          if (TypeUtils.isInterface(getAccess())) {
91              throw new IllegalStateException("static hook is invalid for this class");
92          }
93          if (staticHook == null) {
94              staticHookSig = new Signature("CGLIB$STATICHOOK" + getNextHook(), "()V");
95              staticHook = begin_method(Constants.ACC_STATIC,
96                                        staticHookSig,
97                                        null);
98              if (staticInit != null) {
99                  staticInit.invoke_static_this(staticHookSig);
100              }
101          }
102          return staticHook;
103     }
104 
init()105     protected void init() {
106     }
107 
getAccess()108     public int getAccess() {
109         return classInfo.getModifiers();
110     }
111 
getClassType()112     public Type getClassType() {
113         return classInfo.getType();
114     }
115 
getSuperType()116     public Type getSuperType() {
117         return classInfo.getSuperType();
118     }
119 
end_class()120     public void end_class() {
121         if (staticHook != null && staticInit == null) {
122             // force creation of static init
123             begin_static();
124         }
125         if (staticInit != null) {
126             staticHook.return_value();
127             staticHook.end_method();
128             rawStaticInit.visitInsn(Constants.RETURN);
129             rawStaticInit.visitMaxs(0, 0);
130             staticInit = staticHook = null;
131             staticHookSig = null;
132         }
133         cv.visitEnd();
134     }
135 
begin_method(int access, Signature sig, Type[] exceptions)136     public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
137         if (classInfo == null)
138             throw new IllegalStateException("classInfo is null! " + this);
139         MethodVisitor v = cv.visitMethod(access,
140                                          sig.getName(),
141                                          sig.getDescriptor(),
142                                          null,
143                                          TypeUtils.toInternalNames(exceptions));
144         if (sig.equals(Constants.SIG_STATIC) && !TypeUtils.isInterface(getAccess())) {
145             rawStaticInit = v;
146             MethodVisitor wrapped = new MethodAdapter(v) {
147                 public void visitMaxs(int maxStack, int maxLocals) {
148                     // ignore
149                 }
150                 public void visitInsn(int insn) {
151                     if (insn != Constants.RETURN) {
152                         super.visitInsn(insn);
153                     }
154                 }
155             };
156             staticInit = new CodeEmitter(this, wrapped, access, sig, exceptions);
157             if (staticHook == null) {
158                 // force static hook creation
159                 getStaticHook();
160             } else {
161                 staticInit.invoke_static_this(staticHookSig);
162             }
163             return staticInit;
164         } else if (sig.equals(staticHookSig)) {
165             return new CodeEmitter(this, v, access, sig, exceptions) {
166                 public boolean isStaticHook() {
167                     return true;
168                 }
169             };
170         } else {
171             return new CodeEmitter(this, v, access, sig, exceptions);
172         }
173     }
174 
175     public CodeEmitter begin_static() {
176         return begin_method(Constants.ACC_STATIC, Constants.SIG_STATIC, null);
177     }
178 
179     public void declare_field(int access, String name, Type type, Object value) {
180         FieldInfo existing = (FieldInfo)fieldInfo.get(name);
181         FieldInfo info = new FieldInfo(access, name, type, value);
182         if (existing != null) {
183             if (!info.equals(existing)) {
184                 throw new IllegalArgumentException("Field \"" + name + "\" has been declared differently");
185             }
186         } else {
187             fieldInfo.put(name, info);
188             cv.visitField(access, name, type.getDescriptor(), null, value);
189         }
190     }
191 
192     // TODO: make public?
193     boolean isFieldDeclared(String name) {
194         return fieldInfo.get(name) != null;
195     }
196 
197     FieldInfo getFieldInfo(String name) {
198         FieldInfo field = (FieldInfo)fieldInfo.get(name);
199         if (field == null) {
200             throw new IllegalArgumentException("Field " + name + " is not declared in " + getClassType().getClassName());
201         }
202         return field;
203     }
204 
205     static class FieldInfo {
206         int access;
207         String name;
208         Type type;
209         Object value;
210 
211         public FieldInfo(int access, String name, Type type, Object value) {
212             this.access = access;
213             this.name = name;
214             this.type = type;
215             this.value = value;
216         }
217 
218         public boolean equals(Object o) {
219             if (o == null)
220                 return false;
221             if (!(o instanceof FieldInfo))
222                 return false;
223             FieldInfo other = (FieldInfo)o;
224             if (access != other.access ||
225                 !name.equals(other.name) ||
226                 !type.equals(other.type)) {
227                 return false;
228             }
229             if ((value == null) ^ (other.value == null))
230                 return false;
231             if (value != null && !value.equals(other.value))
232                 return false;
233             return true;
234         }
235 
236         public int hashCode() {
237             return access ^ name.hashCode() ^ type.hashCode() ^ ((value == null) ? 0 : value.hashCode());
238         }
239     }
240 
241     public void visit(int version,
242                       int access,
243                       String name,
244                       String signature,
245                       String superName,
246                       String[] interfaces) {
247         begin_class(version,
248                     access,
249                     name.replace('/', '.'),
250                     TypeUtils.fromInternalName(superName),
251                     TypeUtils.fromInternalNames(interfaces),
252                     null); // TODO
253     }
254 
255     public void visitEnd() {
256         end_class();
257     }
258 
259     public FieldVisitor visitField(int access,
260                                    String name,
261                                    String desc,
262                                    String signature,
263                                    Object value) {
264         declare_field(access, name, Type.getType(desc), value);
265         return null; // TODO
266     }
267 
268     public MethodVisitor visitMethod(int access,
269                                      String name,
270                                      String desc,
271                                      String signature,
272                                      String[] exceptions) {
273         return begin_method(access,
274                             new Signature(name, desc),
275                             TypeUtils.fromInternalNames(exceptions));
276     }
277 }
278