dump.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright (c) 1997, 2008 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.bytecode;
  4. import java.io.*;
  5. import java.util.zip.*;
  6. import java.net.URL;
  7. /** Application to read a ClassType from a DataInputStream (.class file).
  8. *
  9. * To print out the contents of a class file foo.class, you can use
  10. * the class <code>dump</code> as an application:
  11. * <pre>
  12. * java gnu.bytecode.dump foo.class
  13. * </pre>
  14. * This will print out the constant pool, fields, methods, superclass,
  15. * and implemented interfaces of class <code>foo</code>.
  16. * It is useful for printing out more detailed information
  17. * than <code>javap</code> does.
  18. *
  19. * @author Per Bothner
  20. */
  21. public class dump extends ClassFileInput
  22. {
  23. ClassTypeWriter writer;
  24. /**
  25. * @param str input stream, positioned just after the magic number
  26. */
  27. dump (InputStream str, ClassTypeWriter writer)
  28. throws IOException, ClassFormatError
  29. {
  30. super(str);
  31. this.ctype = new ClassType();
  32. readFormatVersion();
  33. readConstants();
  34. readClassInfo();
  35. readFields();
  36. readMethods();
  37. readAttributes(ctype);
  38. writer.print(ctype);
  39. writer.flush();
  40. }
  41. public ConstantPool readConstants () throws IOException
  42. {
  43. ctype.constants = super.readConstants();
  44. return ctype.constants;
  45. }
  46. public Attribute readAttribute (String name, int length, AttrContainer container)
  47. throws IOException
  48. {
  49. return super.readAttribute (name, length, container);
  50. }
  51. static int readMagic (InputStream in) throws IOException
  52. {
  53. int magic = 0;
  54. for (int j = 0; j < 4; j++)
  55. {
  56. int b = in.read();
  57. if (b < 0)
  58. break;
  59. magic = (magic << 8) | (b & 0xff);
  60. }
  61. return magic;
  62. }
  63. public static void process (InputStream in, String filename,
  64. OutputStream out, int flags)
  65. throws IOException
  66. {
  67. process(in, filename, new ClassTypeWriter(null, out, flags));
  68. }
  69. public static void process (InputStream in, String filename,
  70. Writer out, int flags)
  71. throws IOException
  72. {
  73. process(in, filename, new ClassTypeWriter(null, out, flags));
  74. }
  75. public static void process (InputStream in, String filename,
  76. ClassTypeWriter out)
  77. throws IOException
  78. {
  79. InputStream inp = new BufferedInputStream(in);
  80. inp.mark(5);
  81. int magic = readMagic(inp);
  82. if (magic == 0xcafebabe)
  83. {
  84. out.print("Reading .class from ");
  85. out.print(filename);
  86. out.println('.');
  87. new dump(inp, out);
  88. }
  89. else if (magic == (('P' << 24) | ('K' << 16) | (3 << 8) | 4))
  90. {
  91. inp.reset();
  92. out.print("Reading classes from archive ");
  93. out.print(filename);
  94. out.println('.');
  95. ZipInputStream zin = new ZipInputStream(inp);
  96. ZipEntry zent;
  97. while ((zent = zin.getNextEntry()) != null)
  98. {
  99. String name = zent.getName();
  100. if (zent.isDirectory())
  101. {
  102. out.print("Archive directory: ");
  103. out.print(name);
  104. out.println('.');
  105. }
  106. else
  107. {
  108. out.println();
  109. magic = readMagic(zin);
  110. if (magic == 0xcafebabe)
  111. {
  112. out.print("Reading class member: ");
  113. out.print(name);
  114. out.println('.');
  115. new dump(zin, out);
  116. }
  117. else
  118. {
  119. out.print("Skipping non-class member: ");
  120. out.print(name);
  121. out.println('.');
  122. }
  123. }
  124. }
  125. System.exit(-1);
  126. }
  127. else
  128. {
  129. System.err.println("File "+filename+" is not a valid .class file");
  130. System.exit(-1);
  131. }
  132. }
  133. /** Reads a .class file, and prints out the contents to System.out.
  134. * Very rudimentary - prints out the constant pool, and field and method
  135. * names and types, but only minimal attributes (i.e. no dis-assembly yet).
  136. * @param args One argument - the name of a .class file.
  137. */
  138. public static void main (String[] args)
  139. {
  140. int alen = args.length;
  141. ClassTypeWriter out = new ClassTypeWriter(null, System.out, 0);
  142. if (alen == 0)
  143. usage(System.err);
  144. for (int i = 0; i < alen; i++)
  145. {
  146. String filename = args[i];
  147. if (filename.equals("-verbose") || filename.equals("--verbose"))
  148. {
  149. out.setFlags(ClassTypeWriter.PRINT_VERBOSE);
  150. continue;
  151. }
  152. boolean isURL = uriSchemeSpecified(filename);
  153. InputStream in;
  154. try
  155. {
  156. if (isURL)
  157. {
  158. boolean isJarURL = filename.startsWith("jar:");
  159. if (isJarURL)
  160. {
  161. String part = filename.substring(4);
  162. // If no URL scheme follows "jar:", then assume "file:".
  163. if (! uriSchemeSpecified(part))
  164. {
  165. int excl = part.indexOf('!');
  166. if (excl >= 0)
  167. {
  168. String filepart = part.substring(0, excl);
  169. /* #ifdef JAVA5 */
  170. filepart = new File(filepart).toURI().toURL().toString();
  171. /* #else */
  172. // filepart = new File(filepart).toURL().toString();
  173. /* #endif */
  174. filename = "jar:" + filepart + part.substring(excl);
  175. }
  176. }
  177. // Allow "jar:xxxx!foo.bar.baz" -> "jar:xxxx!/foo/bar/baz.class"
  178. if (part.indexOf("!/") < 0)
  179. {
  180. int excl = filename.lastIndexOf('!');
  181. if (excl <= 0)
  182. isJarURL = false;
  183. else if (filename.indexOf('/', excl) < 0)
  184. {
  185. part = filename.substring(excl+1);
  186. part = part.replace('.', '/');
  187. filename = filename.substring(0, excl+1)
  188. + '/' + part + ".class";
  189. }
  190. }
  191. }
  192. try
  193. {
  194. URL url = new URL(filename);
  195. try
  196. {
  197. in = url.openConnection().getInputStream();
  198. }
  199. catch (java.util.zip.ZipException e1)
  200. {
  201. if (isJarURL)
  202. {
  203. String filepart = url.getFile();
  204. int sl = filepart.lastIndexOf('!');
  205. if (sl > 0)
  206. filepart = filepart.substring(0, sl);
  207. try
  208. {
  209. new URL(filepart).openConnection().getInputStream();
  210. }
  211. catch (java.io.FileNotFoundException e2)
  212. {
  213. System.err.print("Jar File for URL ");
  214. System.err.print(filepart);
  215. System.err.println(" not found.");
  216. System.exit(-1);
  217. }
  218. }
  219. throw e1;
  220. }
  221. }
  222. catch (java.io.FileNotFoundException e1)
  223. {
  224. System.err.print("File for URL ");
  225. System.err.print(filename);
  226. System.err.println(" not found.");
  227. System.exit(-1);
  228. in = null;
  229. }
  230. catch (java.util.zip.ZipException e1)
  231. {
  232. System.err.print("Error opening zip archive ");
  233. System.err.print(filename);
  234. System.err.println(" not found.");
  235. e1.printStackTrace();
  236. if (e1.getCause() != null)
  237. e1.getCause().printStackTrace();
  238. System.exit(-1);
  239. in = null;
  240. }
  241. }
  242. else
  243. {
  244. try
  245. {
  246. in = new FileInputStream(filename);
  247. }
  248. catch (java.io.FileNotFoundException e1)
  249. {
  250. // If this a class name rather than a file name?
  251. ClassLoader loader;
  252. try
  253. {
  254. Class clas = ObjectType.getContextClass(filename);
  255. loader = clas.getClassLoader();
  256. }
  257. catch (NoClassDefFoundError e2)
  258. {
  259. loader = ObjectType.getContextClassLoader();
  260. }
  261. catch (Throwable e2)
  262. {
  263. System.err.print("File ");
  264. System.err.print(filename);
  265. System.err.println(" not found.");
  266. System.exit(-1);
  267. loader = null;
  268. in = null;
  269. }
  270. // Ok - we found a class - now find the class file.
  271. String clfilename = filename.replace('.', '/') + ".class";
  272. if (loader == null)
  273. loader = ClassLoader.getSystemClassLoader();
  274. try
  275. {
  276. java.net.URL resource = loader.getResource(clfilename);
  277. in = resource.openConnection().getInputStream();
  278. filename = resource.toString();
  279. }
  280. catch (Throwable ex)
  281. {
  282. System.err.print("Can't find .class file for class ");
  283. System.err.print(filename);
  284. System.err.print(" - ");
  285. System.err.println(ex);
  286. System.exit(-1);
  287. in = null;
  288. }
  289. }
  290. }
  291. process(in, filename, out);
  292. }
  293. catch (java.io.IOException e)
  294. {
  295. e.printStackTrace();
  296. System.err.println("caught ");
  297. System.err.print(e);
  298. System.exit(-1);
  299. }
  300. }
  301. }
  302. /** Helper routine to get the scheme part of a URI.
  303. * The scheme part is "http:" or "file:" or "ftp:" most commonly.
  304. * This functions searches for the first ':' that doesn't follow a '/'.
  305. * @return The length of the scheme component, not counting the colon,
  306. * (or alternatively the index of the colon), or -1 if the is no scheme.
  307. *
  308. * Duplicates gnu.kawa.io.Path.uriSchemeLength, to make gnu.bytecode standalone.
  309. */
  310. static int uriSchemeLength (String uri)
  311. {
  312. int len = uri.length();
  313. for (int i = 0; i < len; i++)
  314. {
  315. char ch = uri.charAt(i);
  316. if (ch == ':')
  317. return i;
  318. if (i == 0 ? ! Character.isLetter(ch)
  319. : (! Character.isLetterOrDigit(ch)
  320. && ch != '+' && ch != '-' && ch != '.'))
  321. return -1;
  322. }
  323. return -1;
  324. }
  325. /** Tests if a URL has a scheme.
  326. * For convenience, we treat a 1-character "scheme" as an
  327. * MS-DOS-style "drive letter" - i.e. not a scheme.
  328. *
  329. * Duplicates gnu.kawa.io.Path.uriSchemeSpecified, to make gnu.bytecode
  330. * standalone.
  331. */
  332. static boolean uriSchemeSpecified (String name)
  333. {
  334. int ulen = uriSchemeLength(name);
  335. if (ulen == 1 && File.separatorChar == '\\')
  336. {
  337. char drive = name.charAt(0);
  338. return ! ((drive >= 'a' && drive <= 'z')
  339. || (drive >= 'A' && drive <= 'Z'));
  340. }
  341. return ulen > 0;
  342. }
  343. public static void usage(PrintStream err)
  344. {
  345. err.println("Prints and dis-assembles the contents of JVM .class files.");
  346. err.println("Usage: [--verbose] class-or-jar ...");
  347. err.println("where a class-or-jar can be one of:");
  348. err.println("- a fully-qualified class name; or");
  349. err.println("- the name of a .class file, or a URL reference to one; or");
  350. err.println("- the name of a .jar or .zip archive file, or a URL reference to one.");
  351. err.println("If a .jar/.zip archive is named, all its.class file members are printed.");
  352. err.println();
  353. err.println("You can name a single .class member of an archive with a jar: URL,");
  354. err.println("which looks like: jar:jar-spec!/p1/p2/cl.class");
  355. err.println("The jar-spec can be a URL or the name of the .jar file.");
  356. err.println("You can also use the shorthand syntax: jar:jar-spec!p1.p2.cl");
  357. System.exit(-1);
  358. }
  359. }