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 */ 16package org.mockito.cglib.core; 17 18import java.io.*; 19import java.util.*; 20 21import org.mockito.asm.*; 22 23/** 24 * @author Juozas Baliuka, Chris Nokleberg 25 */ 26public 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 36 public ClassEmitter(ClassVisitor cv) { 37 super(null); 38 setTarget(cv); 39 } 40 41 public ClassEmitter() { 42 super(null); 43 } 44 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 54 synchronized private static int getNextHook() { 55 return ++hookCounter; 56 } 57 58 public ClassInfo getClassInfo() { 59 return classInfo; 60 } 61 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 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 105 protected void init() { 106 } 107 108 public int getAccess() { 109 return classInfo.getModifiers(); 110 } 111 112 public Type getClassType() { 113 return classInfo.getType(); 114 } 115 116 public Type getSuperType() { 117 return classInfo.getSuperType(); 118 } 119 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 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