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