Home | History | Annotate | Download | only in core
      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 
     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