001: /*
002: * Copyright 2003,2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package net.sf.cglib.reflect;
017:
018: import java.lang.reflect.*;
019: import java.util.*;
020: import net.sf.cglib.core.*;
021: import org.objectweb.asm.ClassVisitor;
022: import org.objectweb.asm.Label;
023: import org.objectweb.asm.Type;
024:
025: class FastClassEmitter extends ClassEmitter {
026: private static final Signature CSTRUCT_CLASS = TypeUtils
027: .parseConstructor("Class");
028: private static final Signature METHOD_GET_INDEX = TypeUtils
029: .parseSignature("int getIndex(String, Class[])");
030: private static final Signature SIGNATURE_GET_INDEX = new Signature(
031: "getIndex", Type.INT_TYPE,
032: new Type[] { Constants.TYPE_SIGNATURE });
033: private static final Signature TO_STRING = TypeUtils
034: .parseSignature("String toString()");
035: private static final Signature CONSTRUCTOR_GET_INDEX = TypeUtils
036: .parseSignature("int getIndex(Class[])");
037: private static final Signature INVOKE = TypeUtils
038: .parseSignature("Object invoke(int, Object, Object[])");
039: private static final Signature NEW_INSTANCE = TypeUtils
040: .parseSignature("Object newInstance(int, Object[])");
041: private static final Signature GET_MAX_INDEX = TypeUtils
042: .parseSignature("int getMaxIndex()");
043: private static final Signature GET_SIGNATURE_WITHOUT_RETURN_TYPE = TypeUtils
044: .parseSignature("String getSignatureWithoutReturnType(String, Class[])");
045: private static final Type FAST_CLASS = TypeUtils
046: .parseType("net.sf.cglib.reflect.FastClass");
047: private static final Type ILLEGAL_ARGUMENT_EXCEPTION = TypeUtils
048: .parseType("IllegalArgumentException");
049: private static final Type INVOCATION_TARGET_EXCEPTION = TypeUtils
050: .parseType("java.lang.reflect.InvocationTargetException");
051: private static final Type[] INVOCATION_TARGET_EXCEPTION_ARRAY = { INVOCATION_TARGET_EXCEPTION };
052:
053: public FastClassEmitter(ClassVisitor v, String className, Class type) {
054: super (v);
055:
056: Type base = Type.getType(type);
057: begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className,
058: FAST_CLASS, null, Constants.SOURCE_FILE);
059:
060: // constructor
061: CodeEmitter e = begin_method(Constants.ACC_PUBLIC,
062: CSTRUCT_CLASS, null);
063: e.load_this ();
064: e.load_args();
065: e.super _invoke_constructor(CSTRUCT_CLASS);
066: e.return_value();
067: e.end_method();
068:
069: VisibilityPredicate vp = new VisibilityPredicate(type, false);
070: List methods = ReflectUtils
071: .addAllMethods(type, new ArrayList());
072: CollectionUtils.filter(methods, vp);
073: CollectionUtils.filter(methods, new DuplicatesPredicate());
074: List constructors = new ArrayList(Arrays.asList(type
075: .getDeclaredConstructors()));
076: CollectionUtils.filter(constructors, vp);
077:
078: // getIndex(String)
079: emitIndexBySignature(methods);
080:
081: // getIndex(String, Class[])
082: emitIndexByClassArray(methods);
083:
084: // getIndex(Class[])
085: e = begin_method(Constants.ACC_PUBLIC, CONSTRUCTOR_GET_INDEX,
086: null);
087: e.load_args();
088: List info = CollectionUtils.transform(constructors,
089: MethodInfoTransformer.getInstance());
090: EmitUtils.constructor_switch(e, info, new GetIndexCallback(e,
091: info));
092: e.end_method();
093:
094: // invoke(int, Object, Object[])
095: e = begin_method(Constants.ACC_PUBLIC, INVOKE,
096: INVOCATION_TARGET_EXCEPTION_ARRAY);
097: e.load_arg(1);
098: e.checkcast(base);
099: e.load_arg(0);
100: invokeSwitchHelper(e, methods, 2, base);
101: e.end_method();
102:
103: // newInstance(int, Object[])
104: e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE,
105: INVOCATION_TARGET_EXCEPTION_ARRAY);
106: e.new_instance(base);
107: e.dup();
108: e.load_arg(0);
109: invokeSwitchHelper(e, constructors, 1, base);
110: e.end_method();
111:
112: // getMaxIndex()
113: e = begin_method(Constants.ACC_PUBLIC, GET_MAX_INDEX, null);
114: e.push(methods.size() - 1);
115: e.return_value();
116: e.end_method();
117:
118: end_class();
119: }
120:
121: // TODO: support constructor indices ("<init>")
122: private void emitIndexBySignature(List methods) {
123: CodeEmitter e = begin_method(Constants.ACC_PUBLIC,
124: SIGNATURE_GET_INDEX, null);
125: List signatures = CollectionUtils.transform(methods,
126: new Transformer() {
127: public Object transform(Object obj) {
128: return ReflectUtils.getSignature((Method) obj)
129: .toString();
130: }
131: });
132: e.load_arg(0);
133: e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
134: signatureSwitchHelper(e, signatures);
135: e.end_method();
136: }
137:
138: private static final int TOO_MANY_METHODS = 100; // TODO
139:
140: private void emitIndexByClassArray(List methods) {
141: CodeEmitter e = begin_method(Constants.ACC_PUBLIC,
142: METHOD_GET_INDEX, null);
143: if (methods.size() > TOO_MANY_METHODS) {
144: // hack for big classes
145: List signatures = CollectionUtils.transform(methods,
146: new Transformer() {
147: public Object transform(Object obj) {
148: String s = ReflectUtils.getSignature(
149: (Method) obj).toString();
150: return s.substring(0,
151: s.lastIndexOf(')') + 1);
152: }
153: });
154: e.load_args();
155: e.invoke_static(FAST_CLASS,
156: GET_SIGNATURE_WITHOUT_RETURN_TYPE);
157: signatureSwitchHelper(e, signatures);
158: } else {
159: e.load_args();
160: List info = CollectionUtils.transform(methods,
161: MethodInfoTransformer.getInstance());
162: EmitUtils.method_switch(e, info, new GetIndexCallback(e,
163: info));
164: }
165: e.end_method();
166: }
167:
168: private void signatureSwitchHelper(final CodeEmitter e,
169: final List signatures) {
170: ObjectSwitchCallback callback = new ObjectSwitchCallback() {
171: public void processCase(Object key, Label end) {
172: // TODO: remove linear indexOf
173: e.push(signatures.indexOf(key));
174: e.return_value();
175: }
176:
177: public void processDefault() {
178: e.push(-1);
179: e.return_value();
180: }
181: };
182: EmitUtils.string_switch(e, (String[]) signatures
183: .toArray(new String[signatures.size()]),
184: Constants.SWITCH_STYLE_HASH, callback);
185: }
186:
187: private static void invokeSwitchHelper(final CodeEmitter e,
188: List members, final int arg, final Type base) {
189: final List info = CollectionUtils.transform(members,
190: MethodInfoTransformer.getInstance());
191: final Label illegalArg = e.make_label();
192: Block block = e.begin_block();
193: e.process_switch(getIntRange(info.size()),
194: new ProcessSwitchCallback() {
195: public void processCase(int key, Label end) {
196: MethodInfo method = (MethodInfo) info.get(key);
197: Type[] types = method.getSignature()
198: .getArgumentTypes();
199: for (int i = 0; i < types.length; i++) {
200: e.load_arg(arg);
201: e.aaload(i);
202: e.unbox(types[i]);
203: }
204: // TODO: change method lookup process so MethodInfo will already reference base
205: // instead of superclass when superclass method is inaccessible
206: e.invoke(method, base);
207: if (!TypeUtils.isConstructor(method)) {
208: e
209: .box(method.getSignature()
210: .getReturnType());
211: }
212: e.return_value();
213: }
214:
215: public void processDefault() {
216: e.goTo(illegalArg);
217: }
218: });
219: block.end();
220: EmitUtils.wrap_throwable(block, INVOCATION_TARGET_EXCEPTION);
221: e.mark(illegalArg);
222: e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION,
223: "Cannot find matching method/constructor");
224: }
225:
226: private static class GetIndexCallback implements
227: ObjectSwitchCallback {
228: private CodeEmitter e;
229: private Map indexes = new HashMap();
230:
231: public GetIndexCallback(CodeEmitter e, List methods) {
232: this .e = e;
233: int index = 0;
234: for (Iterator it = methods.iterator(); it.hasNext();) {
235: indexes.put(it.next(), new Integer(index++));
236: }
237: }
238:
239: public void processCase(Object key, Label end) {
240: e.push(((Integer) indexes.get(key)).intValue());
241: e.return_value();
242: }
243:
244: public void processDefault() {
245: e.push(-1);
246: e.return_value();
247: }
248: }
249:
250: private static int[] getIntRange(int length) {
251: int[] range = new int[length];
252: for (int i = 0; i < length; i++) {
253: range[i] = i;
254: }
255: return range;
256: }
257: }
|