KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > cglib > reflect > FastClassEmitter


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  */

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