KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > cglib > core > EmitUtils


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.core;
17
18 import java.math.BigDecimal JavaDoc;
19 import java.math.BigInteger JavaDoc;
20 import java.util.*;
21 import org.objectweb.asm.Label;
22 import org.objectweb.asm.Type;
23
24 public class EmitUtils {
25     private static final Signature CSTRUCT_NULL =
26       TypeUtils.parseConstructor("");
27     private static final Signature CSTRUCT_THROWABLE =
28       TypeUtils.parseConstructor("Throwable");
29
30     private static final Signature GET_NAME =
31       TypeUtils.parseSignature("String getName()");
32     private static final Signature HASH_CODE =
33       TypeUtils.parseSignature("int hashCode()");
34     private static final Signature EQUALS =
35       TypeUtils.parseSignature("boolean equals(Object)");
36     private static final Signature STRING_LENGTH =
37       TypeUtils.parseSignature("int length()");
38     private static final Signature STRING_CHAR_AT =
39       TypeUtils.parseSignature("char charAt(int)");
40     private static final Signature FOR_NAME =
41       TypeUtils.parseSignature("Class forName(String)");
42     private static final Signature DOUBLE_TO_LONG_BITS =
43       TypeUtils.parseSignature("long doubleToLongBits(double)");
44     private static final Signature FLOAT_TO_INT_BITS =
45       TypeUtils.parseSignature("int floatToIntBits(float)");
46     private static final Signature TO_STRING =
47       TypeUtils.parseSignature("String toString()");
48     private static final Signature APPEND_STRING =
49       TypeUtils.parseSignature("StringBuffer append(String)");
50     private static final Signature APPEND_INT =
51       TypeUtils.parseSignature("StringBuffer append(int)");
52     private static final Signature APPEND_DOUBLE =
53       TypeUtils.parseSignature("StringBuffer append(double)");
54     private static final Signature APPEND_FLOAT =
55       TypeUtils.parseSignature("StringBuffer append(float)");
56     private static final Signature APPEND_CHAR =
57       TypeUtils.parseSignature("StringBuffer append(char)");
58     private static final Signature APPEND_LONG =
59       TypeUtils.parseSignature("StringBuffer append(long)");
60     private static final Signature APPEND_BOOLEAN =
61       TypeUtils.parseSignature("StringBuffer append(boolean)");
62     private static final Signature LENGTH =
63       TypeUtils.parseSignature("int length()");
64     private static final Signature SET_LENGTH =
65       TypeUtils.parseSignature("void setLength(int)");
66     private static final Signature GET_DECLARED_METHOD =
67       TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])");
68      
69     
70
71     public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
72
73     private EmitUtils() {
74     }
75
76     public static void factory_method(ClassEmitter ce, Signature sig) {
77         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null);
78         e.new_instance_this();
79         e.dup();
80         e.load_args();
81         e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
82         e.return_value();
83         e.end_method();
84     }
85
86     public static void null_constructor(ClassEmitter ce) {
87         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null);
88         e.load_this();
89         e.super_invoke_constructor();
90         e.return_value();
91         e.end_method();
92     }
93     
94     /**
95      * Process an array on the stack. Assumes the top item on the stack
96      * is an array of the specified type. For each element in the array,
97      * puts the element on the stack and triggers the callback.
98      * @param type the type of the array (type.isArray() must be true)
99      * @param callback the callback triggered for each element
100      */

101     public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
102         Type componentType = TypeUtils.getComponentType(type);
103         Local array = e.make_local();
104         Local loopvar = e.make_local(Type.INT_TYPE);
105         Label loopbody = e.make_label();
106         Label checkloop = e.make_label();
107         e.store_local(array);
108         e.push(0);
109         e.store_local(loopvar);
110         e.goTo(checkloop);
111         
112         e.mark(loopbody);
113         e.load_local(array);
114         e.load_local(loopvar);
115         e.array_load(componentType);
116         callback.processElement(componentType);
117         e.iinc(loopvar, 1);
118         
119         e.mark(checkloop);
120         e.load_local(loopvar);
121         e.load_local(array);
122         e.arraylength();
123         e.if_icmp(e.LT, loopbody);
124     }
125     
126     /**
127      * Process two arrays on the stack in parallel. Assumes the top two items on the stack
128      * are arrays of the specified class. The arrays must be the same length. For each pair
129      * of elements in the arrays, puts the pair on the stack and triggers the callback.
130      * @param type the type of the arrays (type.isArray() must be true)
131      * @param callback the callback triggered for each pair of elements
132      */

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

472     /**
473      * Branches to the specified label if the top two items on the stack
474      * are not equal. The items must both be of the specified
475      * class. Equality is determined by comparing primitive values
476      * directly and by invoking the <code>equals</code> method for
477      * Objects. Arrays are recursively processed in the same manner.
478      */

479     public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) {
480         (new ProcessArrayCallback() {
481             public void processElement(Type type) {
482                 not_equals_helper(e, type, notEquals, customizer, this);
483             }
484         }).processElement(type);
485     }
486     
487     private static void not_equals_helper(CodeEmitter e,
488                                           Type type,
489                                           Label notEquals,
490                                           Customizer customizer,
491                                           ProcessArrayCallback callback) {
492         if (TypeUtils.isPrimitive(type)) {
493             e.if_cmp(type, e.NE, notEquals);
494         } else {
495             Label end = e.make_label();
496             nullcmp(e, notEquals, end);
497             if (TypeUtils.isArray(type)) {
498                 Label checkContents = e.make_label();
499                 e.dup2();
500                 e.arraylength();
501                 e.swap();
502                 e.arraylength();
503                 e.if_icmp(e.EQ, checkContents);
504                 e.pop2();
505                 e.goTo(notEquals);
506                 e.mark(checkContents);
507                 EmitUtils.process_arrays(e, type, callback);
508             } else {
509                 if (customizer != null) {
510                     customizer.customize(e, type);
511                     e.swap();
512                     customizer.customize(e, type);
513                 }
514                 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
515                 e.if_jump(e.EQ, notEquals);
516             }
517             e.mark(end);
518         }
519     }
520
521     /**
522      * If both objects on the top of the stack are non-null, does nothing.
523      * If one is null, or both are null, both are popped off and execution
524      * branches to the respective label.
525      * @param oneNull label to branch to if only one of the objects is null
526      * @param bothNull label to branch to if both of the objects are null
527      */

528     private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) {
529         e.dup2();
530         Label nonNull = e.make_label();
531         Label oneNullHelper = e.make_label();
532         Label end = e.make_label();
533         e.ifnonnull(nonNull);
534         e.ifnonnull(oneNullHelper);
535         e.pop2();
536         e.goTo(bothNull);
537         
538         e.mark(nonNull);
539         e.ifnull(oneNullHelper);
540         e.goTo(end);
541         
542         e.mark(oneNullHelper);
543         e.pop2();
544         e.goTo(oneNull);
545         
546         e.mark(end);
547     }
548
549     /*
550     public static void to_string(CodeEmitter e,
551                                  Type type,
552                                  ArrayDelimiters delims,
553                                  Customizer customizer) {
554         e.new_instance(Constants.TYPE_STRING_BUFFER);
555         e.dup();
556         e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
557         e.swap();
558         append_string(e, type, delims, customizer);
559         e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
560     }
561     */

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

876     public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) {
877         Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions));
878
879         if (set.contains(Constants.TYPE_THROWABLE))
880             return;
881
882         boolean needThrow = exceptions != null;
883         if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) {
884             e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION);
885             needThrow = true;
886         }
887         if (!set.contains(Constants.TYPE_ERROR)) {
888             e.catch_exception(handler, Constants.TYPE_ERROR);
889             needThrow = true;
890         }
891         if (exceptions != null) {
892             for (int i = 0; i < exceptions.length; i++) {
893                 e.catch_exception(handler, exceptions[i]);
894             }
895         }
896         if (needThrow) {
897             e.athrow();
898         }
899         // e -> eo -> oeo -> ooe -> o
900
e.catch_exception(handler, Constants.TYPE_THROWABLE);
901         e.new_instance(wrapper);
902         e.dup_x1();
903         e.swap();
904         e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
905         e.athrow();
906     }
907
908     public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) {
909         return begin_method(e, method, method.getModifiers());
910     }
911
912     public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) {
913         return e.begin_method(access,
914                               method.getSignature(),
915                               method.getExceptionTypes());
916     }
917 }
918
Popular Tags