FastClassEmitter.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1/* 2 * Copyright 2003,2004 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.reflect; 17 18import java.lang.reflect.*; 19import java.util.*; 20 21import org.mockito.asm.ClassVisitor; 22import org.mockito.asm.Label; 23import org.mockito.asm.Type; 24import org.mockito.cglib.core.*; 25 26class FastClassEmitter extends ClassEmitter { 27 private static final Signature CSTRUCT_CLASS = 28 TypeUtils.parseConstructor("Class"); 29 private static final Signature METHOD_GET_INDEX = 30 TypeUtils.parseSignature("int getIndex(String, Class[])"); 31 private static final Signature SIGNATURE_GET_INDEX = 32 new Signature("getIndex", Type.INT_TYPE, new Type[]{ Constants.TYPE_SIGNATURE }); 33 private static final Signature TO_STRING = 34 TypeUtils.parseSignature("String toString()"); 35 private static final Signature CONSTRUCTOR_GET_INDEX = 36 TypeUtils.parseSignature("int getIndex(Class[])"); 37 private static final Signature INVOKE = 38 TypeUtils.parseSignature("Object invoke(int, Object, Object[])"); 39 private static final Signature NEW_INSTANCE = 40 TypeUtils.parseSignature("Object newInstance(int, Object[])"); 41 private static final Signature GET_MAX_INDEX = 42 TypeUtils.parseSignature("int getMaxIndex()"); 43 private static final Signature GET_SIGNATURE_WITHOUT_RETURN_TYPE = 44 TypeUtils.parseSignature("String getSignatureWithoutReturnType(String, Class[])"); 45 private static final Type FAST_CLASS = 46 TypeUtils.parseType("org.mockito.cglib.reflect.FastClass"); 47 private static final Type ILLEGAL_ARGUMENT_EXCEPTION = 48 TypeUtils.parseType("IllegalArgumentException"); 49 private static final Type INVOCATION_TARGET_EXCEPTION = 50 TypeUtils.parseType("java.lang.reflect.InvocationTargetException"); 51 private static final Type[] INVOCATION_TARGET_EXCEPTION_ARRAY = { INVOCATION_TARGET_EXCEPTION }; 52 53 public FastClassEmitter(ClassVisitor v, String className, Class type) { 54 super(v); 55 56 Type base = Type.getType(type); 57 begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE); 58 59 // constructor 60 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_CLASS, null); 61 e.load_this(); 62 e.load_args(); 63 e.super_invoke_constructor(CSTRUCT_CLASS); 64 e.return_value(); 65 e.end_method(); 66 67 VisibilityPredicate vp = new VisibilityPredicate(type, false); 68 List methods = ReflectUtils.addAllMethods(type, new ArrayList()); 69 CollectionUtils.filter(methods, vp); 70 CollectionUtils.filter(methods, new DuplicatesPredicate()); 71 List constructors = new ArrayList(Arrays.asList(type.getDeclaredConstructors())); 72 CollectionUtils.filter(constructors, vp); 73 74 // getIndex(String) 75 emitIndexBySignature(methods); 76 77 // getIndex(String, Class[]) 78 emitIndexByClassArray(methods); 79 80 // getIndex(Class[]) 81 e = begin_method(Constants.ACC_PUBLIC, CONSTRUCTOR_GET_INDEX, null); 82 e.load_args(); 83 List info = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance()); 84 EmitUtils.constructor_switch(e, info, new GetIndexCallback(e, info)); 85 e.end_method(); 86 87 // invoke(int, Object, Object[]) 88 e = begin_method(Constants.ACC_PUBLIC, INVOKE, INVOCATION_TARGET_EXCEPTION_ARRAY); 89 e.load_arg(1); 90 e.checkcast(base); 91 e.load_arg(0); 92 invokeSwitchHelper(e, methods, 2, base); 93 e.end_method(); 94 95 // newInstance(int, Object[]) 96 e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, INVOCATION_TARGET_EXCEPTION_ARRAY); 97 e.new_instance(base); 98 e.dup(); 99 e.load_arg(0); 100 invokeSwitchHelper(e, constructors, 1, base); 101 e.end_method(); 102 103 // getMaxIndex() 104 e = begin_method(Constants.ACC_PUBLIC, GET_MAX_INDEX, null); 105 e.push(methods.size() - 1); 106 e.return_value(); 107 e.end_method(); 108 109 end_class(); 110 } 111 112 // TODO: support constructor indices ("<init>") 113 private void emitIndexBySignature(List methods) { 114 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SIGNATURE_GET_INDEX, null); 115 List signatures = CollectionUtils.transform(methods, new Transformer() { 116 public Object transform(Object obj) { 117 return ReflectUtils.getSignature((Method)obj).toString(); 118 } 119 }); 120 e.load_arg(0); 121 e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); 122 signatureSwitchHelper(e, signatures); 123 e.end_method(); 124 } 125 126 private static final int TOO_MANY_METHODS = 100; // TODO 127 private void emitIndexByClassArray(List methods) { 128 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, METHOD_GET_INDEX, null); 129 if (methods.size() > TOO_MANY_METHODS) { 130 // hack for big classes 131 List signatures = CollectionUtils.transform(methods, new Transformer() { 132 public Object transform(Object obj) { 133 String s = ReflectUtils.getSignature((Method)obj).toString(); 134 return s.substring(0, s.lastIndexOf(')') + 1); 135 } 136 }); 137 e.load_args(); 138 e.invoke_static(FAST_CLASS, GET_SIGNATURE_WITHOUT_RETURN_TYPE); 139 signatureSwitchHelper(e, signatures); 140 } else { 141 e.load_args(); 142 List info = CollectionUtils.transform(methods, MethodInfoTransformer.getInstance()); 143 EmitUtils.method_switch(e, info, new GetIndexCallback(e, info)); 144 } 145 e.end_method(); 146 } 147 148 private void signatureSwitchHelper(final CodeEmitter e, final List signatures) { 149 ObjectSwitchCallback callback = new ObjectSwitchCallback() { 150 public void processCase(Object key, Label end) { 151 // TODO: remove linear indexOf 152 e.push(signatures.indexOf(key)); 153 e.return_value(); 154 } 155 public void processDefault() { 156 e.push(-1); 157 e.return_value(); 158 } 159 }; 160 EmitUtils.string_switch(e, 161 (String[])signatures.toArray(new String[signatures.size()]), 162 Constants.SWITCH_STYLE_HASH, 163 callback); 164 } 165 166 private static void invokeSwitchHelper(final CodeEmitter e, List members, final int arg, final Type base) { 167 final List info = CollectionUtils.transform(members, MethodInfoTransformer.getInstance()); 168 final Label illegalArg = e.make_label(); 169 Block block = e.begin_block(); 170 e.process_switch(getIntRange(info.size()), new ProcessSwitchCallback() { 171 public void processCase(int key, Label end) { 172 MethodInfo method = (MethodInfo)info.get(key); 173 Type[] types = method.getSignature().getArgumentTypes(); 174 for (int i = 0; i < types.length; i++) { 175 e.load_arg(arg); 176 e.aaload(i); 177 e.unbox(types[i]); 178 } 179 // TODO: change method lookup process so MethodInfo will already reference base 180 // instead of superclass when superclass method is inaccessible 181 e.invoke(method, base); 182 if (!TypeUtils.isConstructor(method)) { 183 e.box(method.getSignature().getReturnType()); 184 } 185 e.return_value(); 186 } 187 public void processDefault() { 188 e.goTo(illegalArg); 189 } 190 }); 191 block.end(); 192 EmitUtils.wrap_throwable(block, INVOCATION_TARGET_EXCEPTION); 193 e.mark(illegalArg); 194 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Cannot find matching method/constructor"); 195 } 196 197 private static class GetIndexCallback implements ObjectSwitchCallback { 198 private CodeEmitter e; 199 private Map indexes = new HashMap(); 200 201 public GetIndexCallback(CodeEmitter e, List methods) { 202 this.e = e; 203 int index = 0; 204 for (Iterator it = methods.iterator(); it.hasNext();) { 205 indexes.put(it.next(), new Integer(index++)); 206 } 207 } 208 209 public void processCase(Object key, Label end) { 210 e.push(((Integer)indexes.get(key)).intValue()); 211 e.return_value(); 212 } 213 214 public void processDefault() { 215 e.push(-1); 216 e.return_value(); 217 } 218 } 219 220 private static int[] getIntRange(int length) { 221 int[] range = new int[length]; 222 for (int i = 0; i < length; i++) { 223 range[i] = i; 224 } 225 return range; 226 } 227} 228