001 002 /* 003 $Id: BytecodeHelper.java,v 1.19 2005/05/27 10:13:09 russel Exp $ 004 005 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 006 007 Redistribution and use of this software and associated documentation 008 ("Software"), with or without modification, are permitted provided 009 that the following conditions are met: 010 011 1. Redistributions of source code must retain copyright 012 statements and notices. Redistributions must also contain a 013 copy of this document. 014 015 2. Redistributions in binary form must reproduce the 016 above copyright notice, this list of conditions and the 017 following disclaimer in the documentation and/or other 018 materials provided with the distribution. 019 020 3. The name "groovy" must not be used to endorse or promote 021 products derived from this Software without prior written 022 permission of The Codehaus. For written permission, 023 please contact info@codehaus.org. 024 025 4. Products derived from this Software may not be called "groovy" 026 nor may "groovy" appear in their names without prior written 027 permission of The Codehaus. "groovy" is a registered 028 trademark of The Codehaus. 029 030 5. Due credit should be given to The Codehaus - 031 http://groovy.codehaus.org/ 032 033 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 034 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 035 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 036 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 037 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 038 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 039 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 040 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 041 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 042 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 043 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 044 OF THE POSSIBILITY OF SUCH DAMAGE. 045 046 */ 047 package org.codehaus.groovy.classgen; 048 049 import groovy.lang.MetaMethod; 050 051 import java.lang.reflect.Method; 052 import java.lang.reflect.Modifier; 053 import java.math.BigDecimal; 054 import java.math.BigInteger; 055 056 import org.codehaus.groovy.ast.FieldNode; 057 import org.codehaus.groovy.ast.Parameter; 058 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; 059 import org.objectweb.asm.MethodVisitor; 060 import org.objectweb.asm.Opcodes; 061 import org.objectweb.asm.Label; 062 063 064 /** 065 * A helper class for bytecode generation with AsmClassGenerator. 066 * 067 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 068 * @author <a href="mailto:b55r@sina.com">Bing Ran</a> 069 * @version $Revision: 1.19 $ 070 */ 071 public class BytecodeHelper implements Opcodes { 072 073 private MethodVisitor cv; 074 075 public MethodVisitor getMethodVisitor() { 076 return cv; 077 } 078 079 public BytecodeHelper(MethodVisitor cv) { 080 this.cv = cv; 081 } 082 083 /** 084 * Generates the bytecode to autobox the current value on the stack 085 */ 086 public void box(Class type) { 087 if (type.isPrimitive() && type != void.class) { 088 String returnString = "(" + getTypeDescription(type.getName()) + ")Ljava/lang/Object;"; 089 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(ScriptBytecodeAdapter.class.getName()), "box", returnString); 090 } 091 } 092 093 /** 094 * box the primitive value on the stack 095 * @param cls 096 */ 097 public void quickBoxIfNecessary(Class cls) { 098 String type = cls.getName(); 099 String descr = getTypeDescription(type); 100 if (cls == boolean.class) { 101 boxBoolean(); 102 } 103 else if (cls.isPrimitive() && cls != void.class) { 104 // use a special integer pool in the invokerhelper 105 if (cls == Integer.TYPE) { 106 cv.visitMethodInsn( 107 INVOKESTATIC, 108 getClassInternalName(ScriptBytecodeAdapter.class.getName()), 109 "integerValue", 110 "(I)Ljava/lang/Integer;" 111 ); 112 return; 113 } 114 115 String wrapperName = getObjectTypeForPrimitive(type); 116 String internName = getClassInternalName(wrapperName); 117 cv.visitTypeInsn(NEW, internName); 118 cv.visitInsn(DUP); 119 if (type.equals("double") || type.equals("long")) { 120 cv.visitInsn(DUP2_X2); 121 cv.visitInsn(POP2); 122 } else { 123 cv.visitInsn(DUP2_X1); 124 cv.visitInsn(POP2); 125 } 126 cv.visitMethodInsn(INVOKESPECIAL, internName, "<init>", "(" + descr + ")V"); 127 128 // Operand opr = new Operand(ITEM_Object, wrapperName, "", ""); 129 // _safePop(); 130 // push(opr); 131 } 132 } 133 134 /** 135 * unbox the ref on the stack 136 * @param cls 137 */ 138 public void quickUnboxIfNecessary(Class cls) { 139 String type = cls.getName(); 140 141 if (cls.isPrimitive() && cls != void.class) { // todo care when BigDecimal or BigIneteger on stack 142 String wrapperName = getObjectTypeForPrimitive(type); 143 String internName = getClassInternalName(wrapperName); 144 if (cls == boolean.class) { 145 cv.visitTypeInsn(CHECKCAST, internName); 146 cv.visitMethodInsn(INVOKEVIRTUAL, internName, type + "Value", "()" + getTypeDescription(type)); 147 } else { // numbers 148 cv.visitTypeInsn(CHECKCAST, "java/lang/Number"); 149 cv.visitMethodInsn(INVOKEVIRTUAL, /*internName*/"java/lang/Number", type + "Value", "()" + getTypeDescription(type)); 150 } 151 } 152 153 } 154 155 public void box(String type) { 156 if (isPrimitiveType(type) && !type.equals("void")) { 157 String returnString = "(" + getTypeDescription(type) + ")Ljava/lang/Object;"; 158 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(ScriptBytecodeAdapter.class.getName()), "box", returnString); 159 // todo optimize this 160 } 161 } 162 163 /** 164 * Generates the bytecode to unbox the current value on the stack 165 */ 166 public void unbox(Class type) { 167 if (type.isPrimitive() && type != void.class) { 168 String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type.getName()); 169 cv.visitMethodInsn( 170 INVOKESTATIC, 171 getClassInternalName(ScriptBytecodeAdapter.class.getName()), 172 type.getName() + "Unbox", 173 returnString); 174 } 175 } 176 177 /** 178 * Generates the bytecode to unbox the current value on the stack 179 */ 180 public void unbox(String type) { 181 if (isPrimitiveType(type) && !type.equals("void")) { 182 String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type); 183 cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(ScriptBytecodeAdapter.class.getName()), type + "Unbox", returnString); 184 } 185 } 186 187 public static boolean isPrimitiveType(String type) { 188 return type != null 189 && (type.equals("boolean") 190 || type.equals("byte") 191 || type.equals("char") 192 || type.equals("short") 193 || type.equals("int") 194 || type.equals("long") 195 || type.equals("float") 196 || type.equals("double")); 197 } 198 199 /** 200 * array types are special: 201 * eg.: String[]: classname: [Ljava/lang/String; 202 * int[]: [I 203 * @return the ASM type description 204 */ 205 public static String getTypeDescription(String name) { 206 // lets avoid class loading 207 // return getType(name).getDescriptor(); 208 if (name == null) { 209 return "Ljava/lang/Object;"; 210 } 211 if (name.equals("void")) { 212 return "V"; 213 } 214 215 if (name.startsWith("[")) { // todo need to take care of multi-dimentional array 216 return name.replace('.', '/'); 217 } 218 219 String prefix = ""; 220 if (name.endsWith("[]")) { 221 prefix = "["; 222 name = name.substring(0, name.length() - 2); 223 } 224 225 if (name.equals("int")) { 226 return prefix + "I"; 227 } 228 else if (name.equals("long")) { 229 return prefix + "J"; 230 } 231 else if (name.equals("short")) { 232 return prefix + "S"; 233 } 234 else if (name.equals("float")) { 235 return prefix + "F"; 236 } 237 else if (name.equals("double")) { 238 return prefix + "D"; 239 } 240 else if (name.equals("byte")) { 241 return prefix + "B"; 242 } 243 else if (name.equals("char")) { 244 return prefix + "C"; 245 } 246 else if (name.equals("boolean")) { 247 return prefix + "Z"; 248 } 249 return prefix + "L" + name.replace('.', '/') + ";"; 250 } 251 252 /** 253 * @return the ASM internal name of the type 254 */ 255 public static String getClassInternalName(String name) { 256 if (name == null) { 257 return "java/lang/Object"; 258 } 259 String answer = name.replace('.', '/'); 260 if (answer.endsWith("[]")) { 261 return "[" + answer.substring(0, answer.length() - 2); 262 } 263 return answer; 264 } 265 266 /** 267 * @return the regular class name of the type 268 */ 269 public static String getClassRegularName(String name) { 270 if (name == null) { 271 return "java.lang.Object"; 272 } 273 if (name.startsWith("L")) { 274 name = name.substring(1); 275 if (name.endsWith(";")) 276 name = name.substring(0, name.length() - 1); 277 } 278 String answer = name.replace('/', '.'); 279 return answer; 280 } 281 282 /** 283 * @return the ASM method type descriptor 284 */ 285 public static String getMethodDescriptor(String returnTypeName, Parameter[] paramTypeNames) { 286 // lets avoid class loading 287 StringBuffer buffer = new StringBuffer("("); 288 for (int i = 0; i < paramTypeNames.length; i++) { 289 buffer.append(getTypeDescription(paramTypeNames[i].getType())); 290 } 291 buffer.append(")"); 292 buffer.append(getTypeDescription(returnTypeName)); 293 return buffer.toString(); 294 } 295 296 /** 297 * @return the ASM method type descriptor 298 */ 299 public static String getMethodDescriptor(Class returnType, Class[] paramTypes) { 300 // lets avoid class loading 301 StringBuffer buffer = new StringBuffer("("); 302 for (int i = 0; i < paramTypes.length; i++) { 303 buffer.append(getTypeDescription(paramTypes[i])); 304 } 305 buffer.append(")"); 306 buffer.append(getTypeDescription(returnType)); 307 return buffer.toString(); 308 } 309 310 public static String getMethodDescriptor(Method meth) { 311 return getMethodDescriptor(meth.getReturnType(), meth.getParameterTypes()); 312 } 313 314 public static String getTypeDescription(Class type) { 315 if (type.isArray()) { 316 return type.getName().replace('.', '/'); 317 } 318 else { 319 return getTypeDescription(type.getName()); 320 } 321 } 322 323 /** 324 * @return an array of ASM internal names of the type 325 */ 326 public static String[] getClassInternalNames(String[] names) { 327 int size = names.length; 328 String[] answer = new String[size]; 329 for (int i = 0; i < size; i++) { 330 answer[i] = getClassInternalName(names[i]); 331 } 332 return answer; 333 } 334 335 protected void pushConstant(boolean value) { 336 if (value) { 337 cv.visitInsn(ICONST_1); 338 } 339 else { 340 cv.visitInsn(ICONST_0); 341 } 342 } 343 344 protected void pushConstant(int value) { 345 switch (value) { 346 case 0 : 347 cv.visitInsn(ICONST_0); 348 break; 349 case 1 : 350 cv.visitInsn(ICONST_1); 351 break; 352 case 2 : 353 cv.visitInsn(ICONST_2); 354 break; 355 case 3 : 356 cv.visitInsn(ICONST_3); 357 break; 358 case 4 : 359 cv.visitInsn(ICONST_4); 360 break; 361 case 5 : 362 cv.visitInsn(ICONST_5); 363 break; 364 default : 365 if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 366 cv.visitIntInsn(BIPUSH, value); 367 } 368 else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 369 cv.visitIntInsn(SIPUSH, value); 370 } 371 else { 372 cv.visitLdcInsn(new Integer(value)); 373 } 374 } 375 } 376 377 public void doCast(String type) { 378 if (!type.equals("java.lang.Object")) { 379 if (isPrimitiveType(type) && !type.equals("void")) { 380 unbox(type); 381 } 382 else { 383 cv.visitTypeInsn( 384 CHECKCAST, 385 type.endsWith("[]") ? getTypeDescription(type) : getClassInternalName(type)); 386 } 387 } 388 } 389 390 public void doCast(Class type) { 391 String name = type.getName(); 392 if (type.isArray()) { 393 name = type.getComponentType().getName() + "[]"; 394 } 395 doCast(name); 396 } 397 398 public void load(String type, int idx) { 399 if (type.equals("double")) { 400 cv.visitVarInsn(DLOAD, idx); 401 } 402 else if (type.equals("float")) { 403 cv.visitVarInsn(FLOAD, idx); 404 } 405 else if (type.equals("long")) { 406 cv.visitVarInsn(LLOAD, idx); 407 } 408 else if ( 409 type.equals("boolean") 410 || type.equals("char") 411 || type.equals("byte") 412 || type.equals("int") 413 || type.equals("short")) { 414 cv.visitVarInsn(ILOAD, idx); 415 } 416 else { 417 cv.visitVarInsn(ALOAD, idx); 418 } 419 } 420 421 public void load(Variable v) { 422 load(v.getTypeName(), v.getIndex()); 423 } 424 425 public void store(String type, int idx) { 426 if (type.equals("double")) { 427 cv.visitVarInsn(DSTORE, idx); 428 } 429 else if (type.equals("float")) { 430 cv.visitVarInsn(FSTORE, idx); 431 } 432 else if (type.equals("long")) { 433 cv.visitVarInsn(LSTORE, idx); 434 } 435 else if ( 436 type.equals("boolean") 437 || type.equals("char") 438 || type.equals("byte") 439 || type.equals("int") 440 || type.equals("short")) { 441 cv.visitVarInsn(ISTORE, idx); 442 } 443 else { 444 cv.visitVarInsn(ASTORE, idx); 445 } 446 } 447 448 public void store(Variable v, boolean markStart) { 449 String type = v.getTypeName(); 450 int idx = v.getIndex(); 451 452 if (type.equals("double")) { 453 cv.visitVarInsn(DSTORE, idx); 454 } 455 else if (type.equals("float")) { 456 cv.visitVarInsn(FSTORE, idx); 457 } 458 else if (type.equals("long")) { 459 cv.visitVarInsn(LSTORE, idx); 460 } 461 else if ( 462 type.equals("boolean") 463 || type.equals("char") 464 || type.equals("byte") 465 || type.equals("int") 466 || type.equals("short")) { 467 cv.visitVarInsn(ISTORE, idx); 468 } 469 else { 470 cv.visitVarInsn(ASTORE, idx); 471 } 472 if (AsmClassGenerator.CREATE_DEBUG_INFO && markStart) { 473 Label l = v.getStartLabel(); 474 if (l != null) { 475 cv.visitLabel(l); 476 } else { 477 System.out.println("start label == null! what to do about this?"); 478 } 479 } 480 } 481 482 public void store(Variable v) { 483 store(v, false); 484 } 485 486 487 public static String getObjectTypeForPrimitive(String type) { 488 if (type.equals("boolean")) { 489 return Boolean.class.getName(); 490 } 491 else if (type.equals("byte")) { 492 return Byte.class.getName(); 493 } 494 else if (type.equals("char")) { 495 return Character.class.getName(); 496 } 497 else if (type.equals("short")) { 498 return Short.class.getName(); 499 } 500 else if (type.equals("int")) { 501 return Integer.class.getName(); 502 } 503 else if (type.equals("long")) { 504 return Long.class.getName(); 505 } 506 else if (type.equals("float")) { 507 return Float.class.getName(); 508 } 509 else if (type.equals("double")) { 510 return Double.class.getName(); 511 } 512 else { 513 return type; 514 } 515 } 516 517 /** 518 * load the constant on the operand stack. primitives auto-boxed. 519 */ 520 void loadConstant (Object value) { 521 if (value == null) { 522 cv.visitInsn(ACONST_NULL); 523 } 524 else if (value instanceof String) { 525 cv.visitLdcInsn(value); 526 } 527 else if (value instanceof Number) { 528 /** todo it would be more efficient to generate class constants */ 529 Number n = (Number) value; 530 String className = BytecodeHelper.getClassInternalName(value.getClass().getName()); 531 cv.visitTypeInsn(NEW, className); 532 cv.visitInsn(DUP); 533 String methodType; 534 if (n instanceof Double) { 535 cv.visitLdcInsn(n); 536 methodType = "(D)V"; 537 } 538 else if (n instanceof Float) { 539 cv.visitLdcInsn(n); 540 methodType = "(F)V"; 541 } 542 else if (n instanceof Long) { 543 cv.visitLdcInsn(n); 544 methodType = "(J)V"; 545 } 546 else if (n instanceof BigDecimal) { 547 cv.visitLdcInsn(n.toString()); 548 methodType = "(Ljava/lang/String;)V"; 549 } 550 else if (n instanceof BigInteger) { 551 cv.visitLdcInsn(n.toString()); 552 methodType = "(Ljava/lang/String;)V"; 553 } 554 else if (n instanceof Integer){ 555 //cv.visitLdcInsn(n); 556 pushConstant(n.intValue()); 557 methodType = "(I)V"; 558 } 559 else 560 { 561 throw new ClassGeneratorException( 562 "Cannot generate bytecode for constant: " + value 563 + " of type: " + value.getClass().getName() 564 +". Numeric constant type not supported."); 565 } 566 cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType); 567 } 568 else if (value instanceof Boolean) { 569 Boolean bool = (Boolean) value; 570 String text = (bool.booleanValue()) ? "TRUE" : "FALSE"; 571 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;"); 572 } 573 else if (value instanceof Class) { 574 Class vc = (Class) value; 575 if (vc.getName().equals("java.lang.Void")) { 576 // load nothing here for void 577 } else { 578 throw new ClassGeneratorException( 579 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName()); 580 } 581 } 582 else { 583 throw new ClassGeneratorException( 584 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName()); 585 } 586 } 587 588 589 /** 590 * load the value of the variable on the operand stack. unbox it if it's a reference 591 * @param variable 592 * @param holder 593 */ 594 public void loadVar(Variable variable, boolean holder) { 595 String type = variable.getTypeName(); 596 int index = variable.getIndex(); 597 if (holder) { 598 cv.visitVarInsn(ALOAD, index); 599 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;"); 600 } else { 601 cv.visitVarInsn(ALOAD, index); // todo? shall xload based on the type? 602 } 603 } 604 605 public void storeVar(Variable variable, boolean holder) { 606 String type = variable.getTypeName(); 607 int index = variable.getIndex(); 608 609 if (holder) { 610 //int tempIndex = visitASTOREInTemp("reference", type); 611 cv.visitVarInsn(ALOAD, index); 612 cv.visitInsn(SWAP); // assuming the value on stack is single word 613 //cv.visitVarInsn(ALOAD, tempIndex); 614 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V"); 615 } 616 else { 617 store(variable.deriveBoxedVersion()); // todo br seems right hand values on the stack are always object refs, primitives boxed 618 // if (!varStored) { 619 // //visitVariableStartLabel(variable); 620 // varStored = true; 621 // } 622 } 623 } 624 625 // private int visitASTOREInTemp(String name, String type) { 626 // Variable var = defineVariable(createVariableName(name), type, false); 627 // int varIdx = var.getIndex(); 628 // cv.visitVarInsn(ASTORE, varIdx); 629 // if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel()); 630 // return varIdx; 631 // } 632 633 public void putField(FieldNode fld) { 634 putField(fld, getClassInternalName(fld.getOwner())); 635 } 636 637 public void putField(FieldNode fld, String ownerName) { 638 cv.visitFieldInsn(PUTFIELD, ownerName, fld.getName(), getTypeDescription(fld.getType())); 639 } 640 641 public void loadThis() { 642 cv.visitVarInsn(ALOAD, 0); 643 } 644 645 public static Class boxOnPrimitive(Class cls) { 646 Class ans = cls; 647 if (ans == null) 648 return null; 649 650 if (cls.isPrimitive() && cls != void.class) { 651 if (cls == int.class) { 652 ans = Integer.class; 653 } 654 else if (cls == byte.class) { 655 ans = Byte.class; 656 } 657 else if (cls == char.class) { 658 ans = Character.class; 659 } 660 else if (cls == short.class) { 661 ans = Short.class; 662 } 663 else if (cls == boolean.class) { 664 ans = Boolean.class; 665 } 666 else if (cls == float.class) { 667 ans = Float.class; 668 } 669 else if (cls == long.class) { 670 ans = Long.class; 671 } 672 else if (cls == double.class) { 673 ans = Double.class; 674 } 675 } 676 else if (cls.isArray()){ 677 // let's convert primitive array too 678 int dimension = 0; 679 Class elemType = null; 680 do { 681 ++dimension; 682 elemType = cls.getComponentType(); 683 } while(elemType.isArray()); 684 685 if (elemType.isPrimitive()) { 686 Class boxElem = null; 687 if (elemType == int.class) { 688 boxElem = Integer.class; 689 } 690 else if (elemType == byte.class) { 691 boxElem = Byte.class; 692 } 693 else if (elemType == char.class) { 694 boxElem = Character.class; 695 } 696 else if (elemType == short.class) { 697 boxElem = Short.class; 698 } 699 else if (elemType == boolean.class) { 700 boxElem = Boolean.class; 701 } 702 else if (elemType == float.class) { 703 boxElem = Float.class; 704 } 705 else if (elemType == long.class) { 706 boxElem = Long.class; 707 } 708 else if (elemType == double.class) { 709 boxElem = Double.class; 710 } 711 // I need to construct a new array type for the box version 712 String typeName = ""; 713 for (int i = 0; i < dimension; i++){ 714 typeName += "["; 715 } 716 typeName += "L" + boxElem.getName() + ";"; 717 try { 718 return Class.forName(typeName); 719 } catch (ClassNotFoundException e) { 720 throw new RuntimeException(e); // should never have come here 721 } 722 } 723 } 724 return ans; 725 } 726 727 /** 728 * create the bytecode to invoke a method 729 * @param meth the method object to invoke 730 */ 731 public void invoke(Method meth) { 732 int op = Modifier.isStatic(meth.getModifiers()) ? 733 INVOKESTATIC : 734 (meth.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL); 735 736 cv.visitMethodInsn( 737 op, 738 getClassInternalName(meth.getDeclaringClass().getName()), 739 meth.getName(), 740 getMethodDescriptor(meth) 741 ); 742 } 743 744 /** 745 * convert boolean to Boolean 746 */ 747 public void boxBoolean() { 748 Label l0 = new Label(); 749 cv.visitJumpInsn(IFEQ, l0); 750 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); 751 Label l1 = new Label(); 752 cv.visitJumpInsn(GOTO, l1); 753 cv.visitLabel(l0); 754 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); 755 cv.visitLabel(l1); 756 } 757 758 public static String getMethodDescriptor(MetaMethod metamethod) { 759 return getMethodDescriptor(metamethod.getReturnType(), metamethod.getParameterTypes()); 760 } 761 762 /** 763 * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose. 764 * @param msg 765 */ 766 public void mark(String msg) { 767 cv.visitLdcInsn(msg); 768 cv.visitInsn(POP); 769 } 770 771 /** 772 * returns a name that Class.forName() can take. Notablely for arrays: 773 * [I, [Ljava.lang.String; etc 774 * Regular object type: java.lang.String 775 * @param name 776 * @return 777 */ 778 public static String formatNameForClassLoading(String name) { 779 if (name.equals("int") 780 || name.equals("long") 781 || name.equals("short") 782 || name.equals("float") 783 || name.equals("double") 784 || name.equals("byte") 785 || name.equals("char") 786 || name.equals("boolean") 787 || name.equals("void") 788 ) { 789 return name; 790 } 791 792 if (name == null) { 793 return "java.lang.Object;"; 794 } 795 796 if (name.startsWith("[")) { 797 return name.replace('/', '.'); 798 } 799 800 if (name.startsWith("L")) { 801 name = name.substring(1); 802 if (name.endsWith(";")) { 803 name = name.substring(0, name.length() - 1); 804 } 805 return name.replace('/', '.'); 806 } 807 808 String prefix = ""; 809 if (name.endsWith("[]")) { // todo need process multi 810 prefix = "["; 811 name = name.substring(0, name.length() - 2); 812 if (name.equals("int")) { 813 return prefix + "I"; 814 } 815 else if (name.equals("long")) { 816 return prefix + "J"; 817 } 818 else if (name.equals("short")) { 819 return prefix + "S"; 820 } 821 else if (name.equals("float")) { 822 return prefix + "F"; 823 } 824 else if (name.equals("double")) { 825 return prefix + "D"; 826 } 827 else if (name.equals("byte")) { 828 return prefix + "B"; 829 } 830 else if (name.equals("char")) { 831 return prefix + "C"; 832 } 833 else if (name.equals("boolean")) { 834 return prefix + "Z"; 835 } 836 else { 837 return prefix + "L" + name.replace('/', '.') + ";"; 838 } 839 } 840 return name.replace('/', '.'); 841 842 } 843 844 public void dup() { 845 cv.visitInsn(DUP); 846 } 847 }