Home | History | Annotate | Download | only in impl
      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.transform.impl;
     17 
     18 import org.mockito.asm.Attribute;
     19 import org.mockito.asm.ClassVisitor;
     20 import org.mockito.asm.Label;
     21 import org.mockito.asm.MethodAdapter;
     22 import org.mockito.asm.MethodVisitor;
     23 import org.mockito.asm.Type;
     24 import org.mockito.cglib.core.*;
     25 import org.mockito.cglib.transform.*;
     26 
     27 /**
     28  * @author Juozas Baliuka, Chris Nokleberg
     29  */
     30 public class InterceptFieldTransformer extends ClassEmitterTransformer {
     31     private static final String CALLBACK_FIELD = "$CGLIB_READ_WRITE_CALLBACK";
     32     private static final Type CALLBACK =
     33       TypeUtils.parseType("org.mockito.cglib.transform.impl.InterceptFieldCallback");
     34     private static final Type ENABLED =
     35       TypeUtils.parseType("org.mockito.cglib.transform.impl.InterceptFieldEnabled");
     36     private static final Signature ENABLED_SET =
     37       new Signature("setInterceptFieldCallback", Type.VOID_TYPE, new Type[]{ CALLBACK });
     38     private static final Signature ENABLED_GET =
     39       new Signature("getInterceptFieldCallback", CALLBACK, new Type[0]);
     40 
     41     private InterceptFieldFilter filter;
     42 
     43     public InterceptFieldTransformer(InterceptFieldFilter filter) {
     44         this.filter = filter;
     45     }
     46 
     47     public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) {
     48         if (!TypeUtils.isInterface(access)) {
     49             super.begin_class(version, access, className, superType, TypeUtils.add(interfaces, ENABLED), sourceFile);
     50 
     51             super.declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT,
     52                                 CALLBACK_FIELD,
     53                                 CALLBACK,
     54                                 null);
     55 
     56             CodeEmitter e;
     57             e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_GET, null);
     58             e.load_this();
     59             e.getfield(CALLBACK_FIELD);
     60             e.return_value();
     61             e.end_method();
     62 
     63             e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_SET, null);
     64             e.load_this();
     65             e.load_arg(0);
     66             e.putfield(CALLBACK_FIELD);
     67             e.return_value();
     68             e.end_method();
     69         } else {
     70             super.begin_class(version, access, className, superType, interfaces, sourceFile);
     71         }
     72     }
     73 
     74     public void declare_field(int access, String name, Type type, Object value) {
     75         super.declare_field(access, name, type, value);
     76         if (!TypeUtils.isStatic(access)) {
     77             if (filter.acceptRead(getClassType(), name)) {
     78                 addReadMethod(name, type);
     79             }
     80             if (filter.acceptWrite(getClassType(), name)) {
     81                 addWriteMethod(name, type);
     82             }
     83         }
     84     }
     85 
     86     private void addReadMethod(String name, Type type) {
     87         CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC,
     88                                            readMethodSig(name, type.getDescriptor()),
     89                                            null);
     90         e.load_this();
     91         e.getfield(name);
     92         e.load_this();
     93         e.invoke_interface(ENABLED,ENABLED_GET);
     94         Label intercept = e.make_label();
     95         e.ifnonnull(intercept);
     96         e.return_value();
     97 
     98         e.mark(intercept);
     99         Local result = e.make_local(type);
    100         e.store_local(result);
    101         e.load_this();
    102         e.invoke_interface(ENABLED,ENABLED_GET);
    103         e.load_this();
    104         e.push(name);
    105         e.load_local(result);
    106         e.invoke_interface(CALLBACK, readCallbackSig(type));
    107         if (!TypeUtils.isPrimitive(type)) {
    108             e.checkcast(type);
    109         }
    110         e.return_value();
    111         e.end_method();
    112     }
    113 
    114     private void addWriteMethod(String name, Type type) {
    115         CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC,
    116                                            writeMethodSig(name, type.getDescriptor()),
    117                                            null);
    118         e.load_this();
    119         e.dup();
    120         e.invoke_interface(ENABLED,ENABLED_GET);
    121         Label skip = e.make_label();
    122         e.ifnull(skip);
    123 
    124         e.load_this();
    125         e.invoke_interface(ENABLED,ENABLED_GET);
    126         e.load_this();
    127         e.push(name);
    128         e.load_this();
    129         e.getfield(name);
    130         e.load_arg(0);
    131         e.invoke_interface(CALLBACK, writeCallbackSig(type));
    132         if (!TypeUtils.isPrimitive(type)) {
    133             e.checkcast(type);
    134         }
    135         Label go = e.make_label();
    136         e.goTo(go);
    137         e.mark(skip);
    138         e.load_arg(0);
    139         e.mark(go);
    140         e.putfield(name);
    141         e.return_value();
    142         e.end_method();
    143     }
    144 
    145     public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
    146         return new CodeEmitter(super.begin_method(access, sig, exceptions)) {
    147             public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    148                 Type towner = TypeUtils.fromInternalName(owner);
    149                 switch (opcode) {
    150                 case Constants.GETFIELD:
    151                     if (filter.acceptRead(towner, name)) {
    152                         helper(towner, readMethodSig(name, desc));
    153                         return;
    154                     }
    155                     break;
    156                 case Constants.PUTFIELD:
    157                     if (filter.acceptWrite(towner, name)) {
    158                         helper(towner, writeMethodSig(name, desc));
    159                         return;
    160                     }
    161                     break;
    162                 }
    163                 super.visitFieldInsn(opcode, owner, name, desc);
    164             }
    165 
    166             private void helper(Type owner, Signature sig) {
    167                 invoke_virtual(owner, sig);
    168             }
    169         };
    170     }
    171 
    172     private static Signature readMethodSig(String name, String desc) {
    173         return new Signature("$cglib_read_" + name, "()" + desc);
    174     }
    175 
    176     private static Signature writeMethodSig(String name, String desc) {
    177         return new Signature("$cglib_write_" + name, "(" + desc + ")V");
    178     }
    179 
    180     private static Signature readCallbackSig(Type type) {
    181         Type remap = remap(type);
    182         return new Signature("read" + callbackName(remap),
    183                              remap,
    184                              new Type[]{ Constants.TYPE_OBJECT,
    185                                          Constants.TYPE_STRING,
    186                                          remap });
    187     }
    188 
    189     private static Signature writeCallbackSig(Type type) {
    190         Type remap = remap(type);
    191         return new Signature("write" + callbackName(remap),
    192                              remap,
    193                              new Type[]{ Constants.TYPE_OBJECT,
    194                                          Constants.TYPE_STRING,
    195                                          remap,
    196                                          remap });
    197     }
    198 
    199     private static Type remap(Type type) {
    200         switch (type.getSort()) {
    201         case Type.OBJECT:
    202         case Type.ARRAY:
    203             return Constants.TYPE_OBJECT;
    204         default:
    205             return type;
    206         }
    207     }
    208 
    209     private static String callbackName(Type type) {
    210         return (type == Constants.TYPE_OBJECT) ?
    211             "Object" :
    212             TypeUtils.upperFirst(TypeUtils.getClassName(type));
    213     }
    214 }
    215