View Javadoc
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