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.transform.impl; 17 18import java.util.*; 19 20import org.mockito.asm.Attribute; 21import org.mockito.asm.Label; 22import org.mockito.asm.Type; 23import org.mockito.cglib.core.*; 24import org.mockito.cglib.transform.*; 25 26public class FieldProviderTransformer extends ClassEmitterTransformer { 27 28 private static final String FIELD_NAMES = "CGLIB$FIELD_NAMES"; 29 private static final String FIELD_TYPES = "CGLIB$FIELD_TYPES"; 30 31 private static final Type FIELD_PROVIDER = 32 TypeUtils.parseType("org.mockito.cglib.transform.impl.FieldProvider"); 33 private static final Type ILLEGAL_ARGUMENT_EXCEPTION = 34 TypeUtils.parseType("IllegalArgumentException"); 35 private static final Signature PROVIDER_GET = 36 TypeUtils.parseSignature("Object getField(String)"); 37 private static final Signature PROVIDER_SET = 38 TypeUtils.parseSignature("void setField(String, Object)"); 39 private static final Signature PROVIDER_SET_BY_INDEX = 40 TypeUtils.parseSignature("void setField(int, Object)"); 41 private static final Signature PROVIDER_GET_BY_INDEX = 42 TypeUtils.parseSignature("Object getField(int)"); 43 private static final Signature PROVIDER_GET_TYPES = 44 TypeUtils.parseSignature("Class[] getFieldTypes()"); 45 private static final Signature PROVIDER_GET_NAMES = 46 TypeUtils.parseSignature("String[] getFieldNames()"); 47 48 private int access; 49 private Map fields; 50 51 public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { 52 if (!TypeUtils.isAbstract(access)) { 53 interfaces = TypeUtils.add(interfaces, FIELD_PROVIDER); 54 } 55 this.access = access; 56 fields = new HashMap(); 57 super.begin_class(version, access, className, superType, interfaces, sourceFile); 58 } 59 60 public void declare_field(int access, String name, Type type, Object value) { 61 super.declare_field(access, name, type, value); 62 63 if (!TypeUtils.isStatic(access)) { 64 fields.put(name, type); 65 } 66 } 67 68 public void end_class() { 69 if (!TypeUtils.isInterface(access)) { 70 try { 71 generate(); 72 } catch (RuntimeException e) { 73 throw e; 74 } catch (Exception e) { 75 throw new CodeGenerationException(e); 76 } 77 } 78 super.end_class(); 79 } 80 81 private void generate() throws Exception { 82 final String[] names = (String[])fields.keySet().toArray(new String[fields.size()]); 83 84 int indexes[] = new int[names.length]; 85 for (int i = 0; i < indexes.length; i++) { 86 indexes[i] = i; 87 } 88 89 super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_NAMES, Constants.TYPE_STRING_ARRAY, null); 90 super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_TYPES, Constants.TYPE_CLASS_ARRAY, null); 91 92 // use separate methods here because each process switch inner class needs a final CodeEmitter 93 initFieldProvider(names); 94 getNames(); 95 getTypes(); 96 getField(names); 97 setField(names); 98 setByIndex(names, indexes); 99 getByIndex(names, indexes); 100 } 101 102 private void initFieldProvider(String[] names) { 103 CodeEmitter e = getStaticHook(); 104 EmitUtils.push_object(e, names); 105 e.putstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); 106 107 e.push(names.length); 108 e.newarray(Constants.TYPE_CLASS); 109 e.dup(); 110 for(int i = 0; i < names.length; i++ ){ 111 e.dup(); 112 e.push(i); 113 Type type = (Type)fields.get(names[i]); 114 EmitUtils.load_class(e, type); 115 e.aastore(); 116 } 117 e.putstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); 118 } 119 120 private void getNames() { 121 CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_NAMES, null); 122 e.getstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); 123 e.return_value(); 124 e.end_method(); 125 } 126 127 private void getTypes() { 128 CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_TYPES, null); 129 e.getstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); 130 e.return_value(); 131 e.end_method(); 132 } 133 134 private void setByIndex(final String[] names, final int[] indexes) throws Exception { 135 final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_SET_BY_INDEX, null); 136 e.load_this(); 137 e.load_arg(1); 138 e.load_arg(0); 139 e.process_switch(indexes, new ProcessSwitchCallback() { 140 public void processCase(int key, Label end) throws Exception { 141 Type type = (Type)fields.get(names[key]); 142 e.unbox(type); 143 e.putfield(names[key]); 144 e.return_value(); 145 } 146 public void processDefault() throws Exception { 147 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); 148 } 149 }); 150 e.end_method(); 151 } 152 153 private void getByIndex(final String[] names, final int[] indexes) throws Exception { 154 final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_BY_INDEX, null); 155 e.load_this(); 156 e.load_arg(0); 157 e.process_switch(indexes, new ProcessSwitchCallback() { 158 public void processCase(int key, Label end) throws Exception { 159 Type type = (Type)fields.get(names[key]); 160 e.getfield(names[key]); 161 e.box(type); 162 e.return_value(); 163 } 164 public void processDefault() throws Exception { 165 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); 166 } 167 }); 168 e.end_method(); 169 } 170 171 // TODO: if this is used to enhance class files SWITCH_STYLE_TRIE should be used 172 // to avoid JVM hashcode implementation incompatibilities 173 private void getField(String[] names) throws Exception { 174 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_GET, null); 175 e.load_this(); 176 e.load_arg(0); 177 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 178 public void processCase(Object key, Label end) { 179 Type type = (Type)fields.get(key); 180 e.getfield((String)key); 181 e.box(type); 182 e.return_value(); 183 } 184 public void processDefault() { 185 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); 186 } 187 }); 188 e.end_method(); 189 } 190 191 private void setField(String[] names) throws Exception { 192 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_SET, null); 193 e.load_this(); 194 e.load_arg(1); 195 e.load_arg(0); 196 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 197 public void processCase(Object key, Label end) { 198 Type type = (Type)fields.get(key); 199 e.unbox(type); 200 e.putfield((String)key); 201 e.return_value(); 202 } 203 public void processDefault() { 204 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); 205 } 206 }); 207 e.end_method(); 208 } 209} 210