1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 package com.sun.org.apache.bcel.internal.classfile; 22 23 import java.io.ByteArrayInputStream; 24 import java.io.ByteArrayOutputStream; 25 import java.io.CharArrayReader; 26 import java.io.CharArrayWriter; 27 import java.io.FilterReader; 28 import java.io.FilterWriter; 29 import java.io.IOException; 30 import java.io.PrintStream; 31 import java.io.PrintWriter; 32 import java.io.Reader; 33 import java.io.Writer; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.zip.GZIPInputStream; 38 import java.util.zip.GZIPOutputStream; 39 40 import com.sun.org.apache.bcel.internal.Const; 41 import com.sun.org.apache.bcel.internal.util.ByteSequence; 42 43 /** 44 * Utility functions that do not really belong to any class in particular. 45 * 46 * @version $Id: Utility.java 1751107 2016-07-03 02:41:18Z dbrosius $ 47 */ 48 // @since 6.0 methods are no longer final 49 public abstract class Utility { 50 51 private static int unwrap(final ThreadLocal<Integer> tl) { 52 return tl.get().intValue(); 53 } 54 55 private static void wrap(final ThreadLocal<Integer> tl, final int value) { 56 tl.set(Integer.valueOf(value)); 57 } 58 59 private static ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() { 60 61 @Override 62 protected Integer initialValue() { 63 return Integer.valueOf(0); 64 } 65 };/* How many chars have been consumed 66 * during parsing in signatureToString(). 67 * Read by methodSignatureToString(). 68 * Set by side effect,but only internally. 69 */ 70 71 private static boolean wide = false; /* The `WIDE' instruction is used in the 72 * byte code to allow 16-bit wide indices 73 * for local variables. This opcode 74 * precedes an `ILOAD', e.g.. The opcode 75 * immediately following takes an extra 76 * byte which is combined with the 77 * following byte to form a 78 * 16-bit value. 79 */ 80 81 82 /** 83 * Convert bit field of flags into string such as `static final'. 84 * 85 * @param access_flags Access flags 86 * @return String representation of flags 87 */ 88 public static String accessToString(final int access_flags) { 89 return accessToString(access_flags, false); 90 } 91 92 /** 93 * Convert bit field of flags into string such as `static final'. 94 * 95 * Special case: Classes compiled with new compilers and with the 96 * `ACC_SUPER' flag would be said to be "synchronized". This is because SUN 97 * used the same value for the flags `ACC_SUPER' and `ACC_SYNCHRONIZED'. 98 * 99 * @param access_flags Access flags 100 * @param for_class access flags are for class qualifiers ? 101 * @return String representation of flags 102 */ 103 public static String accessToString(final int access_flags, final boolean for_class) { 104 final StringBuilder buf = new StringBuilder(); 105 int p = 0; 106 for (int i = 0; p < Const.MAX_ACC_FLAG; i++) { // Loop through known flags 107 p = pow2(i); 108 if ((access_flags & p) != 0) { 109 /* Special case: Classes compiled with new compilers and with the 110 * `ACC_SUPER' flag would be said to be "synchronized". This is 111 * because SUN used the same value for the flags `ACC_SUPER' and 112 * `ACC_SYNCHRONIZED'. 113 */ 114 if (for_class && ((p == Const.ACC_SUPER) || (p == Const.ACC_INTERFACE))) { 115 continue; 116 } 117 buf.append(Const.getAccessName(i)).append(" "); 118 } 119 } 120 return buf.toString().trim(); 121 } 122 123 /** 124 * @param access_flags the class flags 125 * 126 * @return "class" or "interface", depending on the ACC_INTERFACE flag 127 */ 128 public static String classOrInterface(final int access_flags) { 129 return ((access_flags & Const.ACC_INTERFACE) != 0) ? "interface" : "class"; 130 } 131 132 /** 133 * Disassemble a byte array of JVM byte codes starting from code line 134 * `index' and return the disassembled string representation. Decode only 135 * `num' opcodes (including their operands), use -1 if you want to decompile 136 * everything. 137 * 138 * @param code byte code array 139 * @param constant_pool Array of constants 140 * @param index offset in `code' array 141 * <EM>(number of opcodes, not bytes!)</EM> 142 * @param length number of opcodes to decompile, -1 for all 143 * @param verbose be verbose, e.g. print constant pool index 144 * @return String representation of byte codes 145 */ 146 public static String codeToString(final byte[] code, final ConstantPool constant_pool, 147 final int index, final int length, final boolean verbose) { 148 // Should be sufficient // CHECKSTYLE IGNORE MagicNumber 149 final StringBuilder buf = new StringBuilder(code.length * 20); 150 try (ByteSequence stream = new ByteSequence(code)) { 151 for (int i = 0; i < index; i++) { 152 codeToString(stream, constant_pool, verbose); 153 } 154 for (int i = 0; stream.available() > 0; i++) { 155 if ((length < 0) || (i < length)) { 156 final String indices = fillup(stream.getIndex() + ":", 6, true, ' '); 157 buf.append(indices) 158 .append(codeToString(stream, constant_pool, verbose)) 159 .append('\n'); 160 } 161 } 162 } catch (final IOException e) { 163 throw new ClassFormatException("Byte code error: " + buf.toString(), e); 164 } 165 return buf.toString(); 166 } 167 168 public static String codeToString(final byte[] code, final ConstantPool constant_pool, 169 final int index, final int length) { 170 return codeToString(code, constant_pool, index, length, true); 171 } 172 173 /** 174 * Disassemble a stream of byte codes and return the string representation. 175 * 176 * @param bytes stream of bytes 177 * @param constant_pool Array of constants 178 * @param verbose be verbose, e.g. print constant pool index 179 * @return String representation of byte code 180 * 181 * @throws IOException if a failure from reading from the bytes argument 182 * occurs 183 */ 184 @SuppressWarnings("fallthrough") // by design for case Const.INSTANCEOF 185 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool, 186 final boolean verbose) throws IOException { 187 final short opcode = (short) bytes.readUnsignedByte(); 188 int default_offset = 0; 189 int low; 190 int high; 191 int npairs; 192 int index; 193 int vindex; 194 int constant; 195 int[] match; 196 int[] jump_table; 197 int no_pad_bytes = 0; 198 int offset; 199 final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode)); 200 /* Special case: Skip (0-3) padding bytes, i.e., the 201 * following bytes are 4-byte-aligned 202 */ 203 if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) { 204 final int remainder = bytes.getIndex() % 4; 205 no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder; 206 for (int i = 0; i < no_pad_bytes; i++) { 207 byte b; 208 if ((b = bytes.readByte()) != 0) { 209 System.err.println("Warning: Padding byte != 0 in " 210 + Const.getOpcodeName(opcode) + ":" + b); 211 } 212 } 213 // Both cases have a field default_offset in common 214 default_offset = bytes.readInt(); 215 } 216 switch (opcode) { 217 /* Table switch has variable length arguments. 218 */ 219 case Const.TABLESWITCH: 220 low = bytes.readInt(); 221 high = bytes.readInt(); 222 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 223 default_offset += offset; 224 buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low) 225 .append(", high = ").append(high).append("("); 226 jump_table = new int[high - low + 1]; 227 for (int i = 0; i < jump_table.length; i++) { 228 jump_table[i] = offset + bytes.readInt(); 229 buf.append(jump_table[i]); 230 if (i < jump_table.length - 1) { 231 buf.append(", "); 232 } 233 } 234 buf.append(")"); 235 break; 236 /* Lookup switch has variable length arguments. 237 */ 238 case Const.LOOKUPSWITCH: { 239 npairs = bytes.readInt(); 240 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 241 match = new int[npairs]; 242 jump_table = new int[npairs]; 243 default_offset += offset; 244 buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append( 245 npairs).append(" ("); 246 for (int i = 0; i < npairs; i++) { 247 match[i] = bytes.readInt(); 248 jump_table[i] = offset + bytes.readInt(); 249 buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")"); 250 if (i < npairs - 1) { 251 buf.append(", "); 252 } 253 } 254 buf.append(")"); 255 } 256 break; 257 /* Two address bytes + offset from start of byte stream form the 258 * jump target 259 */ 260 case Const.GOTO: 261 case Const.IFEQ: 262 case Const.IFGE: 263 case Const.IFGT: 264 case Const.IFLE: 265 case Const.IFLT: 266 case Const.JSR: 267 case Const.IFNE: 268 case Const.IFNONNULL: 269 case Const.IFNULL: 270 case Const.IF_ACMPEQ: 271 case Const.IF_ACMPNE: 272 case Const.IF_ICMPEQ: 273 case Const.IF_ICMPGE: 274 case Const.IF_ICMPGT: 275 case Const.IF_ICMPLE: 276 case Const.IF_ICMPLT: 277 case Const.IF_ICMPNE: 278 buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readShort()); 279 break; 280 /* 32-bit wide jumps 281 */ 282 case Const.GOTO_W: 283 case Const.JSR_W: 284 buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readInt()); 285 break; 286 /* Index byte references local variable (register) 287 */ 288 case Const.ALOAD: 289 case Const.ASTORE: 290 case Const.DLOAD: 291 case Const.DSTORE: 292 case Const.FLOAD: 293 case Const.FSTORE: 294 case Const.ILOAD: 295 case Const.ISTORE: 296 case Const.LLOAD: 297 case Const.LSTORE: 298 case Const.RET: 299 if (wide) { 300 vindex = bytes.readUnsignedShort(); 301 wide = false; // Clear flag 302 } else { 303 vindex = bytes.readUnsignedByte(); 304 } 305 buf.append("\t\t%").append(vindex); 306 break; 307 /* 308 * Remember wide byte which is used to form a 16-bit address in the 309 * following instruction. Relies on that the method is called again with 310 * the following opcode. 311 */ 312 case Const.WIDE: 313 wide = true; 314 buf.append("\t(wide)"); 315 break; 316 /* Array of basic type. 317 */ 318 case Const.NEWARRAY: 319 buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">"); 320 break; 321 /* Access object/class fields. 322 */ 323 case Const.GETFIELD: 324 case Const.GETSTATIC: 325 case Const.PUTFIELD: 326 case Const.PUTSTATIC: 327 index = bytes.readUnsignedShort(); 328 buf.append("\t\t").append( 329 constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append( 330 verbose ? " (" + index + ")" : ""); 331 break; 332 /* Operands are references to classes in constant pool 333 */ 334 case Const.NEW: 335 case Const.CHECKCAST: 336 buf.append("\t"); 337 //$FALL-THROUGH$ 338 case Const.INSTANCEOF: 339 index = bytes.readUnsignedShort(); 340 buf.append("\t<").append( 341 constant_pool.constantToString(index, Const.CONSTANT_Class)) 342 .append(">").append(verbose ? " (" + index + ")" : ""); 343 break; 344 /* Operands are references to methods in constant pool 345 */ 346 case Const.INVOKESPECIAL: 347 case Const.INVOKESTATIC: 348 index = bytes.readUnsignedShort(); 349 final Constant c = constant_pool.getConstant(index); 350 // With Java8 operand may be either a CONSTANT_Methodref 351 // or a CONSTANT_InterfaceMethodref. (markro) 352 buf.append("\t").append( 353 constant_pool.constantToString(index, c.getTag())) 354 .append(verbose ? " (" + index + ")" : ""); 355 break; 356 case Const.INVOKEVIRTUAL: 357 index = bytes.readUnsignedShort(); 358 buf.append("\t").append( 359 constant_pool.constantToString(index, Const.CONSTANT_Methodref)) 360 .append(verbose ? " (" + index + ")" : ""); 361 break; 362 case Const.INVOKEINTERFACE: 363 index = bytes.readUnsignedShort(); 364 final int nargs = bytes.readUnsignedByte(); // historical, redundant 365 buf.append("\t").append( 366 constant_pool 367 .constantToString(index, Const.CONSTANT_InterfaceMethodref)) 368 .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t") 369 .append(bytes.readUnsignedByte()); // Last byte is a reserved space 370 break; 371 case Const.INVOKEDYNAMIC: 372 index = bytes.readUnsignedShort(); 373 buf.append("\t").append( 374 constant_pool 375 .constantToString(index, Const.CONSTANT_InvokeDynamic)) 376 .append(verbose ? " (" + index + ")\t" : "") 377 .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space 378 .append(bytes.readUnsignedByte()); // Last byte is a reserved space 379 break; 380 /* Operands are references to items in constant pool 381 */ 382 case Const.LDC_W: 383 case Const.LDC2_W: 384 index = bytes.readUnsignedShort(); 385 buf.append("\t\t").append( 386 constant_pool.constantToString(index, constant_pool.getConstant(index) 387 .getTag())).append(verbose ? " (" + index + ")" : ""); 388 break; 389 case Const.LDC: 390 index = bytes.readUnsignedByte(); 391 buf.append("\t\t").append( 392 constant_pool.constantToString(index, constant_pool.getConstant(index) 393 .getTag())).append(verbose ? " (" + index + ")" : ""); 394 break; 395 /* Array of references. 396 */ 397 case Const.ANEWARRAY: 398 index = bytes.readUnsignedShort(); 399 buf.append("\t\t<").append( 400 compactClassName(constant_pool.getConstantString(index, 401 Const.CONSTANT_Class), false)).append(">").append( 402 verbose ? " (" + index + ")" : ""); 403 break; 404 /* Multidimensional array of references. 405 */ 406 case Const.MULTIANEWARRAY: { 407 index = bytes.readUnsignedShort(); 408 final int dimensions = bytes.readUnsignedByte(); 409 buf.append("\t<").append( 410 compactClassName(constant_pool.getConstantString(index, 411 Const.CONSTANT_Class), false)).append(">\t").append(dimensions) 412 .append(verbose ? " (" + index + ")" : ""); 413 } 414 break; 415 /* Increment local variable. 416 */ 417 case Const.IINC: 418 if (wide) { 419 vindex = bytes.readUnsignedShort(); 420 constant = bytes.readShort(); 421 wide = false; 422 } else { 423 vindex = bytes.readUnsignedByte(); 424 constant = bytes.readByte(); 425 } 426 buf.append("\t\t%").append(vindex).append("\t").append(constant); 427 break; 428 default: 429 if (Const.getNoOfOperands(opcode) > 0) { 430 for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) { 431 buf.append("\t\t"); 432 switch (Const.getOperandType(opcode, i)) { 433 case Const.T_BYTE: 434 buf.append(bytes.readByte()); 435 break; 436 case Const.T_SHORT: 437 buf.append(bytes.readShort()); 438 break; 439 case Const.T_INT: 440 buf.append(bytes.readInt()); 441 break; 442 default: // Never reached 443 throw new IllegalStateException("Unreachable default case reached!"); 444 } 445 } 446 } 447 } 448 return buf.toString(); 449 } 450 451 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool) 452 throws IOException { 453 return codeToString(bytes, constant_pool, true); 454 } 455 456 /** 457 * Shorten long class names, <em>java/lang/String</em> becomes 458 * <em>String</em>. 459 * 460 * @param str The long class name 461 * @return Compacted class name 462 */ 463 public static String compactClassName(final String str) { 464 return compactClassName(str, true); 465 } 466 467 /** 468 * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>, 469 * if the class name starts with this string and the flag <em>chopit</em> is 470 * true. Slashes <em>/</em> are converted to dots <em>.</em>. 471 * 472 * @param str The long class name 473 * @param prefix The prefix the get rid off 474 * @param chopit Flag that determines whether chopping is executed or not 475 * @return Compacted class name 476 */ 477 public static String compactClassName(String str, final String prefix, final boolean chopit) { 478 final int len = prefix.length(); 479 str = str.replace('/', '.'); // Is `/' on all systems, even DOS 480 if (chopit) { 481 // If string starts with `prefix' and contains no further dots 482 if (str.startsWith(prefix) && (str.substring(len).indexOf('.') == -1)) { 483 str = str.substring(len); 484 } 485 } 486 return str; 487 } 488 489 /** 490 * Shorten long class names, <em>java/lang/String</em> becomes 491 * <em>java.lang.String</em>, e.g.. If <em>chopit</em> is <em>true</em> the 492 * prefix <em>java.lang</em> 493 * is also removed. 494 * 495 * @param str The long class name 496 * @param chopit Flag that determines whether chopping is executed or not 497 * @return Compacted class name 498 */ 499 public static String compactClassName(final String str, final boolean chopit) { 500 return compactClassName(str, "java.lang.", chopit); 501 } 502 503 /** 504 * @return `flag' with bit `i' set to 1 505 */ 506 public static int setBit(final int flag, final int i) { 507 return flag | pow2(i); 508 } 509 510 /** 511 * @return `flag' with bit `i' set to 0 512 */ 513 public static int clearBit(final int flag, final int i) { 514 final int bit = pow2(i); 515 return (flag & bit) == 0 ? flag : flag ^ bit; 516 } 517 518 /** 519 * @return true, if bit `i' in `flag' is set 520 */ 521 public static boolean isSet(final int flag, final int i) { 522 return (flag & pow2(i)) != 0; 523 } 524 525 /** 526 * Converts string containing the method return and argument types to a byte 527 * code method signature. 528 * 529 * @param ret Return type of method 530 * @param argv Types of method arguments 531 * @return Byte code representation of method signature 532 * 533 * @throws ClassFormatException if the signature is for Void 534 */ 535 public static String methodTypeToSignature(final String ret, final String[] argv) 536 throws ClassFormatException { 537 final StringBuilder buf = new StringBuilder("("); 538 String str; 539 if (argv != null) { 540 for (final String element : argv) { 541 str = getSignature(element); 542 if (str.endsWith("V")) { 543 throw new ClassFormatException("Invalid type: " + element); 544 } 545 buf.append(str); 546 } 547 } 548 str = getSignature(ret); 549 buf.append(")").append(str); 550 return buf.toString(); 551 } 552 553 /** 554 * @param signature Method signature 555 * @return Array of argument types 556 * @throws ClassFormatException 557 */ 558 public static String[] methodSignatureArgumentTypes(final String signature) 559 throws ClassFormatException { 560 return methodSignatureArgumentTypes(signature, true); 561 } 562 563 /** 564 * @param signature Method signature 565 * @param chopit Shorten class names ? 566 * @return Array of argument types 567 * @throws ClassFormatException 568 */ 569 public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit) 570 throws ClassFormatException { 571 final List<String> vec = new ArrayList<>(); 572 int index; 573 try { // Read all declarations between for `(' and `)' 574 if (signature.charAt(0) != '(') { 575 throw new ClassFormatException("Invalid method signature: " + signature); 576 } 577 index = 1; // current string position 578 while (signature.charAt(index) != ')') { 579 vec.add(signatureToString(signature.substring(index), chopit)); 580 //corrected concurrent private static field acess 581 index += unwrap(consumed_chars); // update position 582 } 583 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 584 throw new ClassFormatException("Invalid method signature: " + signature, e); 585 } 586 return vec.toArray(new String[vec.size()]); 587 } 588 589 /** 590 * @param signature Method signature 591 * @return return type of method 592 * @throws ClassFormatException 593 */ 594 public static String methodSignatureReturnType(final String signature) 595 throws ClassFormatException { 596 return methodSignatureReturnType(signature, true); 597 } 598 599 /** 600 * @param signature Method signature 601 * @param chopit Shorten class names ? 602 * @return return type of method 603 * @throws ClassFormatException 604 */ 605 public static String methodSignatureReturnType(final String signature, 606 final boolean chopit) throws ClassFormatException { 607 int index; 608 String type; 609 try { 610 // Read return type after `)' 611 index = signature.lastIndexOf(')') + 1; 612 type = signatureToString(signature.substring(index), chopit); 613 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 614 throw new ClassFormatException("Invalid method signature: " + signature, e); 615 } 616 return type; 617 } 618 619 /** 620 * Converts method signature to string with all class names compacted. 621 * 622 * @param signature to convert 623 * @param name of method 624 * @param access flags of method 625 * @return Human readable signature 626 */ 627 public static String methodSignatureToString(final String signature, 628 final String name, final String access) { 629 return methodSignatureToString(signature, name, access, true); 630 } 631 632 public static String methodSignatureToString(final String signature, 633 final String name, final String access, final boolean chopit) { 634 return methodSignatureToString(signature, name, access, chopit, null); 635 } 636 637 /** 638 * A returntype signature represents the return value from a method. It is a 639 * series of bytes in the following grammar: 640 * 641 * <pre> 642 * <return_signature> ::= <field_type> | V 643 * </pre> 644 * 645 * The character V indicates that the method returns no value. Otherwise, 646 * the signature indicates the type of the return value. An argument 647 * signature represents an argument passed to a method: 648 * 649 * <pre> 650 * <argument_signature> ::= <field_type> 651 * </pre> 652 * 653 * A method signature represents the arguments that the method expects, and 654 * the value that it returns. 655 * <pre> 656 * <method_signature> ::= (<arguments_signature>) <return_signature> 657 * <arguments_signature>::= <argument_signature>* 658 * </pre> 659 * 660 * This method converts such a string into a Java type declaration like 661 * `void main(String[])' and throws a `ClassFormatException' when the parsed 662 * type is invalid. 663 * 664 * @param signature Method signature 665 * @param name Method name 666 * @param access Method access rights 667 * @param chopit 668 * @param vars 669 * @return Java type declaration 670 * @throws ClassFormatException 671 */ 672 public static String methodSignatureToString(final String signature, final String name, 673 final String access, final boolean chopit, final LocalVariableTable vars) 674 throws ClassFormatException { 675 final StringBuilder buf = new StringBuilder("("); 676 String type; 677 int index; 678 int var_index = access.contains("static") ? 0 : 1; 679 try { // Read all declarations between for `(' and `)' 680 if (signature.charAt(0) != '(') { 681 throw new ClassFormatException("Invalid method signature: " + signature); 682 } 683 index = 1; // current string position 684 while (signature.charAt(index) != ')') { 685 final String param_type = signatureToString(signature.substring(index), chopit); 686 buf.append(param_type); 687 if (vars != null) { 688 final LocalVariable l = vars.getLocalVariable(var_index, 0); 689 if (l != null) { 690 buf.append(" ").append(l.getName()); 691 } 692 } else { 693 buf.append(" arg").append(var_index); 694 } 695 if ("double".equals(param_type) || "long".equals(param_type)) { 696 var_index += 2; 697 } else { 698 var_index++; 699 } 700 buf.append(", "); 701 //corrected concurrent private static field acess 702 index += unwrap(consumed_chars); // update position 703 } 704 index++; // update position 705 // Read return type after `)' 706 type = signatureToString(signature.substring(index), chopit); 707 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 708 throw new ClassFormatException("Invalid method signature: " + signature, e); 709 } 710 if (buf.length() > 1) { 711 buf.setLength(buf.length() - 2); 712 } 713 buf.append(")"); 714 return access + ((access.length() > 0) ? " " : "") + // May be an empty string 715 type + " " + name + buf.toString(); 716 } 717 718 // Guess what this does 719 private static int pow2(final int n) { 720 return 1 << n; 721 } 722 723 /** 724 * Replace all occurrences of <em>old</em> in <em>str</em> with 725 * <em>new</em>. 726 * 727 * @param str String to permute 728 * @param old String to be replaced 729 * @param new_ Replacement string 730 * @return new String object 731 */ 732 public static String replace(String str, final String old, final String new_) { 733 int index; 734 int old_index; 735 try { 736 if (str.contains(old)) { // `old' found in str 737 final StringBuilder buf = new StringBuilder(); 738 old_index = 0; // String start offset 739 // While we have something to replace 740 while ((index = str.indexOf(old, old_index)) != -1) { 741 buf.append(str.substring(old_index, index)); // append prefix 742 buf.append(new_); // append replacement 743 old_index = index + old.length(); // Skip `old'.length chars 744 } 745 buf.append(str.substring(old_index)); // append rest of string 746 str = buf.toString(); 747 } 748 } catch (final StringIndexOutOfBoundsException e) { // Should not occur 749 System.err.println(e); 750 } 751 return str; 752 } 753 754 /** 755 * Converts signature to string with all class names compacted. 756 * 757 * @param signature to convert 758 * @return Human readable signature 759 */ 760 public static String signatureToString(final String signature) { 761 return signatureToString(signature, true); 762 } 763 764 /** 765 * The field signature represents the value of an argument to a function or 766 * the value of a variable. It is a series of bytes generated by the 767 * following grammar: 768 * 769 * <PRE> 770 * <field_signature> ::= <field_type> 771 * <field_type> ::= <base_type>|<object_type>|<array_type> 772 * <base_type> ::= B|C|D|F|I|J|S|Z 773 * <object_type> ::= L<fullclassname>; 774 * <array_type> ::= [<field_type> 775 * 776 * The meaning of the base types is as follows: 777 * B byte signed byte 778 * C char character 779 * D double double precision IEEE float 780 * F float single precision IEEE float 781 * I int integer 782 * J long long integer 783 * L<fullclassname>; ... an object of the given class 784 * S short signed short 785 * Z boolean true or false 786 * [<field sig> ... array 787 * </PRE> 788 * 789 * This method converts this string into a Java type declaration such as 790 * `String[]' and throws a `ClassFormatException' when the parsed type is 791 * invalid. 792 * 793 * @param signature Class signature 794 * @param chopit Flag that determines whether chopping is executed or not 795 * @return Java type declaration 796 * @throws ClassFormatException 797 */ 798 public static String signatureToString(final String signature, final boolean chopit) { 799 //corrected concurrent private static field acess 800 wrap(consumed_chars, 1); // This is the default, read just one char like `B' 801 try { 802 switch (signature.charAt(0)) { 803 case 'B': 804 return "byte"; 805 case 'C': 806 return "char"; 807 case 'D': 808 return "double"; 809 case 'F': 810 return "float"; 811 case 'I': 812 return "int"; 813 case 'J': 814 return "long"; 815 case 'T': { // TypeVariableSignature 816 final int index = signature.indexOf(';'); // Look for closing `;' 817 if (index < 0) { 818 throw new ClassFormatException("Invalid signature: " + signature); 819 } 820 //corrected concurrent private static field acess 821 wrap(consumed_chars, index + 1); // "Tblabla;" `T' and `;' are removed 822 return compactClassName(signature.substring(1, index), chopit); 823 } 824 case 'L': { // Full class name 825 // should this be a while loop? can there be more than 826 // one generic clause? (markro) 827 int fromIndex = signature.indexOf('<'); // generic type? 828 if (fromIndex < 0) { 829 fromIndex = 0; 830 } else { 831 fromIndex = signature.indexOf('>', fromIndex); 832 if (fromIndex < 0) { 833 throw new ClassFormatException("Invalid signature: " + signature); 834 } 835 } 836 final int index = signature.indexOf(';', fromIndex); // Look for closing `;' 837 if (index < 0) { 838 throw new ClassFormatException("Invalid signature: " + signature); 839 } 840 // check to see if there are any TypeArguments 841 final int bracketIndex = signature.substring(0, index).indexOf('<'); 842 if (bracketIndex < 0) { 843 // just a class identifier 844 wrap(consumed_chars, index + 1); // "Lblabla;" `L' and `;' are removed 845 return compactClassName(signature.substring(1, index), chopit); 846 } 847 848 // we have TypeArguments; build up partial result 849 // as we recurse for each TypeArgument 850 final StringBuilder type = new StringBuilder( 851 compactClassName(signature.substring(1, bracketIndex), chopit)) 852 .append("<"); 853 int consumed_chars = bracketIndex + 1; // Shadows global var 854 855 // check for wildcards 856 if (signature.charAt(consumed_chars) == '+') { 857 type.append("? extends "); 858 consumed_chars++; 859 } else if (signature.charAt(consumed_chars) == '-') { 860 type.append("? super "); 861 consumed_chars++; 862 } else if (signature.charAt(consumed_chars) == '*') { 863 // must be at end of signature 864 if (signature.charAt(consumed_chars + 1) != '>') { 865 throw new ClassFormatException("Invalid signature: " + signature); 866 } 867 if (signature.charAt(consumed_chars + 2) != ';') { 868 throw new ClassFormatException("Invalid signature: " + signature); 869 } 870 wrap(Utility.consumed_chars, consumed_chars + 3); // remove final "*>;" 871 return type + "?>..."; 872 } 873 874 // get the first TypeArgument 875 type.append(signatureToString(signature.substring(consumed_chars), chopit)); 876 // update our consumed count by the number of characters the for type argument 877 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; 878 wrap(Utility.consumed_chars, consumed_chars); 879 880 // are there more TypeArguments? 881 while (signature.charAt(consumed_chars) != '>') { 882 type.append(", ").append(signatureToString(signature.substring(consumed_chars), chopit)); 883 // update our consumed count by the number of characters the for type argument 884 consumed_chars = unwrap(Utility.consumed_chars) + consumed_chars; 885 wrap(Utility.consumed_chars, consumed_chars); 886 } 887 888 if (signature.charAt(consumed_chars + 1) != ';') { 889 throw new ClassFormatException("Invalid signature: " + signature); 890 } 891 wrap(Utility.consumed_chars, consumed_chars + 2); // remove final ">;" 892 return type.append(">").toString(); 893 } 894 case 'S': 895 return "short"; 896 case 'Z': 897 return "boolean"; 898 case '[': { // Array declaration 899 int n; 900 StringBuilder brackets; 901 String type; 902 int consumed_chars; // Shadows global var 903 brackets = new StringBuilder(); // Accumulate []'s 904 // Count opening brackets and look for optional size argument 905 for (n = 0; signature.charAt(n) == '['; n++) { 906 brackets.append("[]"); 907 } 908 consumed_chars = n; // Remember value 909 // The rest of the string denotes a `<field_type>' 910 type = signatureToString(signature.substring(n), chopit); 911 //corrected concurrent private static field acess 912 //Utility.consumed_chars += consumed_chars; is replaced by: 913 final int _temp = unwrap(Utility.consumed_chars) + consumed_chars; 914 wrap(Utility.consumed_chars, _temp); 915 return type + brackets.toString(); 916 } 917 case 'V': 918 return "void"; 919 default: 920 throw new ClassFormatException("Invalid signature: `" + signature + "'"); 921 } 922 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 923 throw new ClassFormatException("Invalid signature: " + signature, e); 924 } 925 } 926 927 /** 928 * Parse Java type such as "char", or "java.lang.String[]" and return the 929 * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" 930 * respectively. 931 * 932 * @param type Java type 933 * @return byte code signature 934 */ 935 public static String getSignature(String type) { 936 final StringBuilder buf = new StringBuilder(); 937 final char[] chars = type.toCharArray(); 938 boolean char_found = false; 939 boolean delim = false; 940 int index = -1; 941 loop: 942 for (int i = 0; i < chars.length; i++) { 943 switch (chars[i]) { 944 case ' ': 945 case '\t': 946 case '\n': 947 case '\r': 948 case '\f': 949 if (char_found) { 950 delim = true; 951 } 952 break; 953 case '[': 954 if (!char_found) { 955 throw new RuntimeException("Illegal type: " + type); 956 } 957 index = i; 958 break loop; 959 default: 960 char_found = true; 961 if (!delim) { 962 buf.append(chars[i]); 963 } 964 } 965 } 966 int brackets = 0; 967 if (index > 0) { 968 brackets = countBrackets(type.substring(index)); 969 } 970 type = buf.toString(); 971 buf.setLength(0); 972 for (int i = 0; i < brackets; i++) { 973 buf.append('['); 974 } 975 boolean found = false; 976 for (int i = Const.T_BOOLEAN; (i <= Const.T_VOID) && !found; i++) { 977 if (Const.getTypeName(i).equals(type)) { 978 found = true; 979 buf.append(Const.getShortTypeName(i)); 980 } 981 } 982 if (!found) { 983 buf.append('L').append(type.replace('.', '/')).append(';'); 984 } 985 return buf.toString(); 986 } 987 988 private static int countBrackets(final String brackets) { 989 final char[] chars = brackets.toCharArray(); 990 int count = 0; 991 boolean open = false; 992 for (final char c : chars) { 993 switch (c) { 994 case '[': 995 if (open) { 996 throw new RuntimeException("Illegally nested brackets:" + brackets); 997 } 998 open = true; 999 break; 1000 case ']': 1001 if (!open) { 1002 throw new RuntimeException("Illegally nested brackets:" + brackets); 1003 } 1004 open = false; 1005 count++; 1006 break; 1007 default: 1008 // Don't care 1009 break; 1010 } 1011 } 1012 if (open) { 1013 throw new RuntimeException("Illegally nested brackets:" + brackets); 1014 } 1015 return count; 1016 } 1017 1018 /** 1019 * Return type of method signature as a byte value as defined in 1020 * <em>Constants</em> 1021 * 1022 * @param signature in format described above 1023 * @return type of method signature 1024 * @see Const 1025 * 1026 * @throws ClassFormatException if signature is not a method signature 1027 */ 1028 public static byte typeOfMethodSignature(final String signature) throws ClassFormatException { 1029 int index; 1030 try { 1031 if (signature.charAt(0) != '(') { 1032 throw new ClassFormatException("Invalid method signature: " + signature); 1033 } 1034 index = signature.lastIndexOf(')') + 1; 1035 return typeOfSignature(signature.substring(index)); 1036 } catch (final StringIndexOutOfBoundsException e) { 1037 throw new ClassFormatException("Invalid method signature: " + signature, e); 1038 } 1039 } 1040 1041 /** 1042 * Return type of signature as a byte value as defined in <em>Constants</em> 1043 * 1044 * @param signature in format described above 1045 * @return type of signature 1046 * @see Const 1047 * 1048 * @throws ClassFormatException if signature isn't a known type 1049 */ 1050 public static byte typeOfSignature(final String signature) throws ClassFormatException { 1051 try { 1052 switch (signature.charAt(0)) { 1053 case 'B': 1054 return Const.T_BYTE; 1055 case 'C': 1056 return Const.T_CHAR; 1057 case 'D': 1058 return Const.T_DOUBLE; 1059 case 'F': 1060 return Const.T_FLOAT; 1061 case 'I': 1062 return Const.T_INT; 1063 case 'J': 1064 return Const.T_LONG; 1065 case 'L': 1066 case 'T': 1067 return Const.T_REFERENCE; 1068 case '[': 1069 return Const.T_ARRAY; 1070 case 'V': 1071 return Const.T_VOID; 1072 case 'Z': 1073 return Const.T_BOOLEAN; 1074 case 'S': 1075 return Const.T_SHORT; 1076 case '!': 1077 case '+': 1078 case '*': 1079 return typeOfSignature(signature.substring(1)); 1080 default: 1081 throw new ClassFormatException("Invalid method signature: " + signature); 1082 } 1083 } catch (final StringIndexOutOfBoundsException e) { 1084 throw new ClassFormatException("Invalid method signature: " + signature, e); 1085 } 1086 } 1087 1088 /** 1089 * Map opcode names to opcode numbers. E.g., return Constants.ALOAD for 1090 * "aload" 1091 */ 1092 public static short searchOpcode(String name) { 1093 name = name.toLowerCase(Locale.ENGLISH); 1094 for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) { 1095 if (Const.getOpcodeName(i).equals(name)) { 1096 return i; 1097 } 1098 } 1099 return -1; 1100 } 1101 1102 /** 1103 * Convert (signed) byte to (unsigned) short value, i.e., all negative 1104 * values become positive. 1105 */ 1106 private static short byteToShort(final byte b) { 1107 return (b < 0) ? (short) (256 + b) : (short) b; 1108 } 1109 1110 /** 1111 * Convert bytes into hexadecimal string 1112 * 1113 * @param bytes an array of bytes to convert to hexadecimal 1114 * 1115 * @return bytes as hexadecimal string, e.g. 00 fa 12 ... 1116 */ 1117 public static String toHexString(final byte[] bytes) { 1118 final StringBuilder buf = new StringBuilder(); 1119 for (int i = 0; i < bytes.length; i++) { 1120 final short b = byteToShort(bytes[i]); 1121 final String hex = Integer.toHexString(b); 1122 if (b < 0x10) { 1123 buf.append('0'); 1124 } 1125 buf.append(hex); 1126 if (i < bytes.length - 1) { 1127 buf.append(' '); 1128 } 1129 } 1130 return buf.toString(); 1131 } 1132 1133 /** 1134 * Return a string for an integer justified left or right and filled up with 1135 * `fill' characters if necessary. 1136 * 1137 * @param i integer to format 1138 * @param length length of desired string 1139 * @param left_justify format left or right 1140 * @param fill fill character 1141 * @return formatted int 1142 */ 1143 public static String format(final int i, final int length, 1144 final boolean left_justify, final char fill) { 1145 return fillup(Integer.toString(i), length, left_justify, fill); 1146 } 1147 1148 /** 1149 * Fillup char with up to length characters with char `fill' and justify it 1150 * left or right. 1151 * 1152 * @param str string to format 1153 * @param length length of desired string 1154 * @param left_justify format left or right 1155 * @param fill fill character 1156 * @return formatted string 1157 */ 1158 public static String fillup(final String str, final int length, 1159 final boolean left_justify, final char fill) { 1160 final int len = length - str.length(); 1161 final char[] buf = new char[(len < 0) ? 0 : len]; 1162 for (int j = 0; j < buf.length; j++) { 1163 buf[j] = fill; 1164 } 1165 if (left_justify) { 1166 return str + new String(buf); 1167 } 1168 return new String(buf) + str; 1169 } 1170 1171 static boolean equals(final byte[] a, final byte[] b) { 1172 int size; 1173 if ((size = a.length) != b.length) { 1174 return false; 1175 } 1176 for (int i = 0; i < size; i++) { 1177 if (a[i] != b[i]) { 1178 return false; 1179 } 1180 } 1181 return true; 1182 } 1183 1184 public static void printArray(final PrintStream out, final Object[] obj) { 1185 out.println(printArray(obj, true)); 1186 } 1187 1188 public static void printArray(final PrintWriter out, final Object[] obj) { 1189 out.println(printArray(obj, true)); 1190 } 1191 1192 public static String printArray(final Object[] obj) { 1193 return printArray(obj, true); 1194 } 1195 1196 public static String printArray(final Object[] obj, final boolean braces) { 1197 return printArray(obj, braces, false); 1198 } 1199 1200 public static String printArray(final Object[] obj, final boolean braces, final boolean quote) { 1201 if (obj == null) { 1202 return null; 1203 } 1204 final StringBuilder buf = new StringBuilder(); 1205 if (braces) { 1206 buf.append('{'); 1207 } 1208 for (int i = 0; i < obj.length; i++) { 1209 if (obj[i] != null) { 1210 buf.append(quote ? "\"" : "").append(obj[i]).append(quote ? "\"" : ""); 1211 } else { 1212 buf.append("null"); 1213 } 1214 if (i < obj.length - 1) { 1215 buf.append(", "); 1216 } 1217 } 1218 if (braces) { 1219 buf.append('}'); 1220 } 1221 return buf.toString(); 1222 } 1223 1224 /** 1225 * @param ch the character to test if it's part of an identifier 1226 * 1227 * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _) 1228 */ 1229 public static boolean isJavaIdentifierPart(final char ch) { 1230 return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) 1231 || ((ch >= '0') && (ch <= '9')) || (ch == '_'); 1232 } 1233 1234 /** 1235 * Encode byte array it into Java identifier string, i.e., a string that 1236 * only contains the following characters: (a, ... z, A, ... Z, 0, ... 9, _, 1237 * $). The encoding algorithm itself is not too clever: if the current 1238 * byte's ASCII value already is a valid Java identifier part, leave it as 1239 * it is. Otherwise it writes the escape character($) followed by: 1240 * 1241 * <ul> 1242 * <li> the ASCII value as a hexadecimal string, if the value is not in the 1243 * range 200..247</li> 1244 * <li>a Java identifier char not used in a lowercase hexadecimal string, if 1245 * the value is in the range 200..247</li> 1246 * </ul> 1247 * 1248 * <p> 1249 * This operation inflates the original byte array by roughly 40-50%</p> 1250 * 1251 * @param bytes the byte array to convert 1252 * @param compress use gzip to minimize string 1253 * 1254 * @throws IOException if there's a gzip exception 1255 */ 1256 public static String encode(byte[] bytes, final boolean compress) throws IOException { 1257 if (compress) { 1258 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1259 GZIPOutputStream gos = new GZIPOutputStream(baos)) { 1260 gos.write(bytes, 0, bytes.length); 1261 bytes = baos.toByteArray(); 1262 } 1263 } 1264 final CharArrayWriter caw = new CharArrayWriter(); 1265 try (JavaWriter jw = new JavaWriter(caw)) { 1266 for (final byte b : bytes) { 1267 final int in = b & 0x000000ff; // Normalize to unsigned 1268 jw.write(in); 1269 } 1270 } 1271 return caw.toString(); 1272 } 1273 1274 /** 1275 * Decode a string back to a byte array. 1276 * 1277 * @param s the string to convert 1278 * @param uncompress use gzip to uncompress the stream of bytes 1279 * 1280 * @throws IOException if there's a gzip exception 1281 */ 1282 public static byte[] decode(final String s, final boolean uncompress) throws IOException { 1283 byte[] bytes; 1284 try (JavaReader jr = new JavaReader(new CharArrayReader(s.toCharArray())); 1285 ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 1286 int ch; 1287 while ((ch = jr.read()) >= 0) { 1288 bos.write(ch); 1289 } 1290 bytes = bos.toByteArray(); 1291 } 1292 if (uncompress) { 1293 final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes)); 1294 final byte[] tmp = new byte[bytes.length * 3]; // Rough estimate 1295 int count = 0; 1296 int b; 1297 while ((b = gis.read()) >= 0) { 1298 tmp[count++] = (byte) b; 1299 } 1300 bytes = new byte[count]; 1301 System.arraycopy(tmp, 0, bytes, 0, count); 1302 } 1303 return bytes; 1304 } 1305 1306 // A-Z, g-z, _, $ 1307 private static final int FREE_CHARS = 48; 1308 private static int[] CHAR_MAP = new int[FREE_CHARS]; 1309 private static int[] MAP_CHAR = new int[256]; // Reverse map 1310 private static final char ESCAPE_CHAR = '$'; 1311 1312 static { 1313 int j = 0; 1314 for (int i = 'A'; i <= 'Z'; i++) { 1315 CHAR_MAP[j] = i; 1316 MAP_CHAR[i] = j; 1317 j++; 1318 } 1319 for (int i = 'g'; i <= 'z'; i++) { 1320 CHAR_MAP[j] = i; 1321 MAP_CHAR[i] = j; 1322 j++; 1323 } 1324 CHAR_MAP[j] = '$'; 1325 MAP_CHAR['$'] = j; 1326 j++; 1327 CHAR_MAP[j] = '_'; 1328 MAP_CHAR['_'] = j; 1329 } 1330 1331 /** 1332 * Decode characters into bytes. Used by <a 1333 * href="Utility.html#decode(java.lang.String, boolean)">decode()</a> 1334 */ 1335 private static class JavaReader extends FilterReader { 1336 1337 public JavaReader(final Reader in) { 1338 super(in); 1339 } 1340 1341 @Override 1342 public int read() throws IOException { 1343 final int b = in.read(); 1344 if (b != ESCAPE_CHAR) { 1345 return b; 1346 } 1347 final int i = in.read(); 1348 if (i < 0) { 1349 return -1; 1350 } 1351 if (((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape 1352 final int j = in.read(); 1353 if (j < 0) { 1354 return -1; 1355 } 1356 final char[] tmp = { 1357 (char) i, (char) j 1358 }; 1359 final int s = Integer.parseInt(new String(tmp), 16); 1360 return s; 1361 } 1362 return MAP_CHAR[i]; 1363 } 1364 1365 @Override 1366 public int read(final char[] cbuf, final int off, final int len) throws IOException { 1367 for (int i = 0; i < len; i++) { 1368 cbuf[off + i] = (char) read(); 1369 } 1370 return len; 1371 } 1372 } 1373 1374 /** 1375 * Encode bytes into valid java identifier characters. Used by <a 1376 * href="Utility.html#encode(byte[], boolean)">encode()</a> 1377 */ 1378 private static class JavaWriter extends FilterWriter { 1379 1380 public JavaWriter(final Writer out) { 1381 super(out); 1382 } 1383 1384 @Override 1385 public void write(final int b) throws IOException { 1386 if (isJavaIdentifierPart((char) b) && (b != ESCAPE_CHAR)) { 1387 out.write(b); 1388 } else { 1389 out.write(ESCAPE_CHAR); // Escape character 1390 // Special escape 1391 if (b >= 0 && b < FREE_CHARS) { 1392 out.write(CHAR_MAP[b]); 1393 } else { // Normal escape 1394 final char[] tmp = Integer.toHexString(b).toCharArray(); 1395 if (tmp.length == 1) { 1396 out.write('0'); 1397 out.write(tmp[0]); 1398 } else { 1399 out.write(tmp[0]); 1400 out.write(tmp[1]); 1401 } 1402 } 1403 } 1404 } 1405 1406 @Override 1407 public void write(final char[] cbuf, final int off, final int len) throws IOException { 1408 for (int i = 0; i < len; i++) { 1409 write(cbuf[off + i]); 1410 } 1411 } 1412 1413 @Override 1414 public void write(final String str, final int off, final int len) throws IOException { 1415 write(str.toCharArray(), off, len); 1416 } 1417 } 1418 1419 /** 1420 * Escape all occurences of newline chars '\n', quotes \", etc. 1421 */ 1422 public static String convertString(final String label) { 1423 final char[] ch = label.toCharArray(); 1424 final StringBuilder buf = new StringBuilder(); 1425 for (final char element : ch) { 1426 switch (element) { 1427 case '\n': 1428 buf.append("\\n"); 1429 break; 1430 case '\r': 1431 buf.append("\\r"); 1432 break; 1433 case '\"': 1434 buf.append("\\\""); 1435 break; 1436 case '\'': 1437 buf.append("\\'"); 1438 break; 1439 case '\\': 1440 buf.append("\\\\"); 1441 break; 1442 default: 1443 buf.append(element); 1444 break; 1445 } 1446 } 1447 return buf.toString(); 1448 } 1449 }