EmitUtils.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
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 */ 16package org.mockito.cglib.core; 17 18import java.math.BigDecimal; 19import java.math.BigInteger; 20import java.util.*; 21 22import org.mockito.asm.Label; 23import org.mockito.asm.Type; 24 25public class EmitUtils { 26 private static final Signature CSTRUCT_NULL = 27 TypeUtils.parseConstructor(""); 28 private static final Signature CSTRUCT_THROWABLE = 29 TypeUtils.parseConstructor("Throwable"); 30 31 private static final Signature GET_NAME = 32 TypeUtils.parseSignature("String getName()"); 33 private static final Signature HASH_CODE = 34 TypeUtils.parseSignature("int hashCode()"); 35 private static final Signature EQUALS = 36 TypeUtils.parseSignature("boolean equals(Object)"); 37 private static final Signature STRING_LENGTH = 38 TypeUtils.parseSignature("int length()"); 39 private static final Signature STRING_CHAR_AT = 40 TypeUtils.parseSignature("char charAt(int)"); 41 private static final Signature FOR_NAME = 42 TypeUtils.parseSignature("Class forName(String)"); 43 private static final Signature DOUBLE_TO_LONG_BITS = 44 TypeUtils.parseSignature("long doubleToLongBits(double)"); 45 private static final Signature FLOAT_TO_INT_BITS = 46 TypeUtils.parseSignature("int floatToIntBits(float)"); 47 private static final Signature TO_STRING = 48 TypeUtils.parseSignature("String toString()"); 49 private static final Signature APPEND_STRING = 50 TypeUtils.parseSignature("StringBuffer append(String)"); 51 private static final Signature APPEND_INT = 52 TypeUtils.parseSignature("StringBuffer append(int)"); 53 private static final Signature APPEND_DOUBLE = 54 TypeUtils.parseSignature("StringBuffer append(double)"); 55 private static final Signature APPEND_FLOAT = 56 TypeUtils.parseSignature("StringBuffer append(float)"); 57 private static final Signature APPEND_CHAR = 58 TypeUtils.parseSignature("StringBuffer append(char)"); 59 private static final Signature APPEND_LONG = 60 TypeUtils.parseSignature("StringBuffer append(long)"); 61 private static final Signature APPEND_BOOLEAN = 62 TypeUtils.parseSignature("StringBuffer append(boolean)"); 63 private static final Signature LENGTH = 64 TypeUtils.parseSignature("int length()"); 65 private static final Signature SET_LENGTH = 66 TypeUtils.parseSignature("void setLength(int)"); 67 private static final Signature GET_DECLARED_METHOD = 68 TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])"); 69 70 71 72 public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}"); 73 74 private EmitUtils() { 75 } 76 77 public static void factory_method(ClassEmitter ce, Signature sig) { 78 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null); 79 e.new_instance_this(); 80 e.dup(); 81 e.load_args(); 82 e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes())); 83 e.return_value(); 84 e.end_method(); 85 } 86 87 public static void null_constructor(ClassEmitter ce) { 88 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null); 89 e.load_this(); 90 e.super_invoke_constructor(); 91 e.return_value(); 92 e.end_method(); 93 } 94 95 /** 96 * Process an array on the stack. Assumes the top item on the stack 97 * is an array of the specified type. For each element in the array, 98 * puts the element on the stack and triggers the callback. 99 * @param type the type of the array (type.isArray() must be true) 100 * @param callback the callback triggered for each element 101 */ 102 public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) { 103 Type componentType = TypeUtils.getComponentType(type); 104 Local array = e.make_local(); 105 Local loopvar = e.make_local(Type.INT_TYPE); 106 Label loopbody = e.make_label(); 107 Label checkloop = e.make_label(); 108 e.store_local(array); 109 e.push(0); 110 e.store_local(loopvar); 111 e.goTo(checkloop); 112 113 e.mark(loopbody); 114 e.load_local(array); 115 e.load_local(loopvar); 116 e.array_load(componentType); 117 callback.processElement(componentType); 118 e.iinc(loopvar, 1); 119 120 e.mark(checkloop); 121 e.load_local(loopvar); 122 e.load_local(array); 123 e.arraylength(); 124 e.if_icmp(e.LT, loopbody); 125 } 126 127 /** 128 * Process two arrays on the stack in parallel. Assumes the top two items on the stack 129 * are arrays of the specified class. The arrays must be the same length. For each pair 130 * of elements in the arrays, puts the pair on the stack and triggers the callback. 131 * @param type the type of the arrays (type.isArray() must be true) 132 * @param callback the callback triggered for each pair of elements 133 */ 134 public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) { 135 Type componentType = TypeUtils.getComponentType(type); 136 Local array1 = e.make_local(); 137 Local array2 = e.make_local(); 138 Local loopvar = e.make_local(Type.INT_TYPE); 139 Label loopbody = e.make_label(); 140 Label checkloop = e.make_label(); 141 e.store_local(array1); 142 e.store_local(array2); 143 e.push(0); 144 e.store_local(loopvar); 145 e.goTo(checkloop); 146 147 e.mark(loopbody); 148 e.load_local(array1); 149 e.load_local(loopvar); 150 e.array_load(componentType); 151 e.load_local(array2); 152 e.load_local(loopvar); 153 e.array_load(componentType); 154 callback.processElement(componentType); 155 e.iinc(loopvar, 1); 156 157 e.mark(checkloop); 158 e.load_local(loopvar); 159 e.load_local(array1); 160 e.arraylength(); 161 e.if_icmp(e.LT, loopbody); 162 } 163 164 public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) { 165 try { 166 switch (switchStyle) { 167 case Constants.SWITCH_STYLE_TRIE: 168 string_switch_trie(e, strings, callback); 169 break; 170 case Constants.SWITCH_STYLE_HASH: 171 string_switch_hash(e, strings, callback, false); 172 break; 173 case Constants.SWITCH_STYLE_HASHONLY: 174 string_switch_hash(e, strings, callback, true); 175 break; 176 default: 177 throw new IllegalArgumentException("unknown switch style " + switchStyle); 178 } 179 } catch (RuntimeException ex) { 180 throw ex; 181 } catch (Error ex) { 182 throw ex; 183 } catch (Exception ex) { 184 throw new CodeGenerationException(ex); 185 } 186 } 187 188 private static void string_switch_trie(final CodeEmitter e, 189 String[] strings, 190 final ObjectSwitchCallback callback) throws Exception { 191 final Label def = e.make_label(); 192 final Label end = e.make_label(); 193 final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { 194 public Object transform(Object value) { 195 return new Integer(((String)value).length()); 196 } 197 }); 198 e.dup(); 199 e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH); 200 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { 201 public void processCase(int key, Label ignore_end) throws Exception { 202 List bucket = (List)buckets.get(new Integer(key)); 203 stringSwitchHelper(e, bucket, callback, def, end, 0); 204 } 205 public void processDefault() { 206 e.goTo(def); 207 } 208 }); 209 e.mark(def); 210 e.pop(); 211 callback.processDefault(); 212 e.mark(end); 213 } 214 215 private static void stringSwitchHelper(final CodeEmitter e, 216 List strings, 217 final ObjectSwitchCallback callback, 218 final Label def, 219 final Label end, 220 final int index) throws Exception { 221 final int len = ((String)strings.get(0)).length(); 222 final Map buckets = CollectionUtils.bucket(strings, new Transformer() { 223 public Object transform(Object value) { 224 return new Integer(((String)value).charAt(index)); 225 } 226 }); 227 e.dup(); 228 e.push(index); 229 e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT); 230 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { 231 public void processCase(int key, Label ignore_end) throws Exception { 232 List bucket = (List)buckets.get(new Integer(key)); 233 if (index + 1 == len) { 234 e.pop(); 235 callback.processCase(bucket.get(0), end); 236 } else { 237 stringSwitchHelper(e, bucket, callback, def, end, index + 1); 238 } 239 } 240 public void processDefault() { 241 e.goTo(def); 242 } 243 }); 244 } 245 246 static int[] getSwitchKeys(Map buckets) { 247 int[] keys = new int[buckets.size()]; 248 int index = 0; 249 for (Iterator it = buckets.keySet().iterator(); it.hasNext();) { 250 keys[index++] = ((Integer)it.next()).intValue(); 251 } 252 Arrays.sort(keys); 253 return keys; 254 } 255 256 private static void string_switch_hash(final CodeEmitter e, 257 final String[] strings, 258 final ObjectSwitchCallback callback, 259 final boolean skipEquals) throws Exception { 260 final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { 261 public Object transform(Object value) { 262 return new Integer(value.hashCode()); 263 } 264 }); 265 final Label def = e.make_label(); 266 final Label end = e.make_label(); 267 e.dup(); 268 e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); 269 e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { 270 public void processCase(int key, Label ignore_end) throws Exception { 271 List bucket = (List)buckets.get(new Integer(key)); 272 Label next = null; 273 if (skipEquals && bucket.size() == 1) { 274 if (skipEquals) 275 e.pop(); 276 callback.processCase((String)bucket.get(0), end); 277 } else { 278 for (Iterator it = bucket.iterator(); it.hasNext();) { 279 String string = (String)it.next(); 280 if (next != null) { 281 e.mark(next); 282 } 283 if (it.hasNext()) { 284 e.dup(); 285 } 286 e.push(string); 287 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); 288 if (it.hasNext()) { 289 e.if_jump(e.EQ, next = e.make_label()); 290 e.pop(); 291 } else { 292 e.if_jump(e.EQ, def); 293 } 294 callback.processCase(string, end); 295 } 296 } 297 } 298 public void processDefault() { 299 e.pop(); 300 } 301 }); 302 e.mark(def); 303 callback.processDefault(); 304 e.mark(end); 305 } 306 307 public static void load_class_this(CodeEmitter e) { 308 load_class_helper(e, e.getClassEmitter().getClassType()); 309 } 310 311 public static void load_class(CodeEmitter e, Type type) { 312 if (TypeUtils.isPrimitive(type)) { 313 if (type == Type.VOID_TYPE) { 314 throw new IllegalArgumentException("cannot load void type"); 315 } 316 e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS); 317 } else { 318 load_class_helper(e, type); 319 } 320 } 321 322 private static void load_class_helper(CodeEmitter e, final Type type) { 323 if (e.isStaticHook()) { 324 // have to fall back on non-optimized load 325 e.push(TypeUtils.emulateClassGetName(type)); 326 e.invoke_static(Constants.TYPE_CLASS, FOR_NAME); 327 } else { 328 ClassEmitter ce = e.getClassEmitter(); 329 String typeName = TypeUtils.emulateClassGetName(type); 330 331 // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow 332 String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName); 333 if (!ce.isFieldDeclared(fieldName)) { 334 ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null); 335 CodeEmitter hook = ce.getStaticHook(); 336 hook.push(typeName); 337 hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME); 338 hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS); 339 } 340 e.getfield(fieldName); 341 } 342 } 343 344 public static void push_array(CodeEmitter e, Object[] array) { 345 e.push(array.length); 346 e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType()))); 347 for (int i = 0; i < array.length; i++) { 348 e.dup(); 349 e.push(i); 350 push_object(e, array[i]); 351 e.aastore(); 352 } 353 } 354 355 private static Class remapComponentType(Class componentType) { 356 if (componentType.equals(Type.class)) 357 return Class.class; 358 return componentType; 359 } 360 361 public static void push_object(CodeEmitter e, Object obj) { 362 if (obj == null) { 363 e.aconst_null(); 364 } else { 365 Class type = obj.getClass(); 366 if (type.isArray()) { 367 push_array(e, (Object[])obj); 368 } else if (obj instanceof String) { 369 e.push((String)obj); 370 } else if (obj instanceof Type) { 371 load_class(e, (Type)obj); 372 } else if (obj instanceof Class) { 373 load_class(e, Type.getType((Class)obj)); 374 } else if (obj instanceof BigInteger) { 375 e.new_instance(Constants.TYPE_BIG_INTEGER); 376 e.dup(); 377 e.push(obj.toString()); 378 e.invoke_constructor(Constants.TYPE_BIG_INTEGER); 379 } else if (obj instanceof BigDecimal) { 380 e.new_instance(Constants.TYPE_BIG_DECIMAL); 381 e.dup(); 382 e.push(obj.toString()); 383 e.invoke_constructor(Constants.TYPE_BIG_DECIMAL); 384 } else { 385 throw new IllegalArgumentException("unknown type: " + obj.getClass()); 386 } 387 } 388 } 389 390 public static void hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer) { 391 if (TypeUtils.isArray(type)) { 392 hash_array(e, type, multiplier, customizer); 393 } else { 394 e.swap(Type.INT_TYPE, type); 395 e.push(multiplier); 396 e.math(e.MUL, Type.INT_TYPE); 397 e.swap(type, Type.INT_TYPE); 398 if (TypeUtils.isPrimitive(type)) { 399 hash_primitive(e, type); 400 } else { 401 hash_object(e, type, customizer); 402 } 403 e.math(e.ADD, Type.INT_TYPE); 404 } 405 } 406 407 private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer) { 408 Label skip = e.make_label(); 409 Label end = e.make_label(); 410 e.dup(); 411 e.ifnull(skip); 412 EmitUtils.process_array(e, type, new ProcessArrayCallback() { 413 public void processElement(Type type) { 414 hash_code(e, type, multiplier, customizer); 415 } 416 }); 417 e.goTo(end); 418 e.mark(skip); 419 e.pop(); 420 e.mark(end); 421 } 422 423 private static void hash_object(CodeEmitter e, Type type, Customizer customizer) { 424 // (f == null) ? 0 : f.hashCode(); 425 Label skip = e.make_label(); 426 Label end = e.make_label(); 427 e.dup(); 428 e.ifnull(skip); 429 if (customizer != null) { 430 customizer.customize(e, type); 431 } 432 e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); 433 e.goTo(end); 434 e.mark(skip); 435 e.pop(); 436 e.push(0); 437 e.mark(end); 438 } 439 440 private static void hash_primitive(CodeEmitter e, Type type) { 441 switch (type.getSort()) { 442 case Type.BOOLEAN: 443 // f ? 0 : 1 444 e.push(1); 445 e.math(e.XOR, Type.INT_TYPE); 446 break; 447 case Type.FLOAT: 448 // Float.floatToIntBits(f) 449 e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS); 450 break; 451 case Type.DOUBLE: 452 // Double.doubleToLongBits(f), hash_code(Long.TYPE) 453 e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS); 454 // fall through 455 case Type.LONG: 456 hash_long(e); 457 } 458 } 459 460 private static void hash_long(CodeEmitter e) { 461 // (int)(f ^ (f >>> 32)) 462 e.dup2(); 463 e.push(32); 464 e.math(e.USHR, Type.LONG_TYPE); 465 e.math(e.XOR, Type.LONG_TYPE); 466 e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE); 467 } 468 469// public static void not_equals(CodeEmitter e, Type type, Label notEquals) { 470// not_equals(e, type, notEquals, null); 471// } 472 473 /** 474 * Branches to the specified label if the top two items on the stack 475 * are not equal. The items must both be of the specified 476 * class. Equality is determined by comparing primitive values 477 * directly and by invoking the <code>equals</code> method for 478 * Objects. Arrays are recursively processed in the same manner. 479 */ 480 public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) { 481 (new ProcessArrayCallback() { 482 public void processElement(Type type) { 483 not_equals_helper(e, type, notEquals, customizer, this); 484 } 485 }).processElement(type); 486 } 487 488 private static void not_equals_helper(CodeEmitter e, 489 Type type, 490 Label notEquals, 491 Customizer customizer, 492 ProcessArrayCallback callback) { 493 if (TypeUtils.isPrimitive(type)) { 494 e.if_cmp(type, e.NE, notEquals); 495 } else { 496 Label end = e.make_label(); 497 nullcmp(e, notEquals, end); 498 if (TypeUtils.isArray(type)) { 499 Label checkContents = e.make_label(); 500 e.dup2(); 501 e.arraylength(); 502 e.swap(); 503 e.arraylength(); 504 e.if_icmp(e.EQ, checkContents); 505 e.pop2(); 506 e.goTo(notEquals); 507 e.mark(checkContents); 508 EmitUtils.process_arrays(e, type, callback); 509 } else { 510 if (customizer != null) { 511 customizer.customize(e, type); 512 e.swap(); 513 customizer.customize(e, type); 514 } 515 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); 516 e.if_jump(e.EQ, notEquals); 517 } 518 e.mark(end); 519 } 520 } 521 522 /** 523 * If both objects on the top of the stack are non-null, does nothing. 524 * If one is null, or both are null, both are popped off and execution 525 * branches to the respective label. 526 * @param oneNull label to branch to if only one of the objects is null 527 * @param bothNull label to branch to if both of the objects are null 528 */ 529 private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) { 530 e.dup2(); 531 Label nonNull = e.make_label(); 532 Label oneNullHelper = e.make_label(); 533 Label end = e.make_label(); 534 e.ifnonnull(nonNull); 535 e.ifnonnull(oneNullHelper); 536 e.pop2(); 537 e.goTo(bothNull); 538 539 e.mark(nonNull); 540 e.ifnull(oneNullHelper); 541 e.goTo(end); 542 543 e.mark(oneNullHelper); 544 e.pop2(); 545 e.goTo(oneNull); 546 547 e.mark(end); 548 } 549 550 /* 551 public static void to_string(CodeEmitter e, 552 Type type, 553 ArrayDelimiters delims, 554 Customizer customizer) { 555 e.new_instance(Constants.TYPE_STRING_BUFFER); 556 e.dup(); 557 e.invoke_constructor(Constants.TYPE_STRING_BUFFER); 558 e.swap(); 559 append_string(e, type, delims, customizer); 560 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING); 561 } 562 */ 563 564 public static void append_string(final CodeEmitter e, 565 Type type, 566 final ArrayDelimiters delims, 567 final Customizer customizer) { 568 final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS; 569 ProcessArrayCallback callback = new ProcessArrayCallback() { 570 public void processElement(Type type) { 571 append_string_helper(e, type, d, customizer, this); 572 e.push(d.inside); 573 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); 574 } 575 }; 576 append_string_helper(e, type, d, customizer, callback); 577 } 578 579 private static void append_string_helper(CodeEmitter e, 580 Type type, 581 ArrayDelimiters delims, 582 Customizer customizer, 583 ProcessArrayCallback callback) { 584 Label skip = e.make_label(); 585 Label end = e.make_label(); 586 if (TypeUtils.isPrimitive(type)) { 587 switch (type.getSort()) { 588 case Type.INT: 589 case Type.SHORT: 590 case Type.BYTE: 591 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT); 592 break; 593 case Type.DOUBLE: 594 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE); 595 break; 596 case Type.FLOAT: 597 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT); 598 break; 599 case Type.LONG: 600 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG); 601 break; 602 case Type.BOOLEAN: 603 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN); 604 break; 605 case Type.CHAR: 606 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR); 607 break; 608 } 609 } else if (TypeUtils.isArray(type)) { 610 e.dup(); 611 e.ifnull(skip); 612 e.swap(); 613 if (delims != null && delims.before != null && !"".equals(delims.before)) { 614 e.push(delims.before); 615 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); 616 e.swap(); 617 } 618 EmitUtils.process_array(e, type, callback); 619 shrinkStringBuffer(e, 2); 620 if (delims != null && delims.after != null && !"".equals(delims.after)) { 621 e.push(delims.after); 622 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); 623 } 624 } else { 625 e.dup(); 626 e.ifnull(skip); 627 if (customizer != null) { 628 customizer.customize(e, type); 629 } 630 e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); 631 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); 632 } 633 e.goTo(end); 634 e.mark(skip); 635 e.pop(); 636 e.push("null"); 637 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); 638 e.mark(end); 639 } 640 641 private static void shrinkStringBuffer(CodeEmitter e, int amt) { 642 e.dup(); 643 e.dup(); 644 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH); 645 e.push(amt); 646 e.math(e.SUB, Type.INT_TYPE); 647 e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH); 648 } 649 650 public static class ArrayDelimiters { 651 private String before; 652 private String inside; 653 private String after; 654 655 public ArrayDelimiters(String before, String inside, String after) { 656 this.before = before; 657 this.inside = inside; 658 this.after = after; 659 } 660 } 661 662 public static void load_method(CodeEmitter e, MethodInfo method) { 663 load_class(e, method.getClassInfo().getType()); 664 e.push(method.getSignature().getName()); 665 push_object(e, method.getSignature().getArgumentTypes()); 666 e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD); 667 } 668 669 private interface ParameterTyper { 670 Type[] getParameterTypes(MethodInfo member); 671 } 672 673 public static void method_switch(CodeEmitter e, 674 List methods, 675 ObjectSwitchCallback callback) { 676 member_switch_helper(e, methods, callback, true); 677 } 678 679 public static void constructor_switch(CodeEmitter e, 680 List constructors, 681 ObjectSwitchCallback callback) { 682 member_switch_helper(e, constructors, callback, false); 683 } 684 685 private static void member_switch_helper(final CodeEmitter e, 686 List members, 687 final ObjectSwitchCallback callback, 688 boolean useName) { 689 try { 690 final Map cache = new HashMap(); 691 final ParameterTyper cached = new ParameterTyper() { 692 public Type[] getParameterTypes(MethodInfo member) { 693 Type[] types = (Type[])cache.get(member); 694 if (types == null) { 695 cache.put(member, types = member.getSignature().getArgumentTypes()); 696 } 697 return types; 698 } 699 }; 700 final Label def = e.make_label(); 701 final Label end = e.make_label(); 702 if (useName) { 703 e.swap(); 704 final Map buckets = CollectionUtils.bucket(members, new Transformer() { 705 public Object transform(Object value) { 706 return ((MethodInfo)value).getSignature().getName(); 707 } 708 }); 709 String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); 710 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 711 public void processCase(Object key, Label dontUseEnd) throws Exception { 712 member_helper_size(e, (List)buckets.get(key), callback, cached, def, end); 713 } 714 public void processDefault() throws Exception { 715 e.goTo(def); 716 } 717 }); 718 } else { 719 member_helper_size(e, members, callback, cached, def, end); 720 } 721 e.mark(def); 722 e.pop(); 723 callback.processDefault(); 724 e.mark(end); 725 } catch (RuntimeException ex) { 726 throw ex; 727 } catch (Error ex) { 728 throw ex; 729 } catch (Exception ex) { 730 throw new CodeGenerationException(ex); 731 } 732 } 733 734 private static void member_helper_size(final CodeEmitter e, 735 List members, 736 final ObjectSwitchCallback callback, 737 final ParameterTyper typer, 738 final Label def, 739 final Label end) throws Exception { 740 final Map buckets = CollectionUtils.bucket(members, new Transformer() { 741 public Object transform(Object value) { 742 return new Integer(typer.getParameterTypes((MethodInfo)value).length); 743 } 744 }); 745 e.dup(); 746 e.arraylength(); 747 e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() { 748 public void processCase(int key, Label dontUseEnd) throws Exception { 749 List bucket = (List)buckets.get(new Integer(key)); 750 member_helper_type(e, bucket, callback, typer, def, end, new BitSet()); 751 } 752 public void processDefault() throws Exception { 753 e.goTo(def); 754 } 755 }); 756 } 757 758 private static void member_helper_type(final CodeEmitter e, 759 List members, 760 final ObjectSwitchCallback callback, 761 final ParameterTyper typer, 762 final Label def, 763 final Label end, 764 final BitSet checked) throws Exception { 765 if (members.size() == 1) { 766 MethodInfo member = (MethodInfo)members.get(0); 767 Type[] types = typer.getParameterTypes(member); 768 // need to check classes that have not already been checked via switches 769 for (int i = 0; i < types.length; i++) { 770 if (checked == null || !checked.get(i)) { 771 e.dup(); 772 e.aaload(i); 773 e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); 774 e.push(TypeUtils.emulateClassGetName(types[i])); 775 e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); 776 e.if_jump(e.EQ, def); 777 } 778 } 779 e.pop(); 780 callback.processCase(member, end); 781 } else { 782 // choose the index that has the best chance of uniquely identifying member 783 Type[] example = typer.getParameterTypes((MethodInfo)members.get(0)); 784 Map buckets = null; 785 int index = -1; 786 for (int i = 0; i < example.length; i++) { 787 final int j = i; 788 Map test = CollectionUtils.bucket(members, new Transformer() { 789 public Object transform(Object value) { 790 return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]); 791 } 792 }); 793 if (buckets == null || test.size() > buckets.size()) { 794 buckets = test; 795 index = i; 796 } 797 } 798 if (buckets == null || buckets.size() == 1) { 799 // TODO: switch by returnType 800 // must have two methods with same name, types, and different return types 801 e.goTo(def); 802 } else { 803 checked.set(index); 804 805 e.dup(); 806 e.aaload(index); 807 e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); 808 809 final Map fbuckets = buckets; 810 String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); 811 EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 812 public void processCase(Object key, Label dontUseEnd) throws Exception { 813 member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked); 814 } 815 public void processDefault() throws Exception { 816 e.goTo(def); 817 } 818 }); 819 } 820 } 821 } 822 823 public static void wrap_throwable(Block block, Type wrapper) { 824 CodeEmitter e = block.getCodeEmitter(); 825 e.catch_exception(block, Constants.TYPE_THROWABLE); 826 e.new_instance(wrapper); 827 e.dup_x1(); 828 e.swap(); 829 e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); 830 e.athrow(); 831 } 832 833 public static void add_properties(ClassEmitter ce, String[] names, Type[] types) { 834 for (int i = 0; i < names.length; i++) { 835 String fieldName = "$cglib_prop_" + names[i]; 836 ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null); 837 EmitUtils.add_property(ce, names[i], types[i], fieldName); 838 } 839 } 840 841 public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) { 842 String property = TypeUtils.upperFirst(name); 843 CodeEmitter e; 844 e = ce.begin_method(Constants.ACC_PUBLIC, 845 new Signature("get" + property, 846 type, 847 Constants.TYPES_EMPTY), 848 null); 849 e.load_this(); 850 e.getfield(fieldName); 851 e.return_value(); 852 e.end_method(); 853 854 e = ce.begin_method(Constants.ACC_PUBLIC, 855 new Signature("set" + property, 856 Type.VOID_TYPE, 857 new Type[]{ type }), 858 null); 859 e.load_this(); 860 e.load_arg(0); 861 e.putfield(fieldName); 862 e.return_value(); 863 e.end_method(); 864 } 865 866 /* generates: 867 } catch (RuntimeException e) { 868 throw e; 869 } catch (Error e) { 870 throw e; 871 } catch (<DeclaredException> e) { 872 throw e; 873 } catch (Throwable e) { 874 throw new <Wrapper>(e); 875 } 876 */ 877 public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) { 878 Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions)); 879 880 if (set.contains(Constants.TYPE_THROWABLE)) 881 return; 882 883 boolean needThrow = exceptions != null; 884 if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) { 885 e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION); 886 needThrow = true; 887 } 888 if (!set.contains(Constants.TYPE_ERROR)) { 889 e.catch_exception(handler, Constants.TYPE_ERROR); 890 needThrow = true; 891 } 892 if (exceptions != null) { 893 for (int i = 0; i < exceptions.length; i++) { 894 e.catch_exception(handler, exceptions[i]); 895 } 896 } 897 if (needThrow) { 898 e.athrow(); 899 } 900 // e -> eo -> oeo -> ooe -> o 901 e.catch_exception(handler, Constants.TYPE_THROWABLE); 902 e.new_instance(wrapper); 903 e.dup_x1(); 904 e.swap(); 905 e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); 906 e.athrow(); 907 } 908 909 public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) { 910 return begin_method(e, method, method.getModifiers()); 911 } 912 913 public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) { 914 return e.begin_method(access, 915 method.getSignature(), 916 method.getExceptionTypes()); 917 } 918} 919