1 /* 2 * Copyright 2003,2004 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.mockito.cglib.core; 17 18 import java.math.BigDecimal; 19 import java.math.BigInteger; 20 import java.util.*; 21 22 import org.mockito.asm.Label; 23 import org.mockito.asm.Type; 24 25 public 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 EmitUtils()74 private EmitUtils() { 75 } 76 factory_method(ClassEmitter ce, Signature sig)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 null_constructor(ClassEmitter ce)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 */ process_array(CodeEmitter e, Type type, ProcessArrayCallback callback)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 */ process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback)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 string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback)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 string_switch_trie(final CodeEmitter e, String[] strings, final ObjectSwitchCallback callback)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 stringSwitchHelper(final CodeEmitter e, List strings, final ObjectSwitchCallback callback, final Label def, final Label end, final int index)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 getSwitchKeys(Map buckets)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 string_switch_hash(final CodeEmitter e, final String[] strings, final ObjectSwitchCallback callback, final boolean skipEquals)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 load_class_this(CodeEmitter e)307 public static void load_class_this(CodeEmitter e) { 308 load_class_helper(e, e.getClassEmitter().getClassType()); 309 } 310 load_class(CodeEmitter e, Type type)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 load_class_helper(CodeEmitter e, final Type type)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 push_array(CodeEmitter e, Object[] array)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 remapComponentType(Class componentType)355 private static Class remapComponentType(Class componentType) { 356 if (componentType.equals(Type.class)) 357 return Class.class; 358 return componentType; 359 } 360 push_object(CodeEmitter e, Object obj)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 hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer)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 hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer)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 hash_object(CodeEmitter e, Type type, Customizer customizer)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 hash_primitive(CodeEmitter e, Type type)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 hash_long(CodeEmitter e)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 */ not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer)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 not_equals_helper(CodeEmitter e, Type type, Label notEquals, Customizer customizer, ProcessArrayCallback callback)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 */ nullcmp(CodeEmitter e, Label oneNull, Label bothNull)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 append_string(final CodeEmitter e, Type type, final ArrayDelimiters delims, final Customizer customizer)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 append_string_helper(CodeEmitter e, Type type, ArrayDelimiters delims, Customizer customizer, ProcessArrayCallback callback)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 shrinkStringBuffer(CodeEmitter e, int amt)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 ArrayDelimiters(String before, String inside, String after)655 public ArrayDelimiters(String before, String inside, String after) { 656 this.before = before; 657 this.inside = inside; 658 this.after = after; 659 } 660 } 661 load_method(CodeEmitter e, MethodInfo method)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 { getParameterTypes(MethodInfo member)670 Type[] getParameterTypes(MethodInfo member); 671 } 672 method_switch(CodeEmitter e, List methods, ObjectSwitchCallback callback)673 public static void method_switch(CodeEmitter e, 674 List methods, 675 ObjectSwitchCallback callback) { 676 member_switch_helper(e, methods, callback, true); 677 } 678 constructor_switch(CodeEmitter e, List constructors, ObjectSwitchCallback callback)679 public static void constructor_switch(CodeEmitter e, 680 List constructors, 681 ObjectSwitchCallback callback) { 682 member_switch_helper(e, constructors, callback, false); 683 } 684 member_switch_helper(final CodeEmitter e, List members, final ObjectSwitchCallback callback, boolean useName)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 member_helper_size(final CodeEmitter e, List members, final ObjectSwitchCallback callback, final ParameterTyper typer, final Label def, final Label end)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 member_helper_type(final CodeEmitter e, List members, final ObjectSwitchCallback callback, final ParameterTyper typer, final Label def, final Label end, final BitSet checked)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 wrap_throwable(Block block, Type wrapper)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 add_properties(ClassEmitter ce, String[] names, Type[] types)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 add_property(ClassEmitter ce, String name, Type type, String fieldName)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 */ wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper)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 begin_method(ClassEmitter e, MethodInfo method)909 public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) { 910 return begin_method(e, method, method.getModifiers()); 911 } 912 begin_method(ClassEmitter e, MethodInfo method, int access)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