KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > cglib > proxy > EnhancerEmitter


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2002 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  * if any, must include the following acknowledgment:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowledgment may appear in the software itself,
24  * if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  * not be used to endorse or promote products derived from this
28  * software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  * nor may "Apache" appear in their name, without prior written
33  * permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation. For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */

54 package org.logicalcobwebs.cglib.proxy;
55
56 import java.io.ObjectStreamException JavaDoc;
57 import java.lang.reflect.*;
58 import java.util.*;
59 import org.logicalcobwebs.cglib.core.*;
60 import org.logicalcobwebs.asm.ClassVisitor;
61 import org.logicalcobwebs.asm.Label;
62 import org.logicalcobwebs.asm.Type;
63
64 class EnhancerEmitter extends ClassEmitter {
65     private static final String JavaDoc CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
66
67     private static final String JavaDoc SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
68
69     private static final Type ILLEGAL_STATE_EXCEPTION =
70       TypeUtils.parseType("IllegalStateException");
71     private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
72       TypeUtils.parseType("IllegalArgumentException");
73     private static final Type THREAD_LOCAL =
74       TypeUtils.parseType("ThreadLocal");
75     private static final Type FACTORY =
76       TypeUtils.parseType("org.logicalcobwebs.cglib.proxy.Factory");
77
78     private static final Signature CSTRUCT_NULL =
79       TypeUtils.parseConstructor("");
80     private static final Signature SET_THREAD_CALLBACKS =
81       TypeUtils.parseSignature("void " + SET_THREAD_CALLBACKS_NAME + "(org.logicalcobwebs.cglib.proxy.Callback[])");
82     private static final Signature NEW_INSTANCE =
83       TypeUtils.parseSignature("Object newInstance(org.logicalcobwebs.cglib.proxy.Callback[])");
84     private static final Signature MULTIARG_NEW_INSTANCE =
85       TypeUtils.parseSignature("Object newInstance(Class[], Object[], org.logicalcobwebs.cglib.proxy.Callback[])");
86     private static final Signature SINGLE_NEW_INSTANCE =
87       TypeUtils.parseSignature("Object newInstance(org.logicalcobwebs.cglib.proxy.Callback)");
88     private static final Signature COPY_NEW_INSTANCE =
89       TypeUtils.parseSignature("Object newInstance()");
90     private static final Signature COPY_MULTIARG_NEW_INSTANCE =
91       TypeUtils.parseSignature("Object newInstance(Class[], Object[])");
92     private static final Signature SET_CALLBACK =
93       TypeUtils.parseSignature("void setCallback(int, org.logicalcobwebs.cglib.proxy.Callback)");
94     private static final Signature GET_CALLBACK =
95       TypeUtils.parseSignature("org.logicalcobwebs.cglib.proxy.Callback getCallback(int)");
96     private static final Signature SET_CALLBACKS =
97       TypeUtils.parseSignature("void setCallbacks(org.logicalcobwebs.cglib.proxy.Callback[])");
98
99     private static final Signature THREAD_LOCAL_GET =
100       TypeUtils.parseSignature("Object get()");
101     private static final Signature THREAD_LOCAL_SET =
102       TypeUtils.parseSignature("void set(Object)");
103
104     private Class JavaDoc[] callbackTypes;
105     
106     public EnhancerEmitter(ClassVisitor v,
107                            String JavaDoc className,
108                            Class JavaDoc superclass,
109                            Class JavaDoc[] interfaces,
110                            CallbackFilter filter,
111                            Class JavaDoc[] callbackTypes,
112                            boolean useFactory) throws Exception JavaDoc {
113         super(v);
114         if (superclass == null) {
115             superclass = Object JavaDoc.class;
116         }
117         this.callbackTypes = callbackTypes;
118
119         begin_class(Constants.ACC_PUBLIC,
120                     className,
121                     Type.getType(superclass),
122                     (useFactory ?
123                      TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
124                      TypeUtils.getTypes(interfaces)),
125                     Constants.SOURCE_FILE);
126         
127         List clist = new ArrayList(Arrays.asList(superclass.getDeclaredConstructors()));
128         CollectionUtils.filter(clist, new VisibilityPredicate(superclass, true));
129         if (clist.size() == 0) {
130             throw new IllegalArgumentException JavaDoc("No visible constructors in " + superclass);
131         }
132         Constructor[] constructors = (Constructor[])clist.toArray(new Constructor[clist.size()]);
133
134         // Order is very important: must add superclass, then
135
// its superclass chain, then each interface and
136
// its superinterfaces.
137
List methods = new ArrayList();
138         ReflectUtils.addAllMethods(superclass, methods);
139
140         List interfaceMethods = new ArrayList();
141         if (interfaces != null) {
142             for (int i = 0; i < interfaces.length; i++) {
143                 if (interfaces[i] != Factory.class) {
144                     ReflectUtils.addAllMethods(interfaces[i], interfaceMethods);
145                 }
146             }
147         }
148         Set forcePublic = MethodWrapper.createSet(interfaceMethods);
149         methods.addAll(interfaceMethods);
150         CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
151         CollectionUtils.filter(methods, new DuplicatesPredicate());
152         removeFinal(methods);
153
154         Map groups = new HashMap();
155         Map indexes = new HashMap();
156         for (Iterator it = methods.iterator(); it.hasNext();) {
157             Method method = (Method)it.next();
158             int index = filter.accept(method);
159             if (index >= callbackTypes.length) {
160                 throw new IllegalArgumentException JavaDoc("Callback filter returned an index that is too large: " + index);
161             }
162             indexes.put(method, new Integer JavaDoc(index));
163             Object JavaDoc gen = CallbackUtils.getGenerator(callbackTypes[index]);
164             List group = (List)groups.get(gen);
165             if (group == null) {
166                 groups.put(gen, group = new ArrayList(methods.size()));
167             }
168             group.add(method);
169         }
170
171         declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null, null);
172         emitMethods(groups, indexes, forcePublic);
173         emitConstructors(constructors);
174         emitSetThreadCallbacks();
175
176         if (useFactory) {
177             int[] keys = getCallbackKeys();
178             emitNewInstanceCallbacks();
179             emitNewInstanceCallback();
180             emitNewInstanceMultiarg(constructors);
181             emitNewInstanceCopy();
182             emitNewInstanceMultiargCopy(constructors);
183             emitGetCallback(keys);
184             emitSetCallback(keys);
185             emitSetCallbacks();
186         }
187         end_class();
188     }
189
190     static void setThreadCallbacks(Class JavaDoc type, Callback[] callbacks) {
191         // TODO: optimize
192
try {
193             Method setter = type.getDeclaredMethod(SET_THREAD_CALLBACKS_NAME, new Class JavaDoc[]{ Callback[].class });
194             setter.invoke(null, new Object JavaDoc[]{ callbacks });
195         } catch (NoSuchMethodException JavaDoc e) {
196             throw new IllegalArgumentException JavaDoc(type + " is not an enhanced class");
197         } catch (IllegalAccessException JavaDoc e) {
198             throw new CodeGenerationException(e);
199         } catch (InvocationTargetException e) {
200             throw new CodeGenerationException(e);
201         }
202     }
203
204     private void emitConstructors(Constructor[] constructors) {
205         for (int i = 0; i < constructors.length; i++) {
206             Signature sig = ReflectUtils.getSignature(constructors[i]);
207             CodeEmitter e = begin_method(Constants.ACC_PUBLIC,
208                                          sig,
209                                          ReflectUtils.getExceptionTypes(constructors[i]),
210                                          null);
211             e.load_this();
212             e.dup();
213             e.load_args();
214             e.super_invoke_constructor(sig);
215             e.push(1);
216             e.putfield(CONSTRUCTED_FIELD);
217             for (int j = 0; j < callbackTypes.length; j++) {
218                 e.load_this();
219                 e.dup();
220                 e.getfield(getThreadLocal(j));
221                 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
222                 e.checkcast(Type.getType(callbackTypes[j]));
223                 e.putfield(getCallbackField(j));
224
225                 // clear thread-locals; TODO: test!
226
e.getfield(getThreadLocal(j));
227                 e.aconst_null();
228                 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
229             }
230             e.return_value();
231             e.end_method();
232         }
233     }
234     
235     private int[] getCallbackKeys() {
236         int[] keys = new int[callbackTypes.length];
237         for (int i = 0; i < callbackTypes.length; i++) {
238             keys[i] = i;
239         }
240         return keys;
241     }
242
243     private void emitGetCallback(int[] keys) {
244         final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null, null);
245         e.load_this();
246         e.load_arg(0);
247         e.process_switch(keys, new ProcessSwitchCallback() {
248             public void processCase(int key, Label end) {
249                 e.getfield(getCallbackField(key));
250                 e.goTo(end);
251             }
252             public void processDefault() {
253                 e.pop(); // stack height
254
e.aconst_null();
255             }
256         });
257         e.return_value();
258         e.end_method();
259     }
260
261     private void emitSetCallback(int[] keys) {
262         final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null, null);
263         e.load_this();
264         e.load_arg(1);
265         e.dup();
266         e.load_arg(0);
267         e.process_switch(keys, new ProcessSwitchCallback() {
268             public void processCase(int key, Label end) {
269                 // we set thread locals too in case this method is called from within constructor (as Proxy does)
270
e.getfield(getThreadLocal(key));
271                 e.swap();
272                 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
273                 e.checkcast(Type.getType(callbackTypes[key]));
274                 e.putfield(getCallbackField(key));
275                 e.goTo(end);
276             }
277             public void processDefault() {
278                 // stack height
279
e.pop2();
280                 e.pop();
281             }
282         });
283         e.return_value();
284         e.end_method();
285     }
286         
287     private void emitSetCallbacks() {
288         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null, null);
289         emitSetThreadCallbacks(e);
290         e.load_this();
291         e.load_arg(0);
292         for (int i = 0; i < callbackTypes.length; i++) {
293             e.dup2();
294             e.aaload(i);
295             e.checkcast(Type.getType(callbackTypes[i]));
296             e.putfield(getCallbackField(i));
297         }
298         e.return_value();
299         e.end_method();
300     }
301
302     private void emitNewInstanceCallbacks() {
303         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null, null);
304         e.load_arg(0);
305         e.invoke_static_this(SET_THREAD_CALLBACKS);
306         emitCommonNewInstance(e);
307     }
308
309     private void emitNewInstanceCopy() {
310         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, COPY_NEW_INSTANCE, null, null);
311         emitCopyCallbacks(e);
312         emitCommonNewInstance(e);
313     }
314
315     private void emitCopyCallbacks(CodeEmitter e) {
316         for (int i = 0; i < callbackTypes.length; i++) {
317             e.getfield(getThreadLocal(i));
318             e.load_this();
319             e.getfield(getCallbackField(i));
320             e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
321         }
322     }
323
324     private void emitCommonNewInstance(CodeEmitter e) {
325         e.new_instance_this();
326         e.dup();
327         e.invoke_constructor_this();
328         e.return_value();
329         e.end_method();
330     }
331     
332     private void emitNewInstanceCallback() {
333         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null, null);
334         switch (callbackTypes.length) {
335         case 0:
336             // TODO: make sure Callback is null
337
break;
338         case 1:
339             e.getfield(getThreadLocal(0));
340             e.load_arg(0);
341             e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
342             break;
343         default:
344             e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
345         }
346         emitCommonNewInstance(e);
347     }
348
349     private void emitNewInstanceMultiarg(Constructor[] constructors) {
350         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null, null);
351         e.load_arg(2);
352         e.invoke_static_this(SET_THREAD_CALLBACKS);
353         emitCommonMultiarg(constructors, e);
354     }
355
356     private void emitNewInstanceMultiargCopy(Constructor[] constructors) {
357         CodeEmitter e = begin_method(Constants.ACC_PUBLIC, COPY_MULTIARG_NEW_INSTANCE, null, null);
358         emitCopyCallbacks(e);
359         emitCommonMultiarg(constructors, e);
360     }
361
362     private void emitCommonMultiarg(Constructor[] constructors, final CodeEmitter e) {
363         e.new_instance_this();
364         e.dup();
365         e.load_arg(0);
366         EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
367             public void processCase(Object JavaDoc key, Label end) {
368                 Constructor constructor = (Constructor)key;
369                 Type types[] = TypeUtils.getTypes(constructor.getParameterTypes());
370                 for (int i = 0; i < types.length; i++) {
371                     e.load_arg(1);
372                     e.push(i);
373                     e.aaload();
374                     e.unbox(types[i]);
375                 }
376                 e.invoke_constructor_this(ReflectUtils.getSignature(constructor));
377                 e.goTo(end);
378             }
379             public void processDefault() {
380                 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
381             }
382         });
383         e.return_value();
384         e.end_method();
385     }
386
387     private void emitMethods(Map groups, final Map indexes, final Set forcePublic) throws Exception JavaDoc {
388         for (int i = 0; i < callbackTypes.length; i++) {
389             declare_field(Constants.ACC_PRIVATE, getCallbackField(i), Type.getType(callbackTypes[i]), null, null);
390             declare_field(Constants.PRIVATE_FINAL_STATIC, getThreadLocal(i), THREAD_LOCAL, null, null);
391         }
392
393         Set seenGen = new HashSet();
394         CodeEmitter e = begin_static();
395         for (int i = 0; i < callbackTypes.length; i++) {
396             e.new_instance(THREAD_LOCAL);
397             e.dup();
398             e.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
399             e.putfield(getThreadLocal(i));
400
401             CallbackGenerator gen = CallbackUtils.getGenerator(callbackTypes[i]);
402             if (!seenGen.contains(gen)) {
403                 seenGen.add(gen);
404                 final List fmethods = (List)groups.get(gen);
405                 CallbackGenerator.Context context = new CallbackGenerator.Context() {
406                     public Iterator getMethods() {
407                         return fmethods.iterator();
408                     }
409                     public int getIndex(Method method) {
410                         return ((Integer JavaDoc)indexes.get(method)).intValue();
411                     }
412                     public void emitCallback(CodeEmitter e, int index) {
413                         emitCurrentCallback(e, index);
414                     }
415                     public int getModifiers(Method method) {
416                         int modifiers = Constants.ACC_FINAL
417                             | (method.getModifiers()
418                                & ~Constants.ACC_ABSTRACT
419                                & ~Constants.ACC_NATIVE
420                                & ~Constants.ACC_SYNCHRONIZED);
421                         if (forcePublic.contains(MethodWrapper.create(method))) {
422                             modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
423                         }
424                         return modifiers;
425                     }
426                     // TODO: this is probably slow
427
public String JavaDoc getUniqueName(Method method) {
428                         return method.getName() + "_" + fmethods.indexOf(method);
429                     }
430                 };
431                 gen.generate(this, context);
432                 gen.generateStatic(e, context);
433             }
434         }
435         e.return_value();
436         e.end_method();
437     }
438
439     private void emitSetThreadCallbacks() {
440         CodeEmitter e = begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
441                                      SET_THREAD_CALLBACKS,
442                                      null,
443                                      null);
444         emitSetThreadCallbacks(e);
445         e.return_value();
446         e.end_method();
447     }
448
449     private void emitSetThreadCallbacks(CodeEmitter e) {
450         for (int i = 0; i < callbackTypes.length; i++) {
451             e.getfield(getThreadLocal(i));
452             e.load_arg(0);
453             e.aaload(i);
454             e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
455         }
456     }
457
458     private void emitCurrentCallback(CodeEmitter e, int index) {
459         e.load_this();
460         e.getfield(getCallbackField(index));
461         e.dup();
462         Label end = e.make_label();
463         e.ifnonnull(end);
464         e.load_this();
465         e.getfield(CONSTRUCTED_FIELD);
466         e.if_jump(e.NE, end);
467         e.pop();
468         e.getfield(getThreadLocal(index));
469         e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
470         e.checkcast(Type.getType(callbackTypes[index]));
471         e.mark(end);
472     }
473
474     private static String JavaDoc getCallbackField(int index) {
475         return "CGLIB$CALLBACK_" + index;
476     }
477
478     private static String JavaDoc getThreadLocal(int index) {
479         return "CGLIB$TL_CALLBACK_" + index;
480     }
481     
482     private static void removeFinal(List list) {
483         CollectionUtils.filter(list, new Predicate() {
484             public boolean evaluate(Object JavaDoc arg) {
485                 return !Modifier.isFinal(((Method)arg).getModifiers());
486             }
487         });
488     }
489 }
490
Popular Tags