KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > cglib > core > EmitUtils


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.core;
55
56 import java.lang.reflect.Constructor JavaDoc;
57 import java.lang.reflect.Member JavaDoc;
58 import java.lang.reflect.Method JavaDoc;
59 import java.math.BigDecimal JavaDoc;
60 import java.math.BigInteger JavaDoc;
61 import java.util.*;
62 import org.logicalcobwebs.asm.Label;
63 import org.logicalcobwebs.asm.Type;
64
65 public class EmitUtils {
66     private static final Signature CSTRUCT_NULL =
67       TypeUtils.parseConstructor("");
68     private static final Signature CSTRUCT_THROWABLE =
69       TypeUtils.parseConstructor("Throwable");
70
71     private static final Signature GET_NAME =
72       TypeUtils.parseSignature("String getName()");
73     private static final Signature HASH_CODE =
74       TypeUtils.parseSignature("int hashCode()");
75     private static final Signature EQUALS =
76       TypeUtils.parseSignature("boolean equals(Object)");
77     private static final Signature STRING_LENGTH =
78       TypeUtils.parseSignature("int length()");
79     private static final Signature STRING_CHAR_AT =
80       TypeUtils.parseSignature("char charAt(int)");
81     private static final Signature FOR_NAME =
82       TypeUtils.parseSignature("Class forName(String)");
83     private static final Signature DOUBLE_TO_LONG_BITS =
84       TypeUtils.parseSignature("long doubleToLongBits(double)");
85     private static final Signature FLOAT_TO_INT_BITS =
86       TypeUtils.parseSignature("int floatToIntBits(float)");
87     private static final Signature TO_STRING =
88       TypeUtils.parseSignature("String toString()");
89     private static final Signature APPEND_STRING =
90       TypeUtils.parseSignature("StringBuffer append(String)");
91     private static final Signature APPEND_INT =
92       TypeUtils.parseSignature("StringBuffer append(int)");
93     private static final Signature APPEND_DOUBLE =
94       TypeUtils.parseSignature("StringBuffer append(double)");
95     private static final Signature APPEND_FLOAT =
96       TypeUtils.parseSignature("StringBuffer append(float)");
97     private static final Signature APPEND_CHAR =
98       TypeUtils.parseSignature("StringBuffer append(char)");
99     private static final Signature APPEND_LONG =
100       TypeUtils.parseSignature("StringBuffer append(long)");
101     private static final Signature APPEND_BOOLEAN =
102       TypeUtils.parseSignature("StringBuffer append(boolean)");
103     private static final Signature LENGTH =
104       TypeUtils.parseSignature("int length()");
105     private static final Signature SET_LENGTH =
106       TypeUtils.parseSignature("void setLength(int)");
107     private static final Signature GET_DECLARED_METHOD =
108       TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])");
109
110     public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
111
112     private EmitUtils() {
113     }
114
115     public static void factory_method(ClassEmitter ce, Signature sig) {
116         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null, null);
117         e.new_instance_this();
118         e.dup();
119         e.load_args();
120         e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
121         e.return_value();
122         e.end_method();
123     }
124
125     public static void null_constructor(ClassEmitter ce) {
126         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null, null);
127         e.load_this();
128         e.super_invoke_constructor();
129         e.return_value();
130         e.end_method();
131     }
132     
133     /**
134      * Process an array on the stack. Assumes the top item on the stack
135      * is an array of the specified type. For each element in the array,
136      * puts the element on the stack and triggers the callback.
137      * @param type the type of the array (type.isArray() must be true)
138      * @param callback the callback triggered for each element
139      */

140     public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
141         Type componentType = TypeUtils.getComponentType(type);
142         Local array = e.make_local();
143         Local loopvar = e.make_local(Type.INT_TYPE);
144         Label loopbody = e.make_label();
145         Label checkloop = e.make_label();
146         e.store_local(array);
147         e.push(0);
148         e.store_local(loopvar);
149         e.goTo(checkloop);
150         
151         e.mark(loopbody);
152         e.load_local(array);
153         e.load_local(loopvar);
154         e.array_load(componentType);
155         callback.processElement(componentType);
156         e.iinc(loopvar, 1);
157         
158         e.mark(checkloop);
159         e.load_local(loopvar);
160         e.load_local(array);
161         e.arraylength();
162         e.if_icmp(e.LT, loopbody);
163     }
164     
165     /**
166      * Process two arrays on the stack in parallel. Assumes the top two items on the stack
167      * are arrays of the specified class. The arrays must be the same length. For each pair
168      * of elements in the arrays, puts the pair on the stack and triggers the callback.
169      * @param type the type of the arrays (type.isArray() must be true)
170      * @param callback the callback triggered for each pair of elements
171      */

172     public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) {
173         Type componentType = TypeUtils.getComponentType(type);
174         Local array1 = e.make_local();
175         Local array2 = e.make_local();
176         Local loopvar = e.make_local(Type.INT_TYPE);
177         Label loopbody = e.make_label();
178         Label checkloop = e.make_label();
179         e.store_local(array1);
180         e.store_local(array2);
181         e.push(0);
182         e.store_local(loopvar);
183         e.goTo(checkloop);
184         
185         e.mark(loopbody);
186         e.load_local(array1);
187         e.load_local(loopvar);
188         e.array_load(componentType);
189         e.load_local(array2);
190         e.load_local(loopvar);
191         e.array_load(componentType);
192         callback.processElement(componentType);
193         e.iinc(loopvar, 1);
194         
195         e.mark(checkloop);
196         e.load_local(loopvar);
197         e.load_local(array1);
198         e.arraylength();
199         e.if_icmp(e.LT, loopbody);
200     }
201     
202     public static void string_switch(CodeEmitter e, String JavaDoc[] strings, int switchStyle, ObjectSwitchCallback callback) {
203         try {
204             switch (switchStyle) {
205             case Constants.SWITCH_STYLE_TRIE:
206                 string_switch_trie(e, strings, callback);
207                 break;
208             case Constants.SWITCH_STYLE_HASH:
209                 string_switch_hash(e, strings, callback, false);
210                 break;
211             case Constants.SWITCH_STYLE_HASHONLY:
212                 string_switch_hash(e, strings, callback, true);
213                 break;
214             default:
215                 throw new IllegalArgumentException JavaDoc("unknown switch style " + switchStyle);
216             }
217         } catch (RuntimeException JavaDoc ex) {
218             throw ex;
219         } catch (Error JavaDoc ex) {
220             throw ex;
221         } catch (Exception JavaDoc ex) {
222             throw new CodeGenerationException(ex);
223         }
224     }
225
226     private static void string_switch_trie(final CodeEmitter e,
227                                            String JavaDoc[] strings,
228                                            final ObjectSwitchCallback callback) throws Exception JavaDoc {
229         final Label def = e.make_label();
230         final Label end = e.make_label();
231         final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
232             public Object JavaDoc transform(Object JavaDoc value) {
233                 return new Integer JavaDoc(((String JavaDoc)value).length());
234             }
235         });
236         e.dup();
237         e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH);
238         e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
239                 public void processCase(int key, Label ignore_end) throws Exception JavaDoc {
240                     List bucket = (List)buckets.get(new Integer JavaDoc(key));
241                     stringSwitchHelper(e, bucket, callback, def, end, 0);
242                 }
243                 public void processDefault() {
244                     e.goTo(def);
245                 }
246             });
247         e.mark(def);
248         e.pop();
249         callback.processDefault();
250         e.mark(end);
251     }
252
253     private static void stringSwitchHelper(final CodeEmitter e,
254                                            List strings,
255                                            final ObjectSwitchCallback callback,
256                                            final Label def,
257                                            final Label end,
258                                            final int index) throws Exception JavaDoc {
259         final int len = ((String JavaDoc)strings.get(0)).length();
260         final Map buckets = CollectionUtils.bucket(strings, new Transformer() {
261             public Object JavaDoc transform(Object JavaDoc value) {
262                 return new Integer JavaDoc(((String JavaDoc)value).charAt(index));
263             }
264         });
265         e.dup();
266         e.push(index);
267         e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT);
268         e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
269                 public void processCase(int key, Label ignore_end) throws Exception JavaDoc {
270                     List bucket = (List)buckets.get(new Integer JavaDoc(key));
271                     if (index + 1 == len) {
272                         e.pop();
273                         callback.processCase(bucket.get(0), end);
274                     } else {
275                         stringSwitchHelper(e, bucket, callback, def, end, index + 1);
276                     }
277                 }
278                 public void processDefault() {
279                     e.goTo(def);
280                 }
281             });
282     }
283
284     static int[] getSwitchKeys(Map buckets) {
285         int[] keys = new int[buckets.size()];
286         int index = 0;
287         for (Iterator it = buckets.keySet().iterator(); it.hasNext();) {
288             keys[index++] = ((Integer JavaDoc)it.next()).intValue();
289         }
290         Arrays.sort(keys);
291         return keys;
292     }
293
294     private static void string_switch_hash(final CodeEmitter e,
295                                            final String JavaDoc[] strings,
296                                            final ObjectSwitchCallback callback,
297                                            final boolean skipEquals) throws Exception JavaDoc {
298         final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
299             public Object JavaDoc transform(Object JavaDoc value) {
300                 return new Integer JavaDoc(value.hashCode());
301             }
302         });
303         final Label def = e.make_label();
304         final Label end = e.make_label();
305         e.dup();
306         e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
307         e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
308             public void processCase(int key, Label ignore_end) throws Exception JavaDoc {
309                 List bucket = (List)buckets.get(new Integer JavaDoc(key));
310                 Label next = null;
311                 if (skipEquals && bucket.size() == 1) {
312                     callback.processCase((String JavaDoc)bucket.get(0), end);
313                 } else {
314                     for (Iterator it = bucket.iterator(); it.hasNext();) {
315                         String JavaDoc string = (String JavaDoc)it.next();
316                         if (next != null) {
317                             e.mark(next);
318                         }
319                         if (it.hasNext()) {
320                             e.dup();
321                         }
322                         e.push(string);
323                         e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
324                         if (it.hasNext()) {
325                             e.if_jump(e.EQ, next = e.make_label());
326                             e.pop();
327                         } else {
328                             e.if_jump(e.EQ, def);
329                         }
330                         callback.processCase(string, end);
331                     }
332                 }
333             }
334             public void processDefault() {
335                 e.pop();
336             }
337         });
338         e.mark(def);
339         callback.processDefault();
340         e.mark(end);
341     }
342
343     public static void load_class_this(CodeEmitter e) {
344         load_class_helper(e, e.getClassEmitter().getClassType());
345     }
346     
347     public static void load_class(CodeEmitter e, Type type) {
348         if (TypeUtils.isPrimitive(type)) {
349             if (type == Type.VOID_TYPE) {
350                 throw new IllegalArgumentException JavaDoc("cannot load void type");
351             }
352             e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS);
353         } else {
354             load_class_helper(e, type);
355         }
356     }
357
358     private static void load_class_helper(CodeEmitter e, final Type type) {
359         if (e.isStaticHook()) {
360             // have to fall back on non-optimized load
361
e.push(TypeUtils.emulateClassGetName(type));
362             e.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
363         } else {
364             ClassEmitter ce = e.getClassEmitter();
365             String JavaDoc typeName = TypeUtils.emulateClassGetName(type);
366             String JavaDoc fieldName = getFieldName(typeName);
367             if (!ce.isFieldDeclared(fieldName)) {
368                 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null, null);
369                 CodeEmitter hook = ce.getStaticHook();
370                 hook.push(typeName);
371                 hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
372                 hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS);
373             }
374             e.getfield(fieldName);
375         }
376     }
377
378     private static String JavaDoc getFieldName(String JavaDoc typeName) {
379         return "CGLIB$load_class$" + escapeType(typeName);
380     }
381
382     private static String JavaDoc escapeType(String JavaDoc s) {
383         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
384         for (int i = 0, len = s.length(); i < len; i++) {
385             char c = s.charAt(i);
386             switch (c) {
387             case '$': sb.append("$24"); break;
388             case '.': sb.append("$2E"); break;
389             case '[': sb.append("$5B"); break;
390             case ';': sb.append("$3B"); break;
391             default:
392                 sb.append(c);
393             }
394         }
395         return sb.toString();
396     }
397
398     public static void push_array(CodeEmitter e, Object JavaDoc[] array) {
399         e.push(array.length);
400         e.newarray(Type.getType(array.getClass().getComponentType()));
401         for (int i = 0; i < array.length; i++) {
402             e.dup();
403             e.push(i);
404             push_object(e, array[i]);
405             e.aastore();
406         }
407     }
408     
409     public static void push_object(CodeEmitter e, Object JavaDoc obj) {
410         if (obj == null) {
411             e.aconst_null();
412         } else {
413             Class JavaDoc type = obj.getClass();
414             if (type.isArray()) {
415                 push_array(e, (Object JavaDoc[])obj);
416             } else if (obj instanceof String JavaDoc) {
417                 e.push((String JavaDoc)obj);
418             } else if (obj instanceof Class JavaDoc) {
419                 load_class(e, Type.getType((Class JavaDoc)obj));
420             } else if (obj instanceof BigInteger JavaDoc) {
421                 e.new_instance(Constants.TYPE_BIG_INTEGER);
422                 e.dup();
423                 e.push(obj.toString());
424                 e.invoke_constructor(Constants.TYPE_BIG_INTEGER);
425             } else if (obj instanceof BigDecimal JavaDoc) {
426                 e.new_instance(Constants.TYPE_BIG_DECIMAL);
427                 e.dup();
428                 e.push(obj.toString());
429                 e.invoke_constructor(Constants.TYPE_BIG_DECIMAL);
430             } else {
431                 throw new IllegalArgumentException JavaDoc("unknown type: " + obj.getClass());
432             }
433         }
434     }
435
436     public static void hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer) {
437         if (TypeUtils.isArray(type)) {
438             hash_array(e, type, multiplier, customizer);
439         } else {
440             e.swap(Type.INT_TYPE, type);
441             e.push(multiplier);
442             e.math(e.MUL, Type.INT_TYPE);
443             e.swap(type, Type.INT_TYPE);
444             if (TypeUtils.isPrimitive(type)) {
445                 hash_primitive(e, type);
446             } else {
447                 hash_object(e, type, customizer);
448             }
449             e.math(e.ADD, Type.INT_TYPE);
450         }
451     }
452
453     private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer) {
454         Label skip = e.make_label();
455         Label end = e.make_label();
456         e.dup();
457         e.ifnull(skip);
458         EmitUtils.process_array(e, type, new ProcessArrayCallback() {
459             public void processElement(Type type) {
460                 hash_code(e, type, multiplier, customizer);
461             }
462         });
463         e.goTo(end);
464         e.mark(skip);
465         e.pop();
466         e.mark(end);
467     }
468
469     private static void hash_object(CodeEmitter e, Type type, Customizer customizer) {
470         // (f == null) ? 0 : f.hashCode();
471
Label skip = e.make_label();
472         Label end = e.make_label();
473         e.dup();
474         e.ifnull(skip);
475         if (customizer != null) {
476             customizer.customize(e, type);
477         }
478         e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
479         e.goTo(end);
480         e.mark(skip);
481         e.pop();
482         e.push(0);
483         e.mark(end);
484     }
485
486     private static void hash_primitive(CodeEmitter e, Type type) {
487         switch (type.getSort()) {
488         case Type.BOOLEAN:
489             // f ? 0 : 1
490
e.push(1);
491             e.math(e.XOR, Type.INT_TYPE);
492             break;
493         case Type.FLOAT:
494             // Float.floatToIntBits(f)
495
e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS);
496             break;
497         case Type.DOUBLE:
498             // Double.doubleToLongBits(f), hash_code(Long.TYPE)
499
e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS);
500             // fall through
501
case Type.LONG:
502             hash_long(e);
503         }
504     }
505
506     private static void hash_long(CodeEmitter e) {
507         // (int)(f ^ (f >>> 32))
508
e.dup2();
509         e.push(32);
510         e.math(e.USHR, Type.LONG_TYPE);
511         e.math(e.XOR, Type.LONG_TYPE);
512         e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE);
513     }
514
515 // public static void not_equals(CodeEmitter e, Type type, Label notEquals) {
516
// not_equals(e, type, notEquals, null);
517
// }
518

519     /**
520      * Branches to the specified label if the top two items on the stack
521      * are not equal. The items must both be of the specified
522      * class. Equality is determined by comparing primitive values
523      * directly and by invoking the <code>equals</code> method for
524      * Objects. Arrays are recursively processed in the same manner.
525      */

526     public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) {
527         (new ProcessArrayCallback() {
528             public void processElement(Type type) {
529                 not_equals_helper(e, type, notEquals, customizer, this);
530             }
531         }).processElement(type);
532     }
533     
534     private static void not_equals_helper(CodeEmitter e,
535                                           Type type,
536                                           Label notEquals,
537                                           Customizer customizer,
538                                           ProcessArrayCallback callback) {
539         if (TypeUtils.isPrimitive(type)) {
540             e.if_cmp(type, e.NE, notEquals);
541         } else {
542             Label end = e.make_label();
543             nullcmp(e, notEquals, end);
544             if (TypeUtils.isArray(type)) {
545                 Label checkContents = e.make_label();
546                 e.dup2();
547                 e.arraylength();
548                 e.swap();
549                 e.arraylength();
550                 e.if_icmp(e.EQ, checkContents);
551                 e.pop2();
552                 e.goTo(notEquals);
553                 e.mark(checkContents);
554                 EmitUtils.process_arrays(e, type, callback);
555             } else {
556                 if (customizer != null) {
557                     customizer.customize(e, type);
558                     e.swap();
559                     customizer.customize(e, type);
560                 }
561                 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
562                 e.if_jump(e.EQ, notEquals);
563             }
564             e.mark(end);
565         }
566     }
567
568     /**
569      * If both objects on the top of the stack are non-null, does nothing.
570      * If one is null, or both are null, both are popped off and execution
571      * branches to the respective label.
572      * @param oneNull label to branch to if only one of the objects is null
573      * @param bothNull label to branch to if both of the objects are null
574      */

575     private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) {
576         e.dup2();
577         Label nonNull = e.make_label();
578         Label oneNullHelper = e.make_label();
579         Label end = e.make_label();
580         e.ifnonnull(nonNull);
581         e.ifnonnull(oneNullHelper);
582         e.pop2();
583         e.goTo(bothNull);
584         
585         e.mark(nonNull);
586         e.ifnull(oneNullHelper);
587         e.goTo(end);
588         
589         e.mark(oneNullHelper);
590         e.pop2();
591         e.goTo(oneNull);
592         
593         e.mark(end);
594     }
595
596     /*
597     public static void to_string(CodeEmitter e,
598                                  Type type,
599                                  ArrayDelimiters delims,
600                                  Customizer customizer) {
601         e.new_instance(Constants.TYPE_STRING_BUFFER);
602         e.dup();
603         e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
604         e.swap();
605         append_string(e, type, delims, customizer);
606         e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
607     }
608     */

609
610     public static void append_string(final CodeEmitter e,
611                                      Type type,
612                                      final ArrayDelimiters delims,
613                                      final Customizer customizer) {
614         final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS;
615         ProcessArrayCallback callback = new ProcessArrayCallback() {
616             public void processElement(Type type) {
617                 append_string_helper(e, type, d, customizer, this);
618                 e.push(d.inside);
619                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
620             }
621         };
622         append_string_helper(e, type, d, customizer, callback);
623     }
624
625     private static void append_string_helper(CodeEmitter e,
626                                              Type type,
627                                              ArrayDelimiters delims,
628                                              Customizer customizer,
629                                              ProcessArrayCallback callback) {
630         Label skip = e.make_label();
631         Label end = e.make_label();
632         if (TypeUtils.isPrimitive(type)) {
633             switch (type.getSort()) {
634             case Type.INT:
635             case Type.SHORT:
636             case Type.BYTE:
637                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT);
638                 break;
639             case Type.DOUBLE:
640                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE);
641                 break;
642             case Type.FLOAT:
643                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT);
644                 break;
645             case Type.LONG:
646                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG);
647                 break;
648             case Type.BOOLEAN:
649                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN);
650                 break;
651             case Type.CHAR:
652                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR);
653                 break;
654             }
655         } else if (TypeUtils.isArray(type)) {
656             e.dup();
657             e.ifnull(skip);
658             e.swap();
659             if (delims != null && delims.before != null && !"".equals(delims.before)) {
660                 e.push(delims.before);
661                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
662                 e.swap();
663             }
664             EmitUtils.process_array(e, type, callback);
665             shrinkStringBuffer(e, 2);
666             if (delims != null && delims.after != null && !"".equals(delims.after)) {
667                 e.push(delims.after);
668                 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
669             }
670         } else {
671             e.dup();
672             e.ifnull(skip);
673             if (customizer != null) {
674                 customizer.customize(e, type);
675             }
676             e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
677             e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
678         }
679         e.goTo(end);
680         e.mark(skip);
681         e.pop();
682         e.push("null");
683         e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
684         e.mark(end);
685     }
686
687     private static void shrinkStringBuffer(CodeEmitter e, int amt) {
688         e.dup();
689         e.dup();
690         e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH);
691         e.push(amt);
692         e.math(e.SUB, Type.INT_TYPE);
693         e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH);
694     }
695
696     public static class ArrayDelimiters {
697         private String JavaDoc before;
698         private String JavaDoc inside;
699         private String JavaDoc after;
700             
701         public ArrayDelimiters(String JavaDoc before, String JavaDoc inside, String JavaDoc after) {
702             this.before = before;
703             this.inside = inside;
704             this.after = after;
705         }
706     }
707
708     public static void load_method(CodeEmitter e, Method JavaDoc method) {
709         load_class(e, Type.getType(method.getDeclaringClass()));
710         e.push(method.getName());
711         push_object(e, method.getParameterTypes());
712         e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD);
713     }
714
715     private interface ParameterTyper {
716         Class JavaDoc[] getParameterTypes(Object JavaDoc member);
717     }
718
719     public static void method_switch(CodeEmitter e,
720                                      Method JavaDoc[] methods,
721                                      ObjectSwitchCallback callback) {
722         member_switch_helper(e, Arrays.asList(methods), callback, true, new ParameterTyper() {
723             public Class JavaDoc[] getParameterTypes(Object JavaDoc member) {
724                 return ((Method JavaDoc)member).getParameterTypes();
725             }
726         });
727     }
728
729     public static void constructor_switch(CodeEmitter e,
730                                           Constructor JavaDoc[] cstructs,
731                                           ObjectSwitchCallback callback) {
732         member_switch_helper(e, Arrays.asList(cstructs), callback, false, new ParameterTyper() {
733             public Class JavaDoc[] getParameterTypes(Object JavaDoc member) {
734                 return ((Constructor JavaDoc)member).getParameterTypes();
735             }
736         });
737     }
738
739     private static void member_switch_helper(final CodeEmitter e,
740                                              List members,
741                                              final ObjectSwitchCallback callback,
742                                              boolean useName,
743                                              final ParameterTyper typer) {
744         try {
745             final Map cache = new HashMap();
746             final ParameterTyper cached = new ParameterTyper() {
747                     public Class JavaDoc[] getParameterTypes(Object JavaDoc member) {
748                         Class JavaDoc[] types = (Class JavaDoc[])cache.get(member);
749                         if (types == null) {
750                             cache.put(member, types = typer.getParameterTypes(member));
751                         }
752                         return types;
753                     }
754                 };
755             final Label def = e.make_label();
756             final Label end = e.make_label();
757             if (useName) {
758                 e.swap();
759                 final Map buckets = CollectionUtils.bucket(members, new Transformer() {
760                         public Object JavaDoc transform(Object JavaDoc value) {
761                             return ((Member JavaDoc)value).getName();
762                         }
763                     });
764                 String JavaDoc[] names = (String JavaDoc[])buckets.keySet().toArray(new String JavaDoc[buckets.size()]);
765                 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
766                         public void processCase(Object JavaDoc key, Label dontUseEnd) throws Exception JavaDoc {
767                             member_helper_size(e, (List)buckets.get(key), callback, cached, def, end);
768                         }
769                         public void processDefault() throws Exception JavaDoc {
770                             e.goTo(def);
771                         }
772                     });
773             } else {
774                 member_helper_size(e, members, callback, cached, def, end);
775             }
776             e.mark(def);
777             e.pop();
778             callback.processDefault();
779             e.mark(end);
780         } catch (RuntimeException JavaDoc ex) {
781             throw ex;
782         } catch (Error JavaDoc ex) {
783             throw ex;
784         } catch (Exception JavaDoc ex) {
785             throw new CodeGenerationException(ex);
786         }
787     }
788
789     private static void member_helper_size(final CodeEmitter e,
790                                            List members,
791                                            final ObjectSwitchCallback callback,
792                                            final ParameterTyper typer,
793                                            final Label def,
794                                            final Label end) throws Exception JavaDoc {
795         final Map buckets = CollectionUtils.bucket(members, new Transformer() {
796             public Object JavaDoc transform(Object JavaDoc value) {
797                 return new Integer JavaDoc(typer.getParameterTypes(value).length);
798             }
799         });
800         e.dup();
801         e.arraylength();
802         e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() {
803             public void processCase(int key, Label dontUseEnd) throws Exception JavaDoc {
804                 List bucket = (List)buckets.get(new Integer JavaDoc(key));
805                 member_helper_type(e, bucket, callback, typer, def, end, new BitSet());
806             }
807             public void processDefault() throws Exception JavaDoc {
808                 e.goTo(def);
809             }
810         });
811     }
812
813     private static void member_helper_type(final CodeEmitter e,
814                                            List members,
815                                            final ObjectSwitchCallback callback,
816                                            final ParameterTyper typer,
817                                            final Label def,
818                                            final Label end,
819                                            final BitSet checked) throws Exception JavaDoc {
820         if (members.size() == 1) {
821             Member JavaDoc member = (Member JavaDoc)members.get(0);
822             Class JavaDoc[] types = typer.getParameterTypes(member);
823             // need to check classes that have not already been checked via switches
824
for (int i = 0; i < types.length; i++) {
825                 if (checked == null || !checked.get(i)) {
826                     e.dup();
827                     e.aaload(i);
828                     e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
829                     e.push(types[i].getName());
830                     e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
831                     e.if_jump(e.EQ, def);
832                 }
833             }
834             e.pop();
835             callback.processCase(member, end);
836         } else {
837             // choose the index that has the best chance of uniquely identifying member
838
Class JavaDoc[] example = typer.getParameterTypes(members.get(0));
839             Map buckets = null;
840             int index = -1;
841             for (int i = 0; i < example.length; i++) {
842                 final int j = i;
843                 Map test = CollectionUtils.bucket(members, new Transformer() {
844                     public Object JavaDoc transform(Object JavaDoc value) {
845                         return typer.getParameterTypes(value)[j].getName();
846                     }
847                 });
848                 if (buckets == null || test.size() > buckets.size()) {
849                     buckets = test;
850                     index = i;
851                 }
852             }
853             if (buckets == null) {
854                 // TODO: switch by returnType
855
// must have two methods with same name, types, and different return types
856
e.goTo(def);
857             } else {
858                 checked.set(index);
859
860                 e.dup();
861                 e.aaload(index);
862                 e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
863
864                 final Map fbuckets = buckets;
865                 String JavaDoc[] names = (String JavaDoc[])buckets.keySet().toArray(new String JavaDoc[buckets.size()]);
866                 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
867                     public void processCase(Object JavaDoc key, Label dontUseEnd) throws Exception JavaDoc {
868                         member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked);
869                     }
870                     public void processDefault() throws Exception JavaDoc {
871                         e.goTo(def);
872                     }
873                 });
874             }
875         }
876     }
877
878     public static void wrap_throwable(Block block, Type wrapper) {
879         CodeEmitter e = block.getCodeEmitter();
880         e.catch_exception(block, Constants.TYPE_THROWABLE);
881         e.new_instance(wrapper);
882         e.dup_x1();
883         e.swap();
884         e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
885         e.athrow();
886     }
887
888     public static void add_properties(ClassEmitter ce, String JavaDoc[] names, Type[] types) {
889         for (int i = 0; i < names.length; i++) {
890             String JavaDoc fieldName = "$cglib_prop_" + names[i];
891             ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null, null);
892             EmitUtils.add_property(ce, names[i], types[i], fieldName);
893         }
894     }
895
896     public static void add_property(ClassEmitter ce, String JavaDoc name, Type type, String JavaDoc fieldName) {
897         String JavaDoc property = TypeUtils.upperFirst(name);
898         CodeEmitter e;
899         e = ce.begin_method(Constants.ACC_PUBLIC,
900                             new Signature("get" + property,
901                                           type,
902                                           Constants.TYPES_EMPTY),
903                             null,
904                             null);
905         e.load_this();
906         e.getfield(fieldName);
907         e.return_value();
908         e.end_method();
909
910         e = ce.begin_method(Constants.ACC_PUBLIC,
911                             new Signature("set" + property,
912                                           Type.VOID_TYPE,
913                                           new Type[]{ type }),
914                             null,
915                             null);
916         e.load_this();
917         e.load_arg(0);
918         e.putfield(fieldName);
919         e.return_value();
920         e.end_method();
921     }
922 }
923
Popular Tags