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 net.sf.cglib.core;
55
56 import java.lang.reflect.Constructor;
57 import java.lang.reflect.Member;
58 import java.lang.reflect.Method;
59 import java.math.BigDecimal;
60 import java.math.BigInteger;
61 import java.util.*;
62 import org.objectweb.asm.Label;
63 import org.objectweb.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
111
112 public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
113
114 private EmitUtils() {
115 }
116
117 public static void factory_method(ClassEmitter ce, Signature sig) {
118 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null, null);
119 e.new_instance_this();
120 e.dup();
121 e.load_args();
122 e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
123 e.return_value();
124 e.end_method();
125 }
126
127 public static void null_constructor(ClassEmitter ce) {
128 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null, null);
129 e.load_this();
130 e.super_invoke_constructor();
131 e.return_value();
132 e.end_method();
133 }
134
135 /***
136 * Process an array on the stack. Assumes the top item on the stack
137 * is an array of the specified type. For each element in the array,
138 * puts the element on the stack and triggers the callback.
139 * @param type the type of the array (type.isArray() must be true)
140 * @param callback the callback triggered for each element
141 */
142 public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
143 Type componentType = TypeUtils.getComponentType(type);
144 Local array = e.make_local();
145 Local loopvar = e.make_local(Type.INT_TYPE);
146 Label loopbody = e.make_label();
147 Label checkloop = e.make_label();
148 e.store_local(array);
149 e.push(0);
150 e.store_local(loopvar);
151 e.goTo(checkloop);
152
153 e.mark(loopbody);
154 e.load_local(array);
155 e.load_local(loopvar);
156 e.array_load(componentType);
157 callback.processElement(componentType);
158 e.iinc(loopvar, 1);
159
160 e.mark(checkloop);
161 e.load_local(loopvar);
162 e.load_local(array);
163 e.arraylength();
164 e.if_icmp(e.LT, loopbody);
165 }
166
167 /***
168 * Process two arrays on the stack in parallel. Assumes the top two items on the stack
169 * are arrays of the specified class. The arrays must be the same length. For each pair
170 * of elements in the arrays, puts the pair on the stack and triggers the callback.
171 * @param type the type of the arrays (type.isArray() must be true)
172 * @param callback the callback triggered for each pair of elements
173 */
174 public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) {
175 Type componentType = TypeUtils.getComponentType(type);
176 Local array1 = e.make_local();
177 Local array2 = e.make_local();
178 Local loopvar = e.make_local(Type.INT_TYPE);
179 Label loopbody = e.make_label();
180 Label checkloop = e.make_label();
181 e.store_local(array1);
182 e.store_local(array2);
183 e.push(0);
184 e.store_local(loopvar);
185 e.goTo(checkloop);
186
187 e.mark(loopbody);
188 e.load_local(array1);
189 e.load_local(loopvar);
190 e.array_load(componentType);
191 e.load_local(array2);
192 e.load_local(loopvar);
193 e.array_load(componentType);
194 callback.processElement(componentType);
195 e.iinc(loopvar, 1);
196
197 e.mark(checkloop);
198 e.load_local(loopvar);
199 e.load_local(array1);
200 e.arraylength();
201 e.if_icmp(e.LT, loopbody);
202 }
203
204 public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) {
205 try {
206 switch (switchStyle) {
207 case Constants.SWITCH_STYLE_TRIE:
208 string_switch_trie(e, strings, callback);
209 break;
210 case Constants.SWITCH_STYLE_HASH:
211 string_switch_hash(e, strings, callback, false);
212 break;
213 case Constants.SWITCH_STYLE_HASHONLY:
214 string_switch_hash(e, strings, callback, true);
215 break;
216 default:
217 throw new IllegalArgumentException("unknown switch style " + switchStyle);
218 }
219 } catch (RuntimeException ex) {
220 throw ex;
221 } catch (Error ex) {
222 throw ex;
223 } catch (Exception ex) {
224 throw new CodeGenerationException(ex);
225 }
226 }
227
228 private static void string_switch_trie(final CodeEmitter e,
229 String[] strings,
230 final ObjectSwitchCallback callback) throws Exception {
231 final Label def = e.make_label();
232 final Label end = e.make_label();
233 final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
234 public Object transform(Object value) {
235 return new Integer(((String)value).length());
236 }
237 });
238 e.dup();
239 e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH);
240 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
241 public void processCase(int key, Label ignore_end) throws Exception {
242 List bucket = (List)buckets.get(new Integer(key));
243 stringSwitchHelper(e, bucket, callback, def, end, 0);
244 }
245 public void processDefault() {
246 e.goTo(def);
247 }
248 });
249 e.mark(def);
250 e.pop();
251 callback.processDefault();
252 e.mark(end);
253 }
254
255 private static void stringSwitchHelper(final CodeEmitter e,
256 List strings,
257 final ObjectSwitchCallback callback,
258 final Label def,
259 final Label end,
260 final int index) throws Exception {
261 final int len = ((String)strings.get(0)).length();
262 final Map buckets = CollectionUtils.bucket(strings, new Transformer() {
263 public Object transform(Object value) {
264 return new Integer(((String)value).charAt(index));
265 }
266 });
267 e.dup();
268 e.push(index);
269 e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT);
270 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
271 public void processCase(int key, Label ignore_end) throws Exception {
272 List bucket = (List)buckets.get(new Integer(key));
273 if (index + 1 == len) {
274 e.pop();
275 callback.processCase(bucket.get(0), end);
276 } else {
277 stringSwitchHelper(e, bucket, callback, def, end, index + 1);
278 }
279 }
280 public void processDefault() {
281 e.goTo(def);
282 }
283 });
284 }
285
286 static int[] getSwitchKeys(Map buckets) {
287 int[] keys = new int[buckets.size()];
288 int index = 0;
289 for (Iterator it = buckets.keySet().iterator(); it.hasNext();) {
290 keys[index++] = ((Integer)it.next()).intValue();
291 }
292 Arrays.sort(keys);
293 return keys;
294 }
295
296 private static void string_switch_hash(final CodeEmitter e,
297 final String[] strings,
298 final ObjectSwitchCallback callback,
299 final boolean skipEquals) throws Exception {
300 final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
301 public Object transform(Object value) {
302 return new Integer(value.hashCode());
303 }
304 });
305 final Label def = e.make_label();
306 final Label end = e.make_label();
307 e.dup();
308 e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
309 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
310 public void processCase(int key, Label ignore_end) throws Exception {
311 List bucket = (List)buckets.get(new Integer(key));
312 Label next = null;
313 if (skipEquals && bucket.size() == 1) {
314 callback.processCase((String)bucket.get(0), end);
315 } else {
316 for (Iterator it = bucket.iterator(); it.hasNext();) {
317 String string = (String)it.next();
318 if (next != null) {
319 e.mark(next);
320 }
321 if (it.hasNext()) {
322 e.dup();
323 }
324 e.push(string);
325 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
326 if (it.hasNext()) {
327 e.if_jump(e.EQ, next = e.make_label());
328 e.pop();
329 } else {
330 e.if_jump(e.EQ, def);
331 }
332 callback.processCase(string, end);
333 }
334 }
335 }
336 public void processDefault() {
337 e.pop();
338 }
339 });
340 e.mark(def);
341 callback.processDefault();
342 e.mark(end);
343 }
344
345 public static void load_class_this(CodeEmitter e) {
346 load_class_helper(e, e.getClassEmitter().getClassType());
347 }
348
349 public static void load_class(CodeEmitter e, Type type) {
350 if (TypeUtils.isPrimitive(type)) {
351 if (type == Type.VOID_TYPE) {
352 throw new IllegalArgumentException("cannot load void type");
353 }
354 e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS);
355 } else {
356 load_class_helper(e, type);
357 }
358 }
359
360 private static void load_class_helper(CodeEmitter e, final Type type) {
361 if (e.isStaticHook()) {
362 // have to fall back on non-optimized load
363 e.push(TypeUtils.emulateClassGetName(type));
364 e.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
365 } else {
366 ClassEmitter ce = e.getClassEmitter();
367 String typeName = TypeUtils.emulateClassGetName(type);
368
369 // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow
370 String fieldName = "CGLIB$load_class$" + escapeType(typeName);
371 if (!ce.isFieldDeclared(fieldName)) {
372 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null, null);
373 CodeEmitter hook = ce.getStaticHook();
374 hook.push(typeName);
375 hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
376 hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS);
377 }
378 e.getfield(fieldName);
379 }
380 }
381
382 private static String escapeType(String s) {
383 StringBuffer sb = new StringBuffer();
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[] 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 obj) {
410 if (obj == null) {
411 e.aconst_null();
412 } else {
413 Class type = obj.getClass();
414 if (type.isArray()) {
415 push_array(e, (Object[])obj);
416 } else if (obj instanceof String) {
417 e.push((String)obj);
418 } else if (obj instanceof Class) {
419 load_class(e, Type.getType((Class)obj));
420 } else if (obj instanceof BigInteger) {
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) {
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("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 before;
698 private String inside;
699 private String after;
700
701 public ArrayDelimiters(String before, String inside, String 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 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[] getParameterTypes(Object member);
717 }
718
719 public static void method_switch(CodeEmitter e,
720 Method[] methods,
721 ObjectSwitchCallback callback) {
722 member_switch_helper(e, Arrays.asList(methods), callback, true, new ParameterTyper() {
723 public Class[] getParameterTypes(Object member) {
724 return ((Method)member).getParameterTypes();
725 }
726 });
727 }
728
729 public static void constructor_switch(CodeEmitter e,
730 Constructor[] cstructs,
731 ObjectSwitchCallback callback) {
732 member_switch_helper(e, Arrays.asList(cstructs), callback, false, new ParameterTyper() {
733 public Class[] getParameterTypes(Object member) {
734 return ((Constructor)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[] getParameterTypes(Object member) {
748 Class[] types = (Class[])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 transform(Object value) {
761 return ((Member)value).getName();
762 }
763 });
764 String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
765 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
766 public void processCase(Object key, Label dontUseEnd) throws Exception {
767 member_helper_size(e, (List)buckets.get(key), callback, cached, def, end);
768 }
769 public void processDefault() throws Exception {
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 ex) {
781 throw ex;
782 } catch (Error ex) {
783 throw ex;
784 } catch (Exception 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 {
795 final Map buckets = CollectionUtils.bucket(members, new Transformer() {
796 public Object transform(Object value) {
797 return new Integer(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 {
804 List bucket = (List)buckets.get(new Integer(key));
805 member_helper_type(e, bucket, callback, typer, def, end, new BitSet());
806 }
807 public void processDefault() throws Exception {
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 {
820 if (members.size() == 1) {
821 Member member = (Member)members.get(0);
822 Class[] 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[] 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 transform(Object 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[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
866 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
867 public void processCase(Object key, Label dontUseEnd) throws Exception {
868 member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked);
869 }
870 public void processDefault() throws Exception {
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[] names, Type[] types) {
889 for (int i = 0; i < names.length; i++) {
890 String 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 name, Type type, String fieldName) {
897 String 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 /* generates:
924 } catch (RuntimeException e) {
925 throw e;
926 } catch (Error e) {
927 throw e;
928 } catch (<DeclaredException> e) {
929 throw e;
930 } catch (Throwable e) {
931 throw new <Wrapper>(e);
932 }
933 */
934 public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) {
935 Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions));
936
937 if (set.contains(Constants.TYPE_THROWABLE))
938 return;
939
940 boolean needThrow = exceptions != null;
941 if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) {
942 e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION);
943 needThrow = true;
944 }
945 if (!set.contains(Constants.TYPE_ERROR)) {
946 e.catch_exception(handler, Constants.TYPE_ERROR);
947 needThrow = true;
948 }
949 if (exceptions != null) {
950 for (int i = 0; i < exceptions.length; i++) {
951 e.catch_exception(handler, exceptions[i]);
952 }
953 }
954 if (needThrow) {
955 e.athrow();
956 }
957 // e -> eo -> oeo -> ooe -> o
958 e.catch_exception(handler, Constants.TYPE_THROWABLE);
959 e.new_instance(wrapper);
960 e.dup_x1();
961 e.swap();
962 e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
963 e.athrow();
964 }
965 }
This page was automatically generated by Maven