1 46 package org.codehaus.groovy.classgen; 47 48 import groovy.lang.MetaMethod; 49 50 import java.lang.reflect.Method ; 51 import java.lang.reflect.Modifier ; 52 import java.math.BigDecimal ; 53 import java.math.BigInteger ; 54 55 import org.codehaus.groovy.ast.FieldNode; 56 import org.codehaus.groovy.ast.Parameter; 57 import org.codehaus.groovy.runtime.InvokerHelper; 58 import org.objectweb.asm.CodeVisitor; 59 import org.objectweb.asm.Constants; 60 import org.objectweb.asm.Label; 61 62 63 70 public class BytecodeHelper implements Constants { 71 72 private CodeVisitor cv; 73 74 public CodeVisitor getCodeVisitor() { 75 return cv; 76 } 77 78 public BytecodeHelper(CodeVisitor cv) { 79 this.cv = cv; 80 } 81 82 85 public void box(Class type) { 86 if (type.isPrimitive() && type != void.class) { 87 String returnString = "(" + getTypeDescription(type.getName()) + ")Ljava/lang/Object;"; 88 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), "box", returnString); 89 } 90 } 91 92 96 public void quickBoxIfNecessary(Class cls) { 97 String type = cls.getName(); 98 String descr = getTypeDescription(type); 99 if (cls == boolean.class) { 100 boxBoolean(); 101 } 102 else if (cls.isPrimitive() && cls != void.class) { 103 if (cls == Integer.TYPE) { 105 cv.visitMethodInsn( 106 INVOKESTATIC, 107 getClassInternalName(InvokerHelper.class.getName()), 108 "integerValue", 109 "(I)Ljava/lang/Integer;" 110 ); 111 return; 112 } 113 114 String wrapperName = getObjectTypeForPrimitive(type); 115 String internName = getClassInternalName(wrapperName); 116 cv.visitTypeInsn(NEW, internName); 117 cv.visitInsn(DUP); 118 if (type.equals("double") || type.equals("long")) { 119 cv.visitInsn(DUP2_X2); 120 cv.visitInsn(POP2); 121 } else { 122 cv.visitInsn(DUP2_X1); 123 cv.visitInsn(POP2); 124 } 125 cv.visitMethodInsn(INVOKESPECIAL, internName, "<init>", "(" + descr + ")V"); 126 127 } 131 } 132 133 137 public void quickUnboxIfNecessary(Class cls) { 138 String type = cls.getName(); 139 140 if (cls.isPrimitive() && cls != void.class) { String wrapperName = getObjectTypeForPrimitive(type); 142 String internName = getClassInternalName(wrapperName); 143 if (cls == boolean.class) { 144 cv.visitTypeInsn(CHECKCAST, internName); 145 cv.visitMethodInsn(INVOKEVIRTUAL, internName, type + "Value", "()" + getTypeDescription(type)); 146 } else { cv.visitTypeInsn(CHECKCAST, "java/lang/Number"); 148 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", type + "Value", "()" + getTypeDescription(type)); 149 } 150 } 151 152 } 153 154 public void box(String type) { 155 if (isPrimitiveType(type) && !type.equals("void")) { 156 String returnString = "(" + getTypeDescription(type) + ")Ljava/lang/Object;"; 157 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), "box", returnString); 158 } 160 } 161 162 165 public void unbox(Class type) { 166 if (type.isPrimitive() && type != void.class) { 167 String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type.getName()); 168 cv.visitMethodInsn( 169 INVOKESTATIC, 170 getClassInternalName(InvokerHelper.class.getName()), 171 type.getName() + "Unbox", 172 returnString); 173 } 174 } 175 176 179 public void unbox(String type) { 180 if (isPrimitiveType(type) && !type.equals("void")) { 181 String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type); 182 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), type + "Unbox", returnString); 183 } 184 } 185 186 public static boolean isPrimitiveType(String type) { 187 return type != null 188 && (type.equals("boolean") 189 || type.equals("byte") 190 || type.equals("char") 191 || type.equals("short") 192 || type.equals("int") 193 || type.equals("long") 194 || type.equals("float") 195 || type.equals("double")); 196 } 197 198 204 public static String getTypeDescription(String name) { 205 if (name == null) { 208 return "Ljava/lang/Object;"; 209 } 210 if (name.equals("void")) { 211 return "V"; 212 } 213 214 if (name.startsWith("[")) { return name.replace('.', '/'); 216 } 217 218 String prefix = ""; 219 if (name.endsWith("[]")) { 220 prefix = "["; 221 name = name.substring(0, name.length() - 2); 222 } 223 224 if (name.equals("int")) { 225 return prefix + "I"; 226 } 227 else if (name.equals("long")) { 228 return prefix + "J"; 229 } 230 else if (name.equals("short")) { 231 return prefix + "S"; 232 } 233 else if (name.equals("float")) { 234 return prefix + "F"; 235 } 236 else if (name.equals("double")) { 237 return prefix + "D"; 238 } 239 else if (name.equals("byte")) { 240 return prefix + "B"; 241 } 242 else if (name.equals("char")) { 243 return prefix + "C"; 244 } 245 else if (name.equals("boolean")) { 246 return prefix + "Z"; 247 } 248 return prefix + "L" + name.replace('.', '/') + ";"; 249 } 250 251 254 public static String getClassInternalName(String name) { 255 if (name == null) { 256 return "java/lang/Object"; 257 } 258 String answer = name.replace('.', '/'); 259 if (answer.endsWith("[]")) { 260 return "[" + answer.substring(0, answer.length() - 2); 261 } 262 return answer; 263 } 264 265 268 public static String getClassRegularName(String name) { 269 if (name == null) { 270 return "java.lang.Object"; 271 } 272 if (name.startsWith("L")) { 273 name = name.substring(1); 274 if (name.endsWith(";")) 275 name = name.substring(0, name.length() - 1); 276 } 277 String answer = name.replace('/', '.'); 278 return answer; 279 } 280 281 284 public static String getMethodDescriptor(String returnTypeName, Parameter[] paramTypeNames) { 285 StringBuffer buffer = new StringBuffer ("("); 287 for (int i = 0; i < paramTypeNames.length; i++) { 288 buffer.append(getTypeDescription(paramTypeNames[i].getType())); 289 } 290 buffer.append(")"); 291 buffer.append(getTypeDescription(returnTypeName)); 292 return buffer.toString(); 293 } 294 295 298 public static String getMethodDescriptor(Class returnType, Class [] paramTypes) { 299 StringBuffer buffer = new StringBuffer ("("); 301 for (int i = 0; i < paramTypes.length; i++) { 302 buffer.append(getTypeDescription(paramTypes[i])); 303 } 304 buffer.append(")"); 305 buffer.append(getTypeDescription(returnType)); 306 return buffer.toString(); 307 } 308 309 public static String getMethodDescriptor(Method meth) { 310 return getMethodDescriptor(meth.getReturnType(), meth.getParameterTypes()); 311 } 312 313 public static String getTypeDescription(Class type) { 314 if (type.isArray()) { 315 return type.getName().replace('.', '/'); 316 } 317 else { 318 return getTypeDescription(type.getName()); 319 } 320 } 321 322 325 public static String [] getClassInternalNames(String [] names) { 326 int size = names.length; 327 String [] answer = new String [size]; 328 for (int i = 0; i < size; i++) { 329 answer[i] = getClassInternalName(names[i]); 330 } 331 return answer; 332 } 333 334 protected void pushConstant(boolean value) { 335 if (value) { 336 cv.visitInsn(ICONST_1); 337 } 338 else { 339 cv.visitInsn(ICONST_0); 340 } 341 } 342 343 protected void pushConstant(int value) { 344 switch (value) { 345 case 0 : 346 cv.visitInsn(ICONST_0); 347 break; 348 case 1 : 349 cv.visitInsn(ICONST_1); 350 break; 351 case 2 : 352 cv.visitInsn(ICONST_2); 353 break; 354 case 3 : 355 cv.visitInsn(ICONST_3); 356 break; 357 case 4 : 358 cv.visitInsn(ICONST_4); 359 break; 360 case 5 : 361 cv.visitInsn(ICONST_5); 362 break; 363 default : 364 if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 365 cv.visitIntInsn(BIPUSH, value); 366 } 367 else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 368 cv.visitIntInsn(SIPUSH, value); 369 } 370 else { 371 cv.visitLdcInsn(new Integer (value)); 372 } 373 } 374 } 375 376 public void doCast(String type) { 377 if (!type.equals("java.lang.Object")) { 378 if (isPrimitiveType(type) && !type.equals("void")) { 379 unbox(type); 380 } 381 else { 382 cv.visitTypeInsn( 383 CHECKCAST, 384 type.endsWith("[]") ? getTypeDescription(type) : getClassInternalName(type)); 385 } 386 } 387 } 388 389 public void doCast(Class type) { 390 String name = type.getName(); 391 if (type.isArray()) { 392 name = type.getComponentType().getName() + "[]"; 393 } 394 doCast(name); 395 } 396 397 public void load(String type, int idx) { 398 if (type.equals("double")) { 399 cv.visitVarInsn(DLOAD, idx); 400 } 401 else if (type.equals("float")) { 402 cv.visitVarInsn(FLOAD, idx); 403 } 404 else if (type.equals("long")) { 405 cv.visitVarInsn(LLOAD, idx); 406 } 407 else if ( 408 type.equals("boolean") 409 || type.equals("char") 410 || type.equals("byte") 411 || type.equals("int") 412 || type.equals("short")) { 413 cv.visitVarInsn(ILOAD, idx); 414 } 415 else { 416 cv.visitVarInsn(ALOAD, idx); 417 } 418 } 419 420 public void load(Variable v) { 421 load(v.getTypeName(), v.getIndex()); 422 } 423 424 public void store(String type, int idx) { 425 if (type.equals("double")) { 426 cv.visitVarInsn(DSTORE, idx); 427 } 428 else if (type.equals("float")) { 429 cv.visitVarInsn(FSTORE, idx); 430 } 431 else if (type.equals("long")) { 432 cv.visitVarInsn(LSTORE, idx); 433 } 434 else if ( 435 type.equals("boolean") 436 || type.equals("char") 437 || type.equals("byte") 438 || type.equals("int") 439 || type.equals("short")) { 440 cv.visitVarInsn(ISTORE, idx); 441 } 442 else { 443 cv.visitVarInsn(ASTORE, idx); 444 } 445 } 446 447 public void store(Variable v, boolean markStart) { 448 String type = v.getTypeName(); 449 int idx = v.getIndex(); 450 451 if (type.equals("double")) { 452 cv.visitVarInsn(DSTORE, idx); 453 } 454 else if (type.equals("float")) { 455 cv.visitVarInsn(FSTORE, idx); 456 } 457 else if (type.equals("long")) { 458 cv.visitVarInsn(LSTORE, idx); 459 } 460 else if ( 461 type.equals("boolean") 462 || type.equals("char") 463 || type.equals("byte") 464 || type.equals("int") 465 || type.equals("short")) { 466 cv.visitVarInsn(ISTORE, idx); 467 } 468 else { 469 cv.visitVarInsn(ASTORE, idx); 470 } 471 if (AsmClassGenerator2.CREATE_DEBUG_INFO && markStart) { 472 Label l = v.getStartLabel(); 473 if (l != null) { 474 cv.visitLabel(l); 475 } else { 476 System.out.println("start label == null! what to do about this?"); 477 } 478 } 479 } 480 481 public void store(Variable v) { 482 store(v, false); 483 } 484 485 486 public static String getObjectTypeForPrimitive(String type) { 487 if (type.equals("boolean")) { 488 return Boolean .class.getName(); 489 } 490 else if (type.equals("byte")) { 491 return Byte .class.getName(); 492 } 493 else if (type.equals("char")) { 494 return Character .class.getName(); 495 } 496 else if (type.equals("short")) { 497 return Short .class.getName(); 498 } 499 else if (type.equals("int")) { 500 return Integer .class.getName(); 501 } 502 else if (type.equals("long")) { 503 return Long .class.getName(); 504 } 505 else if (type.equals("float")) { 506 return Float .class.getName(); 507 } 508 else if (type.equals("double")) { 509 return Double .class.getName(); 510 } 511 else { 512 return type; 513 } 514 } 515 516 521 public static String getObjectArrayTypeForPrimitiveArray(String type) { 522 String prefix = ""; 523 while (type.endsWith("[]")) { 524 prefix += "["; 525 type = type.substring(0, type.length() - 2); 526 } 527 528 if (prefix.length() ==0) 529 return type; 530 531 String suffix = getObjectTypeForPrimitive(type); 532 return prefix + "L" + suffix + ";"; 533 } 534 535 536 537 540 void loadConstant (Object value) { 541 if (value == null) { 542 cv.visitInsn(ACONST_NULL); 543 } 544 else if (value instanceof String ) { 545 cv.visitLdcInsn(value); 546 } 547 else if (value instanceof Number ) { 548 549 Number n = (Number ) value; 550 String className = BytecodeHelper.getClassInternalName(value.getClass().getName()); 551 cv.visitTypeInsn(NEW, className); 552 cv.visitInsn(DUP); 553 String methodType; 554 if (n instanceof Double ) { 555 cv.visitLdcInsn(n); 556 methodType = "(D)V"; 557 } 558 else if (n instanceof Float ) { 559 cv.visitLdcInsn(n); 560 methodType = "(F)V"; 561 } 562 else if (n instanceof Long ) { 563 cv.visitLdcInsn(n); 564 methodType = "(J)V"; 565 } 566 else if (n instanceof BigDecimal ) { 567 cv.visitLdcInsn(n.toString()); 568 methodType = "(Ljava/lang/String;)V"; 569 } 570 else if (n instanceof BigInteger ) { 571 cv.visitLdcInsn(n.toString()); 572 methodType = "(Ljava/lang/String;)V"; 573 } 574 else if (n instanceof Integer ){ 575 pushConstant(n.intValue()); 577 methodType = "(I)V"; 578 } 579 else 580 { 581 throw new ClassGeneratorException( 582 "Cannot generate bytecode for constant: " + value 583 + " of type: " + value.getClass().getName() 584 +". Numeric constant type not supported."); 585 } 586 cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType); 587 } 588 else if (value instanceof Boolean ) { 589 Boolean bool = (Boolean ) value; 590 String text = (bool.booleanValue()) ? "TRUE" : "FALSE"; 591 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;"); 592 } 593 else if (value instanceof Class ) { 594 Class vc = (Class ) value; 595 if (vc.getName().equals("java.lang.Void")) { 596 } else { 598 throw new ClassGeneratorException( 599 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName()); 600 } 601 } 602 else { 603 throw new ClassGeneratorException( 604 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName()); 605 } 606 } 607 608 609 614 public void loadVar(Variable variable, boolean holder) { 615 String type = variable.getTypeName(); 616 int index = variable.getIndex(); 617 if (holder) { 618 cv.visitVarInsn(ALOAD, index); 619 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;"); 620 } else { 621 cv.visitVarInsn(ALOAD, index); } 623 } 624 625 public void storeVar(Variable variable, boolean holder) { 626 String type = variable.getTypeName(); 627 int index = variable.getIndex(); 628 629 if (holder) { 630 cv.visitVarInsn(ALOAD, index); 632 cv.visitInsn(SWAP); cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V"); 635 } 636 else { 637 store(variable.deriveBoxedVersion()); } 643 } 644 645 653 public void putField(FieldNode fld) { 654 putField(fld, getClassInternalName(fld.getOwner())); 655 } 656 657 public void putField(FieldNode fld, String ownerName) { 658 cv.visitFieldInsn(PUTFIELD, ownerName, fld.getName(), getTypeDescription(fld.getType())); 659 } 660 661 public void loadThis() { 662 cv.visitVarInsn(ALOAD, 0); 663 } 664 665 public static Class boxOnPrimitive(Class cls) { 666 Class ans = cls; 667 if (ans == null) 668 return null; 669 670 if (cls.isPrimitive() && cls != void.class) { 671 if (cls == int.class) { 672 ans = Integer .class; 673 } 674 else if (cls == byte.class) { 675 ans = Byte .class; 676 } 677 else if (cls == char.class) { 678 ans = Character .class; 679 } 680 else if (cls == short.class) { 681 ans = Short .class; 682 } 683 else if (cls == boolean.class) { 684 ans = Boolean .class; 685 } 686 else if (cls == float.class) { 687 ans = Float .class; 688 } 689 else if (cls == long.class) { 690 ans = Long .class; 691 } 692 else if (cls == double.class) { 693 ans = Double .class; 694 } 695 } 696 else if (cls.isArray()){ 697 int dimension = 0; 699 Class elemType = null; 700 do { 701 ++dimension; 702 elemType = cls.getComponentType(); 703 } while(elemType.isArray()); 704 705 if (elemType.isPrimitive()) { 706 Class boxElem = null; 707 if (elemType == int.class) { 708 boxElem = Integer .class; 709 } 710 else if (elemType == byte.class) { 711 boxElem = Byte .class; 712 } 713 else if (elemType == char.class) { 714 boxElem = Character .class; 715 } 716 else if (elemType == short.class) { 717 boxElem = Short .class; 718 } 719 else if (elemType == boolean.class) { 720 boxElem = Boolean .class; 721 } 722 else if (elemType == float.class) { 723 boxElem = Float .class; 724 } 725 else if (elemType == long.class) { 726 boxElem = Long .class; 727 } 728 else if (elemType == double.class) { 729 boxElem = Double .class; 730 } 731 String typeName = ""; 733 for (int i = 0; i < dimension; i++){ 734 typeName += "["; 735 } 736 typeName += "L" + boxElem.getName() + ";"; 737 try { 738 return Class.forName(typeName); 739 } catch (ClassNotFoundException e) { 740 throw new RuntimeException (e); } 742 } 743 } 744 return ans; 745 } 746 747 751 public void invoke(Method meth) { 752 int op = Modifier.isStatic(meth.getModifiers()) ? 753 INVOKESTATIC : 754 (meth.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL); 755 756 cv.visitMethodInsn( 757 op, 758 getClassInternalName(meth.getDeclaringClass().getName()), 759 meth.getName(), 760 getMethodDescriptor(meth) 761 ); 762 } 763 764 767 public void boxBoolean() { 768 Label l0 = new Label(); 769 cv.visitJumpInsn(IFEQ, l0); 770 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); 771 Label l1 = new Label(); 772 cv.visitJumpInsn(GOTO, l1); 773 cv.visitLabel(l0); 774 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); 775 cv.visitLabel(l1); 776 } 777 778 public static String getMethodDescriptor(MetaMethod metamethod) { 779 return getMethodDescriptor(metamethod.getReturnType(), metamethod.getParameterTypes()); 780 } 781 782 786 public void mark(String msg) { 787 cv.visitLdcInsn(msg); 788 cv.visitInsn(POP); 789 } 790 791 798 public static String formatNameForClassLoading(String name) { 799 if (name.equals("int") 800 || name.equals("long") 801 || name.equals("short") 802 || name.equals("float") 803 || name.equals("double") 804 || name.equals("byte") 805 || name.equals("char") 806 || name.equals("boolean") 807 || name.equals("void") 808 ) { 809 return name; 810 } 811 812 if (name == null) { 813 return "java.lang.Object;"; 814 } 815 816 if (name.startsWith("[")) { 817 return name.replace('/', '.'); 818 } 819 820 if (name.startsWith("L")) { 821 name = name.substring(1); 822 if (name.endsWith(";")) { 823 name = name.substring(0, name.length() - 1); 824 } 825 return name.replace('/', '.'); 826 } 827 828 String prefix = ""; 829 if (name.endsWith("[]")) { prefix = "["; 831 name = name.substring(0, name.length() - 2); 832 if (name.equals("int")) { 833 return prefix + "I"; 834 } 835 else if (name.equals("long")) { 836 return prefix + "J"; 837 } 838 else if (name.equals("short")) { 839 return prefix + "S"; 840 } 841 else if (name.equals("float")) { 842 return prefix + "F"; 843 } 844 else if (name.equals("double")) { 845 return prefix + "D"; 846 } 847 else if (name.equals("byte")) { 848 return prefix + "B"; 849 } 850 else if (name.equals("char")) { 851 return prefix + "C"; 852 } 853 else if (name.equals("boolean")) { 854 return prefix + "Z"; 855 } 856 else { 857 return prefix + "L" + name.replace('/', '.') + ";"; 858 } 859 } 860 return name.replace('/', '.'); 861 862 } 863 864 public void dup() { 865 cv.visitInsn(DUP); 866 } 867 } 868 | Popular Tags |