1. package com.sun.org.apache.bcel.internal.classfile;
  2. /* ====================================================================
  3. * The Apache Software License, Version 1.1
  4. *
  5. * Copyright (c) 2001 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Apache" and "Apache Software Foundation" and
  28. * "Apache BCEL" must not be used to endorse or promote products
  29. * derived from this software without prior written permission. For
  30. * written permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * "Apache BCEL", nor may "Apache" appear in their name, without
  34. * prior written permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation. For more
  52. * information on the Apache Software Foundation, please see
  53. * <http://www.apache.org/>.
  54. */
  55. import com.sun.org.apache.bcel.internal.Constants;
  56. import com.sun.org.apache.bcel.internal.util.ByteSequence;
  57. import java.io.*;
  58. import java.util.ArrayList;
  59. import java.util.zip.*;
  60. /**
  61. * Utility functions that do not really belong to any class in particular.
  62. *
  63. * @version $Id: Utility.java,v 1.1.1.1 2001/10/29 20:00:05 jvanzyl Exp $
  64. * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  65. */
  66. public abstract class Utility {
  67. private static int consumed_chars; /* How many chars have been consumed
  68. * during parsing in signatureToString().
  69. * Read by methodSignatureToString().
  70. * Set by side effect,but only internally.
  71. */
  72. private static boolean wide=false; /* The `WIDE' instruction is used in the
  73. * byte code to allow 16-bit wide indices
  74. * for local variables. This opcode
  75. * precedes an `ILOAD', e.g.. The opcode
  76. * immediately following takes an extra
  77. * byte which is combined with the
  78. * following byte to form a
  79. * 16-bit value.
  80. */
  81. /**
  82. * Convert bit field of flags into string such as `static final'.
  83. *
  84. * @param access_flags Access flags
  85. * @return String representation of flags
  86. */
  87. public static final String accessToString(int access_flags) {
  88. return accessToString(access_flags, false);
  89. }
  90. /**
  91. * Convert bit field of flags into string such as `static final'.
  92. *
  93. * Special case: Classes compiled with new compilers and with the
  94. * `ACC_SUPER' flag would be said to be "synchronized". This is
  95. * because SUN used the same value for the flags `ACC_SUPER' and
  96. * `ACC_SYNCHRONIZED'.
  97. *
  98. * @param access_flags Access flags
  99. * @param for_class access flags are for class qualifiers ?
  100. * @return String representation of flags
  101. */
  102. public static final String accessToString(int access_flags,
  103. boolean for_class)
  104. {
  105. StringBuffer buf = new StringBuffer();
  106. int p = 0;
  107. for(int i=0; p < Constants.MAX_ACC_FLAG; i++) { // Loop through known flags
  108. p = pow2(i);
  109. if((access_flags & p) != 0) {
  110. /* Special case: Classes compiled with new compilers and with the
  111. * `ACC_SUPER' flag would be said to be "synchronized". This is
  112. * because SUN used the same value for the flags `ACC_SUPER' and
  113. * `ACC_SYNCHRONIZED'.
  114. */
  115. if(for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE)))
  116. continue;
  117. buf.append(Constants.ACCESS_NAMES[i] + " ");
  118. }
  119. }
  120. return buf.toString().trim();
  121. }
  122. /**
  123. * @return "class" or "interface", depending on the ACC_INTERFACE flag
  124. */
  125. public static final String classOrInterface(int access_flags) {
  126. return ((access_flags & Constants.ACC_INTERFACE) != 0)? "interface" : "class";
  127. }
  128. /**
  129. * Disassemble a byte array of JVM byte codes starting from code line
  130. * `index' and return the disassembled string representation. Decode only
  131. * `num' opcodes (including their operands), use -1 if you want to
  132. * decompile everything.
  133. *
  134. * @param code byte code array
  135. * @param constant_pool Array of constants
  136. * @param index offset in `code' array
  137. * <EM>(number of opcodes, not bytes!)</EM>
  138. * @param length number of opcodes to decompile, -1 for all
  139. * @param verbose be verbose, e.g. print constant pool index
  140. * @return String representation of byte codes
  141. */
  142. public static final String codeToString(byte[] code,
  143. ConstantPool constant_pool,
  144. int index, int length, boolean verbose)
  145. {
  146. StringBuffer buf = new StringBuffer(code.length * 20); // Should be sufficient
  147. ByteSequence stream = new ByteSequence(code);
  148. try {
  149. for(int i=0; i < index; i++) // Skip `index' lines of code
  150. codeToString(stream, constant_pool, verbose);
  151. for(int i=0; stream.available() > 0; i++) {
  152. if((length < 0) || (i < length)) {
  153. String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
  154. buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
  155. }
  156. }
  157. } catch(IOException e) {
  158. System.out.println(buf.toString());
  159. e.printStackTrace();
  160. throw new ClassFormatError("Byte code error: " + e);
  161. }
  162. return buf.toString();
  163. }
  164. public static final String codeToString(byte[] code,
  165. ConstantPool constant_pool,
  166. int index, int length) {
  167. return codeToString(code, constant_pool, index, length, true);
  168. }
  169. /**
  170. * Disassemble a stream of byte codes and return the
  171. * string representation.
  172. *
  173. * @param bytes stream of bytes
  174. * @param constant_pool Array of constants
  175. * @param verbose be verbose, e.g. print constant pool index
  176. * @return String representation of byte code
  177. */
  178. public static final String codeToString(ByteSequence bytes,
  179. ConstantPool constant_pool, boolean verbose)
  180. throws IOException
  181. {
  182. short opcode = (short)bytes.readUnsignedByte();
  183. int default_offset=0, low, high, npairs;
  184. int index, vindex, constant;
  185. int[] match, jump_table;
  186. int no_pad_bytes=0, offset;
  187. StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
  188. /* Special case: Skip (0-3) padding bytes, i.e., the
  189. * following bytes are 4-byte-aligned
  190. */
  191. if((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
  192. int remainder = bytes.getIndex() % 4;
  193. no_pad_bytes = (remainder == 0)? 0 : 4 - remainder;
  194. for(int i=0; i < no_pad_bytes; i++) {
  195. byte b;
  196. if((b=bytes.readByte()) != 0)
  197. System.err.println("Warning: Padding byte != 0 in " +
  198. Constants.OPCODE_NAMES[opcode] + ":" + b);
  199. }
  200. // Both cases have a field default_offset in common
  201. default_offset = bytes.readInt();
  202. }
  203. switch(opcode) {
  204. /* Table switch has variable length arguments.
  205. */
  206. case Constants.TABLESWITCH:
  207. low = bytes.readInt();
  208. high = bytes.readInt();
  209. offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
  210. default_offset += offset;
  211. buf.append("\tdefault = " + default_offset + ", low = " + low +
  212. ", high = " + high + "(");
  213. jump_table = new int[high - low + 1];
  214. for(int i=0; i < jump_table.length; i++) {
  215. jump_table[i] = offset + bytes.readInt();
  216. buf.append(jump_table[i]);
  217. if(i < jump_table.length - 1)
  218. buf.append(", ");
  219. }
  220. buf.append(")");
  221. break;
  222. /* Lookup switch has variable length arguments.
  223. */
  224. case Constants.LOOKUPSWITCH: {
  225. npairs = bytes.readInt();
  226. offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
  227. match = new int[npairs];
  228. jump_table = new int[npairs];
  229. default_offset += offset;
  230. buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
  231. " (");
  232. for(int i=0; i < npairs; i++) {
  233. match[i] = bytes.readInt();
  234. jump_table[i] = offset + bytes.readInt();
  235. buf.append("(" + match[i] + ", " + jump_table[i] + ")");
  236. if(i < npairs - 1)
  237. buf.append(", ");
  238. }
  239. buf.append(")");
  240. }
  241. break;
  242. /* Two address bytes + offset from start of byte stream form the
  243. * jump target
  244. */
  245. case Constants.GOTO: case Constants.IFEQ: case Constants.IFGE: case Constants.IFGT:
  246. case Constants.IFLE: case Constants.IFLT: case Constants.JSR: case Constants.IFNE:
  247. case Constants.IFNONNULL: case Constants.IFNULL: case Constants.IF_ACMPEQ:
  248. case Constants.IF_ACMPNE: case Constants.IF_ICMPEQ: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT:
  249. case Constants.IF_ICMPLE: case Constants.IF_ICMPLT: case Constants.IF_ICMPNE:
  250. buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
  251. break;
  252. /* 32-bit wide jumps
  253. */
  254. case Constants.GOTO_W: case Constants.JSR_W:
  255. buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
  256. break;
  257. /* Index byte references local variable (register)
  258. */
  259. case Constants.ALOAD: case Constants.ASTORE: case Constants.DLOAD: case Constants.DSTORE: case Constants.FLOAD:
  260. case Constants.FSTORE: case Constants.ILOAD: case Constants.ISTORE: case Constants.LLOAD: case Constants.LSTORE:
  261. case Constants.RET:
  262. if(wide) {
  263. vindex = bytes.readUnsignedShort();
  264. wide=false; // Clear flag
  265. }
  266. else
  267. vindex = bytes.readUnsignedByte();
  268. buf.append("\t\t%" + vindex);
  269. break;
  270. /*
  271. * Remember wide byte which is used to form a 16-bit address in the
  272. * following instruction. Relies on that the method is called again with
  273. * the following opcode.
  274. */
  275. case Constants.WIDE:
  276. wide = true;
  277. buf.append("\t(wide)");
  278. break;
  279. /* Array of basic type.
  280. */
  281. case Constants.NEWARRAY:
  282. buf.append("\t\t<" + Constants.TYPE_NAMES[bytes.readByte()] + ">");
  283. break;
  284. /* Access object/class fields.
  285. */
  286. case Constants.GETFIELD: case Constants.GETSTATIC: case Constants.PUTFIELD: case Constants.PUTSTATIC:
  287. index = bytes.readUnsignedShort();
  288. buf.append("\t\t" +
  289. constant_pool.constantToString(index, Constants.CONSTANT_Fieldref) +
  290. (verbose? " (" + index + ")" : ""));
  291. break;
  292. /* Operands are references to classes in constant pool
  293. */
  294. case Constants.NEW:
  295. case Constants.CHECKCAST:
  296. buf.append("\t");
  297. case Constants.INSTANCEOF:
  298. index = bytes.readUnsignedShort();
  299. buf.append("\t<" + constant_pool.constantToString(index,
  300. Constants.CONSTANT_Class) +
  301. ">" + (verbose? " (" + index + ")" : ""));
  302. break;
  303. /* Operands are references to methods in constant pool
  304. */
  305. case Constants.INVOKESPECIAL: case Constants.INVOKESTATIC: case Constants.INVOKEVIRTUAL:
  306. index = bytes.readUnsignedShort();
  307. buf.append("\t" + constant_pool.constantToString(index,
  308. Constants.CONSTANT_Methodref) +
  309. (verbose? " (" + index + ")" : ""));
  310. break;
  311. case Constants.INVOKEINTERFACE:
  312. index = bytes.readUnsignedShort();
  313. int nargs = bytes.readUnsignedByte(); // historical, redundant
  314. buf.append("\t" +
  315. constant_pool.constantToString(index,
  316. Constants.CONSTANT_InterfaceMethodref) +
  317. (verbose? " (" + index + ")\t" : "") + nargs + "\t" +
  318. bytes.readUnsignedByte()); // Last byte is a reserved space
  319. break;
  320. /* Operands are references to items in constant pool
  321. */
  322. case Constants.LDC_W: case Constants.LDC2_W:
  323. index = bytes.readUnsignedShort();
  324. buf.append("\t\t" + constant_pool.constantToString
  325. (index, constant_pool.getConstant(index).getTag()) +
  326. (verbose? " (" + index + ")" : ""));
  327. break;
  328. case Constants.LDC:
  329. index = bytes.readUnsignedByte();
  330. buf.append("\t\t" +
  331. constant_pool.constantToString
  332. (index, constant_pool.getConstant(index).getTag()) +
  333. (verbose? " (" + index + ")" : ""));
  334. break;
  335. /* Array of references.
  336. */
  337. case Constants.ANEWARRAY:
  338. index = bytes.readUnsignedShort();
  339. buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
  340. (index, Constants.CONSTANT_Class), false) +
  341. ">" + (verbose? " (" + index + ")": ""));
  342. break;
  343. /* Multidimensional array of references.
  344. */
  345. case Constants.MULTIANEWARRAY: {
  346. index = bytes.readUnsignedShort();
  347. int dimensions = bytes.readUnsignedByte();
  348. buf.append("\t<" + compactClassName(constant_pool.getConstantString
  349. (index, Constants.CONSTANT_Class), false) +
  350. ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
  351. }
  352. break;
  353. /* Increment local variable.
  354. */
  355. case Constants.IINC:
  356. if(wide) {
  357. vindex = bytes.readUnsignedShort();
  358. constant = bytes.readShort();
  359. wide = false;
  360. }
  361. else {
  362. vindex = bytes.readUnsignedByte();
  363. constant = bytes.readByte();
  364. }
  365. buf.append("\t\t%" + vindex + "\t" + constant);
  366. break;
  367. default:
  368. if(Constants.NO_OF_OPERANDS[opcode] > 0) {
  369. for(int i=0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
  370. buf.append("\t\t");
  371. switch(Constants.TYPE_OF_OPERANDS[opcode][i]) {
  372. case Constants.T_BYTE: buf.append(bytes.readByte()); break;
  373. case Constants.T_SHORT: buf.append(bytes.readShort()); break;
  374. case Constants.T_INT: buf.append(bytes.readInt()); break;
  375. default: // Never reached
  376. System.err.println("Unreachable default case reached!");
  377. System.exit(-1);
  378. }
  379. }
  380. }
  381. }
  382. return buf.toString();
  383. }
  384. public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
  385. throws IOException
  386. {
  387. return codeToString(bytes, constant_pool, true);
  388. }
  389. /**
  390. * Shorten long class names, <em>java/lang/String</em> becomes
  391. * <em>String</em>.
  392. *
  393. * @param str The long class name
  394. * @return Compacted class name
  395. */
  396. public static final String compactClassName(String str) {
  397. return compactClassName(str, true);
  398. }
  399. /**
  400. * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
  401. * if the
  402. * class name starts with this string and the flag <em>chopit</em> is true.
  403. * Slashes <em>/</em> are converted to dots <em>.</em>.
  404. *
  405. * @param str The long class name
  406. * @param prefix The prefix the get rid off
  407. * @param chopit Flag that determines whether chopping is executed or not
  408. * @return Compacted class name
  409. */
  410. public static final String compactClassName(String str,
  411. String prefix,
  412. boolean chopit)
  413. {
  414. int len = prefix.length();
  415. str = str.replace('/', '.'); // Is `/' on all systems, even DOS
  416. if(chopit) {
  417. // If string starts with `prefix' and contains no further dots
  418. if(str.startsWith(prefix) &&
  419. (str.substring(len).indexOf('.') == -1))
  420. str = str.substring(len);
  421. }
  422. return str;
  423. }
  424. /**
  425. * Shorten long class names, <em>java/lang/String</em> becomes
  426. * <em>java.lang.String</em>,
  427. * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
  428. * is also removed.
  429. *
  430. * @param str The long class name
  431. * @param chopit Flag that determines whether chopping is executed or not
  432. * @return Compacted class name
  433. */
  434. public static final String compactClassName(String str, boolean chopit) {
  435. return compactClassName(str, "java.lang.", chopit);
  436. }
  437. private static final boolean is_digit(char ch) {
  438. return (ch >= '0') && (ch <= '9');
  439. }
  440. private static final boolean is_space(char ch) {
  441. return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
  442. }
  443. /**
  444. * @return `flag' with bit `i' set to 1
  445. */
  446. public static final int setBit(int flag, int i) {
  447. return flag | pow2(i);
  448. }
  449. /**
  450. * @return `flag' with bit `i' set to 0
  451. */
  452. public static final int clearBit(int flag, int i) {
  453. int bit = pow2(i);
  454. return (flag & bit) == 0? flag : flag ^ bit;
  455. }
  456. /**
  457. * @return true, if bit `i' in `flag' is set
  458. */
  459. public static final boolean isSet(int flag, int i) {
  460. return (flag & pow2(i)) != 0;
  461. }
  462. /**
  463. * Converts string containing the method return and argument types
  464. * to a byte code method signature.
  465. *
  466. * @param ret Return type of method
  467. * @param argv Types of method arguments
  468. * @return Byte code representation of method signature
  469. */
  470. public final static String methodTypeToSignature(String ret, String[] argv)
  471. throws ClassFormatError
  472. {
  473. StringBuffer buf = new StringBuffer("(");
  474. String str;
  475. if(argv != null)
  476. for(int i=0; i < argv.length; i++) {
  477. str = getSignature(argv[i]);
  478. if(str.endsWith("V")) // void can't be a method argument
  479. throw new ClassFormatError("Invalid type: " + argv[i]);
  480. buf.append(str);
  481. }
  482. str = getSignature(ret);
  483. buf.append(")" + str);
  484. return buf.toString();
  485. }
  486. /**
  487. * @param signature Method signature
  488. * @return Array of argument types
  489. * @throw ClassFormatError
  490. */
  491. public static final String[] methodSignatureArgumentTypes(String signature)
  492. throws ClassFormatError
  493. {
  494. return methodSignatureArgumentTypes(signature, true);
  495. }
  496. /**
  497. * @param signature Method signature
  498. * @param chopit Shorten class names ?
  499. * @return Array of argument types
  500. * @throw ClassFormatError
  501. */
  502. public static final String[] methodSignatureArgumentTypes(String signature,
  503. boolean chopit)
  504. throws ClassFormatError
  505. {
  506. ArrayList vec = new ArrayList();
  507. int index;
  508. String[] types;
  509. try { // Read all declarations between for `(' and `)'
  510. if(signature.charAt(0) != '(')
  511. throw new ClassFormatError("Invalid method signature: " + signature);
  512. index = 1; // current string position
  513. while(signature.charAt(index) != ')') {
  514. vec.add(signatureToString(signature.substring(index), chopit));
  515. index += consumed_chars; // update position
  516. }
  517. } catch(StringIndexOutOfBoundsException e) { // Should never occur
  518. throw new ClassFormatError("Invalid method signature: " + signature);
  519. }
  520. types = new String[vec.size()];
  521. vec.toArray(types);
  522. return types;
  523. }
  524. /**
  525. * @param signature Method signature
  526. * @return return type of method
  527. * @throw ClassFormatError
  528. */
  529. public static final String methodSignatureReturnType(String signature)
  530. throws ClassFormatError
  531. {
  532. return methodSignatureReturnType(signature, true);
  533. }
  534. /**
  535. * @param signature Method signature
  536. * @param chopit Shorten class names ?
  537. * @return return type of method
  538. * @throw ClassFormatError
  539. */
  540. public static final String methodSignatureReturnType(String signature,
  541. boolean chopit)
  542. throws ClassFormatError
  543. {
  544. int index;
  545. String type;
  546. try {
  547. // Read return type after `)'
  548. index = signature.lastIndexOf(')') + 1;
  549. type = signatureToString(signature.substring(index), chopit);
  550. } catch(StringIndexOutOfBoundsException e) { // Should never occur
  551. throw new ClassFormatError("Invalid method signature: " + signature);
  552. }
  553. return type;
  554. }
  555. /**
  556. * Converts method signature to string with all class names compacted.
  557. *
  558. * @param signature to convert
  559. * @param name of method
  560. * @param access flags of method
  561. * @return Human readable signature
  562. */
  563. public static final String methodSignatureToString(String signature,
  564. String name,
  565. String access) {
  566. return methodSignatureToString(signature, name, access, true);
  567. }
  568. public static final String methodSignatureToString(String signature,
  569. String name,
  570. String access,
  571. boolean chopit) {
  572. return methodSignatureToString(signature, name, access, chopit, null);
  573. }
  574. /**
  575. * A returnÿtype signature represents the return value from a method.
  576. * It is a series of bytes in the following grammar:
  577. *
  578. * <return_signature> ::= <field_type> | V
  579. *
  580. * The character V indicates that the method returns no value. Otherwise, the
  581. * signature indicates the type of the return value.
  582. * An argument signature represents an argument passed to a method:
  583. *
  584. * <argument_signature> ::= <field_type>
  585. *
  586. * A method signature represents the arguments that the method expects, and
  587. * the value that it returns.
  588. * <method_signature> ::= (<arguments_signature>) <return_signature>
  589. * <arguments_signature>::= <argument_signature>*
  590. *
  591. * This method converts such a string into a Java type declaration like
  592. * `void main(String[])' and throws a `ClassFormatError' when the parsed
  593. * type is invalid.
  594. *
  595. * @param signature Method signature
  596. * @param name Method name
  597. * @param access Method access rights
  598. * @return Java type declaration
  599. * @throw ClassFormatError
  600. */
  601. public static final String methodSignatureToString(String signature,
  602. String name,
  603. String access,
  604. boolean chopit,
  605. LocalVariableTable vars)
  606. throws ClassFormatError
  607. {
  608. StringBuffer buf = new StringBuffer("(");
  609. String type;
  610. int index;
  611. int var_index = (access.indexOf("static") >= 0)? 0 : 1;
  612. try { // Read all declarations between for `(' and `)'
  613. if(signature.charAt(0) != '(')
  614. throw new ClassFormatError("Invalid method signature: " + signature);
  615. index = 1; // current string position
  616. while(signature.charAt(index) != ')') {
  617. buf.append(signatureToString(signature.substring(index), chopit));
  618. if(vars != null) {
  619. LocalVariable l = vars.getLocalVariable(var_index);
  620. if(l != null)
  621. buf.append(" " + l.getName());
  622. } else
  623. buf.append(" arg" + var_index);
  624. var_index++;
  625. buf.append(", ");
  626. index += consumed_chars; // update position
  627. }
  628. index++; // update position
  629. // Read return type after `)'
  630. type = signatureToString(signature.substring(index), chopit);
  631. } catch(StringIndexOutOfBoundsException e) { // Should never occur
  632. throw new ClassFormatError("Invalid method signature: " + signature);
  633. }
  634. if(buf.length() > 1) // Tack off the extra ", "
  635. buf.setLength(buf.length() - 2);
  636. buf.append(")");
  637. return access + ((access.length() > 0)? " " : "") + // May be an empty string
  638. type + " " + name + buf.toString();
  639. }
  640. // Guess what this does
  641. private static final int pow2(int n) {
  642. return 1 << n;
  643. }
  644. /**
  645. * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
  646. *
  647. * @param str String to permute
  648. * @param old String to be replaced
  649. * @param new Replacement string
  650. * @return new String object
  651. */
  652. public static final String replace(String str, String old, String new_) {
  653. int index, old_index;
  654. StringBuffer buf = new StringBuffer();
  655. try {
  656. if((index = str.indexOf(old)) != -1) { // `old' found in str
  657. old_index = 0; // String start offset
  658. // While we have something to replace
  659. while((index = str.indexOf(old, old_index)) != -1) {
  660. buf.append(str.substring(old_index, index)); // append prefix
  661. buf.append(new_); // append replacement
  662. old_index = index + old.length(); // Skip `old'.length chars
  663. }
  664. buf.append(str.substring(old_index)); // append rest of string
  665. str = buf.toString();
  666. }
  667. } catch(StringIndexOutOfBoundsException e) { // Should not occur
  668. System.err.println(e);
  669. }
  670. return str;
  671. }
  672. /**
  673. * Converts signature to string with all class names compacted.
  674. *
  675. * @param signature to convert
  676. * @return Human readable signature
  677. */
  678. public static final String signatureToString(String signature) {
  679. return signatureToString(signature, true);
  680. }
  681. /**
  682. * The field signature represents the value of an argument to a function or
  683. * the value of a variable. It is a series of bytes generated by the
  684. * following grammar:
  685. *
  686. * <PRE>
  687. * <field_signature> ::= <field_type>
  688. * <field_type> ::= <base_type>|<object_type>|<array_type>
  689. * <base_type> ::= B|C|D|F|I|J|S|Z
  690. * <object_type> ::= L<fullclassname>
  691. * <array_type> ::= [<field_type>
  692. *
  693. * The meaning of the base types is as follows:
  694. * B byte signed byte
  695. * C char character
  696. * D double double precision IEEE float
  697. * F float single precision IEEE float
  698. * I int integer
  699. * J long long integer
  700. * L<fullclassname> ... an object of the given class
  701. * S short signed short
  702. * Z boolean true or false
  703. * [<field sig> ... array
  704. * </PRE>
  705. *
  706. * This method converts this string into a Java type declaration such as
  707. * `String[]' and throws a `ClassFormatError' when the parsed type is
  708. * invalid.
  709. *
  710. * @param signature Class signature
  711. * @param chopit Flag that determines whether chopping is executed or not
  712. * @return Java type declaration
  713. * @throws ClassFormatError
  714. */
  715. public static final String signatureToString(String signature,
  716. boolean chopit)
  717. {
  718. consumed_chars = 1; // This is the default, read just one char like `B'
  719. try {
  720. switch(signature.charAt(0)) {
  721. case 'B' : return "byte";
  722. case 'C' : return "char";
  723. case 'D' : return "double";
  724. case 'F' : return "float";
  725. case 'I' : return "int";
  726. case 'J' : return "long";
  727. case 'L' : { // Full class name
  728. int index = signature.indexOf(';'); // Look for closing `;'
  729. if(index < 0)
  730. throw new ClassFormatError("Invalid signature: " + signature);
  731. consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
  732. return compactClassName(signature.substring(1, index), chopit);
  733. }
  734. case 'S' : return "short";
  735. case 'Z' : return "boolean";
  736. case '[' : { // Array declaration
  737. int n;
  738. StringBuffer buf, brackets;
  739. String type;
  740. char ch;
  741. int consumed_chars; // Shadows global var
  742. brackets = new StringBuffer(); // Accumulate []'s
  743. // Count opening brackets and look for optional size argument
  744. for(n=0; signature.charAt(n) == '['; n++)
  745. brackets.append("[]");
  746. consumed_chars = n; // Remember value
  747. // The rest of the string denotes a `<field_type>'
  748. type = signatureToString(signature.substring(n), chopit);
  749. Utility.consumed_chars += consumed_chars;
  750. return type + brackets.toString();
  751. }
  752. case 'V' : return "void";
  753. default : throw new ClassFormatError("Invalid signature: `" +
  754. signature + "'");
  755. }
  756. } catch(StringIndexOutOfBoundsException e) { // Should never occur
  757. throw new ClassFormatError("Invalid signature: " + e + ":" + signature);
  758. }
  759. }
  760. /** Parse Java type such as "char", or "java.lang.String[]" and return the
  761. * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
  762. *
  763. * @param type Java type
  764. * @return byte code signature
  765. */
  766. public static String getSignature(String type) {
  767. StringBuffer buf = new StringBuffer();
  768. char[] chars = type.toCharArray();
  769. boolean char_found = false, delim = false;
  770. int index = -1;
  771. loop:
  772. for(int i=0; i < chars.length; i++) {
  773. switch(chars[i]) {
  774. case ' ': case '\t': case '\n': case '\r': case '\f':
  775. if(char_found)
  776. delim = true;
  777. break;
  778. case '[':
  779. if(!char_found)
  780. throw new RuntimeException("Illegal type: " + type);
  781. index = i;
  782. break loop;
  783. default:
  784. char_found = true;
  785. if(!delim)
  786. buf.append(chars[i]);
  787. }
  788. }
  789. int brackets = 0;
  790. if(index > 0)
  791. brackets = countBrackets(type.substring(index));
  792. type = buf.toString();
  793. buf.setLength(0);
  794. for(int i=0; i < brackets; i++)
  795. buf.append('[');
  796. boolean found = false;
  797. for(int i=Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) {
  798. if(Constants.TYPE_NAMES[i].equals(type)) {
  799. found = true;
  800. buf.append(Constants.SHORT_TYPE_NAMES[i]);
  801. }
  802. }
  803. if(!found) // Class name
  804. buf.append('L' + type.replace('.', '/') + ';');
  805. return buf.toString();
  806. }
  807. private static int countBrackets(String brackets) {
  808. char[] chars = brackets.toCharArray();
  809. int count = 0;
  810. boolean open = false;
  811. for(int i=0; i<chars.length; i++) {
  812. switch(chars[i]) {
  813. case '[':
  814. if(open)
  815. throw new RuntimeException("Illegally nested brackets:" + brackets);
  816. open = true;
  817. break;
  818. case ']':
  819. if(!open)
  820. throw new RuntimeException("Illegally nested brackets:" + brackets);
  821. open = false;
  822. count++;
  823. break;
  824. default:
  825. // Don't care
  826. }
  827. }
  828. if(open)
  829. throw new RuntimeException("Illegally nested brackets:" + brackets);
  830. return count;
  831. }
  832. /**
  833. * Return type of method signature as a byte value as defined in <em>Constants</em>
  834. *
  835. * @param signature in format described above
  836. * @return type of method signature
  837. * @see Constants
  838. */
  839. public static final byte typeOfMethodSignature(String signature)
  840. throws ClassFormatError
  841. {
  842. int index;
  843. try {
  844. if(signature.charAt(0) != '(')
  845. throw new ClassFormatError("Invalid method signature: " + signature);
  846. index = signature.lastIndexOf(')') + 1;
  847. return typeOfSignature(signature.substring(index));
  848. } catch(StringIndexOutOfBoundsException e) {
  849. throw new ClassFormatError("Invalid method signature: " + signature);
  850. }
  851. }
  852. /**
  853. * Return type of signature as a byte value as defined in <em>Constants</em>
  854. *
  855. * @param signature in format described above
  856. * @return type of signature
  857. * @see Constants
  858. */
  859. public static final byte typeOfSignature(String signature)
  860. throws ClassFormatError
  861. {
  862. try {
  863. switch(signature.charAt(0)) {
  864. case 'B' : return Constants.T_BYTE;
  865. case 'C' : return Constants.T_CHAR;
  866. case 'D' : return Constants.T_DOUBLE;
  867. case 'F' : return Constants.T_FLOAT;
  868. case 'I' : return Constants.T_INT;
  869. case 'J' : return Constants.T_LONG;
  870. case 'L' : return Constants.T_REFERENCE;
  871. case '[' : return Constants.T_ARRAY;
  872. case 'V' : return Constants.T_VOID;
  873. case 'Z' : return Constants.T_BOOLEAN;
  874. case 'S' : return Constants.T_SHORT;
  875. default:
  876. throw new ClassFormatError("Invalid method signature: " + signature);
  877. }
  878. } catch(StringIndexOutOfBoundsException e) {
  879. throw new ClassFormatError("Invalid method signature: " + signature);
  880. }
  881. }
  882. /** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
  883. */
  884. public static short searchOpcode(String name) {
  885. name = name.toLowerCase();
  886. for(short i=0; i < Constants.OPCODE_NAMES.length; i++)
  887. if(Constants.OPCODE_NAMES[i].equals(name))
  888. return i;
  889. return -1;
  890. }
  891. /**
  892. * Convert (signed) byte to (unsigned) short value, i.e., all negative
  893. * values become positive.
  894. */
  895. private static final short byteToShort(byte b) {
  896. return (b < 0)? (short)(256 + b) : (short)b;
  897. }
  898. /** Convert bytes into hexidecimal string
  899. *
  900. * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
  901. */
  902. public static final String toHexString(byte[] bytes) {
  903. StringBuffer buf = new StringBuffer();
  904. for(int i=0; i < bytes.length; i++) {
  905. short b = byteToShort(bytes[i]);
  906. String hex = Integer.toString(b, 0x10);
  907. if(b < 0x10) // just one digit, prepend '0'
  908. buf.append('0');
  909. buf.append(hex);
  910. if(i < bytes.length - 1)
  911. buf.append(' ');
  912. }
  913. return buf.toString();
  914. }
  915. /**
  916. * Return a string for an integer justified left or right and filled up with
  917. * `fill' characters if necessary.
  918. *
  919. * @param i integer to format
  920. * @param length length of desired string
  921. * @param left_justify format left or right
  922. * @param fill fill character
  923. * @return formatted int
  924. */
  925. public static final String format(int i, int length, boolean left_justify, char fill) {
  926. return fillup(Integer.toString(i), length, left_justify, fill);
  927. }
  928. /**
  929. * Fillup char with up to length characters with char `fill' and justify it left or right.
  930. *
  931. * @param str string to format
  932. * @param length length of desired string
  933. * @param left_justify format left or right
  934. * @param fill fill character
  935. * @return formatted string
  936. */
  937. public static final String fillup(String str, int length, boolean left_justify, char fill) {
  938. int len = length - str.length();
  939. char[] buf = new char[(len < 0)? 0 : len];
  940. for(int j=0; j < buf.length; j++)
  941. buf[j] = fill;
  942. if(left_justify)
  943. return str + new String(buf);
  944. else
  945. return new String(buf) + str;
  946. }
  947. static final boolean equals(byte[] a, byte[] b) {
  948. int size;
  949. if((size=a.length) != b.length)
  950. return false;
  951. for(int i=0; i < size; i++)
  952. if(a[i] != b[i])
  953. return false;
  954. return true;
  955. }
  956. public static final void printArray(PrintStream out, Object[] obj) {
  957. out.println(printArray(obj, true));
  958. }
  959. public static final void printArray(PrintWriter out, Object[] obj) {
  960. out.println(printArray(obj, true));
  961. }
  962. public static final String printArray(Object[] obj) {
  963. return printArray(obj, true);
  964. }
  965. public static final String printArray(Object[] obj, boolean braces) {
  966. if(obj == null)
  967. return null;
  968. StringBuffer buf = new StringBuffer();
  969. if(braces)
  970. buf.append('{');
  971. for(int i=0; i < obj.length; i++) {
  972. if(obj[i] != null)
  973. buf.append(obj[i].toString());
  974. else
  975. buf.append("null");
  976. if(i < obj.length - 1)
  977. buf.append(", ");
  978. }
  979. if(braces)
  980. buf.append('}');
  981. return buf.toString();
  982. }
  983. /** @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
  984. */
  985. public static boolean isJavaIdentifierPart(char ch) {
  986. return ((ch >= 'a') && (ch <= 'z')) ||
  987. ((ch >= 'A') && (ch <= 'Z')) ||
  988. ((ch >= '0') && (ch <= '9')) ||
  989. (ch == '_');
  990. }
  991. /** Encode byte array it into Java identifier string, i.e., a string
  992. * that only contains the following characters: (a, ... z, A, ... Z,
  993. * 0, ... 9, _, $). The encoding algorithm itself is not too
  994. * clever: if the current byte's ASCII value already is a valid Java
  995. * identifier part, leave it as it is. Otherwise it writes the
  996. * escape character($) followed by <p><ul><li> the ASCII value as a
  997. * hexadecimal string, if the value is not in the range
  998. * 200..247</li> <li>a Java identifier char not used in a lowercase
  999. * hexadecimal string, if the value is in the range
  1000. * 200..247</li><ul></p>
  1001. *
  1002. * <p>This operation inflates the original byte array by roughly 40-50%</p>
  1003. *
  1004. * @param bytes the byte array to convert
  1005. * @param compress use gzip to minimize string
  1006. */
  1007. public static String encode(byte[] bytes, boolean compress) throws IOException {
  1008. if(compress) {
  1009. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  1010. GZIPOutputStream gos = new GZIPOutputStream(baos);
  1011. gos.write(bytes, 0, bytes.length);
  1012. gos.close();
  1013. baos.close();
  1014. bytes = baos.toByteArray();
  1015. }
  1016. CharArrayWriter caw = new CharArrayWriter();
  1017. JavaWriter jw = new JavaWriter(caw);
  1018. for(int i=0; i < bytes.length; i++) {
  1019. int in = bytes[i] & 0x000000ff; // Normalize to unsigned
  1020. jw.write(in);
  1021. }
  1022. return caw.toString();
  1023. }
  1024. /** Decode a string back to a byte array.
  1025. *
  1026. * @param bytes the byte array to convert
  1027. * @param uncompress use gzip to uncompress the stream of bytes
  1028. */
  1029. public static byte[] decode(String s, boolean uncompress) throws IOException {
  1030. char[] chars = s.toCharArray();
  1031. CharArrayReader car = new CharArrayReader(chars);
  1032. JavaReader jr = new JavaReader(car);
  1033. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  1034. int ch;
  1035. while((ch = jr.read()) >= 0) {
  1036. bos.write(ch);
  1037. }
  1038. bos.close();
  1039. car.close();
  1040. jr.close();
  1041. byte[] bytes = bos.toByteArray();
  1042. if(uncompress) {
  1043. GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
  1044. byte[] tmp = new byte[bytes.length * 3]; // Rough estimate
  1045. int count = 0;
  1046. int b;
  1047. while((b = gis.read()) >= 0)
  1048. tmp[count++] = (byte)b;
  1049. bytes = new byte[count];
  1050. System.arraycopy(tmp, 0, bytes, 0, count);
  1051. }
  1052. return bytes;
  1053. }
  1054. // A-Z, g-z, _, $
  1055. private static final int FREE_CHARS = 48;
  1056. private static int[] CHAR_MAP = new int[FREE_CHARS];
  1057. private static int[] MAP_CHAR = new int[256]; // Reverse map
  1058. private static final char ESCAPE_CHAR = '$';
  1059. static {
  1060. int j = 0, k = 0;
  1061. for(int i='A'; i <= 'Z'; i++) {
  1062. CHAR_MAP[j] = i;
  1063. MAP_CHAR[i] = j;
  1064. j++;
  1065. }
  1066. for(int i='g'; i <= 'z'; i++) {
  1067. CHAR_MAP[j] = i;
  1068. MAP_CHAR[i] = j;
  1069. j++;
  1070. }
  1071. CHAR_MAP[j] = '$';
  1072. MAP_CHAR['$'] = j;
  1073. j++;
  1074. CHAR_MAP[j] = '_';
  1075. MAP_CHAR['_'] = j;
  1076. }
  1077. /** Decode characters into bytes.
  1078. * Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
  1079. */
  1080. private static class JavaReader extends FilterReader {
  1081. public JavaReader(Reader in) {
  1082. super(in);
  1083. }
  1084. public int read() throws IOException {
  1085. int b = in.read();
  1086. if(b != ESCAPE_CHAR) {
  1087. return b;
  1088. } else {
  1089. int i = in.read();
  1090. if(i < 0)
  1091. return -1;
  1092. if(((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape
  1093. int j = in.read();
  1094. if(j < 0)
  1095. return -1;
  1096. char[] tmp = { (char)i, (char)j };
  1097. int s = Integer.parseInt(new String(tmp), 16);
  1098. return s;
  1099. } else { // Special escape
  1100. return MAP_CHAR[i];
  1101. }
  1102. }
  1103. }
  1104. public int read(char[] cbuf, int off, int len) throws IOException {
  1105. for(int i=0; i < len; i++)
  1106. cbuf[off + i] = (char)read();
  1107. return len;
  1108. }
  1109. }
  1110. /** Encode bytes into valid java identifier characters.
  1111. * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a>
  1112. */
  1113. private static class JavaWriter extends FilterWriter {
  1114. public JavaWriter(Writer out) {
  1115. super(out);
  1116. }
  1117. public void write(int b) throws IOException {
  1118. if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
  1119. out.write(b);
  1120. } else {
  1121. out.write(ESCAPE_CHAR); // Escape character
  1122. // Special escape
  1123. if(b >= 0 && b < FREE_CHARS) {
  1124. out.write(CHAR_MAP[b]);
  1125. } else { // Normal escape
  1126. char[] tmp = Integer.toHexString(b).toCharArray();
  1127. if(tmp.length == 1) {
  1128. out.write('0');
  1129. out.write(tmp[0]);
  1130. } else {
  1131. out.write(tmp[0]);
  1132. out.write(tmp[1]);
  1133. }
  1134. }
  1135. }
  1136. }
  1137. public void write(char[] cbuf, int off, int len) throws IOException {
  1138. for(int i=0; i < len; i++)
  1139. write(cbuf[off + i]);
  1140. }
  1141. public void write(String str, int off, int len) throws IOException {
  1142. write(str.toCharArray(), off, len);
  1143. }
  1144. }
  1145. }