blob: 8a433965216a2fdf62f6fb8c7ee3a32ff8ea288e [file] [log] [blame]
Brian Muramatsu0748ed52011-11-29 14:43:10 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dxconvext;
18
19import com.android.dx.cf.direct.ClassPathOpener;
20import com.android.dx.cf.direct.DirectClassFile;
21import com.android.dx.cf.direct.StdAttributeFactory;
22import com.android.dx.cf.iface.Member;
23import com.android.dx.cf.iface.ParseObserver;
24import com.android.dx.util.ByteArray;
25import com.android.dx.util.FileUtils;
26
27import java.io.BufferedWriter;
28import java.io.File;
29import java.io.FileNotFoundException;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.OutputStreamWriter;
33import java.io.Writer;
34
35public class ClassFileParser {
36
37 private BufferedWriter bw; // the writer to write the result to.
38
39 /**
40 * Parses a .class file and outputs a .cfh (class file in hex format) file.
41 *
42 * args[0] is the absolute path to the java src directory e.g.
43 * /home/fjost/android/workspace/dxconverter/src
44 *
45 * args[1] is the absolute path to the classes directory e.g.
46 * /home/fjost/android/workspace/out/classes_javac this is the place where
47 *
48 * args[2] is the absolute path to the java source file, e.g.
49 * /home/fjost/android/workspace/dxconverter/src/test/MyTest.java
50 *
51 *
52 *
53 * @param args
54 */
55 public static void main(String[] args) throws IOException {
56 ClassFileParser cfp = new ClassFileParser();
57 cfp.process(args[0], args[1], args[2]);
58 }
59
60 private void process(final String srcDir, final String classesDir,
61 final String absSrcFilePath) throws IOException {
62 ClassPathOpener opener;
63
64 String fileName = absSrcFilePath;
65 // e.g. test/p1/MyTest.java
66 String pckPath = fileName.substring(srcDir.length() + 1);
67 // e.g. test/p1
68 String pck = pckPath.substring(0, pckPath.lastIndexOf("/"));
69 // e.g. MyTest
70 String cName = pckPath.substring(pck.length() + 1);
71 cName = cName.substring(0, cName.lastIndexOf("."));
72 String cfName = pck+"/"+cName+".class";
73 // 2. calculate the target file name:
74 // e.g. <out-path>/test/p1/MyTest.class
75 String inFile = classesDir + "/" + pck + "/" + cName + ".class";
76 if (!new File(inFile).exists()) {
77 throw new RuntimeException("cannot read:" + inFile);
78 }
79 byte[] bytes = FileUtils.readFile(inFile);
80 // write the outfile to the same directory as the corresponding .java
81 // file
82 String outFile = absSrcFilePath.substring(0, absSrcFilePath
83 .lastIndexOf("/"))+ "/" + cName + ".cfh";
84 Writer w;
85 try {
86 w = new OutputStreamWriter(new FileOutputStream(new File(outFile)));
87 } catch (FileNotFoundException e) {
88 throw new RuntimeException("cannot write to file:"+outFile, e);
89 }
90 // Writer w = new OutputStreamWriter(System.out);
91 ClassFileParser.this.processFileBytes(w, cfName, bytes);
92
93 }
94
95 /**
96 *
97 * @param w the writer to write the generated .cfh file to
98 * @param name the relative name of the java src file, e.g.
99 * dxc/util/Util.java
100 * @param allbytes the bytes of this java src file
101 * @return true if everthing went alright
102 */
103 void processFileBytes(Writer w, String name, final byte[] allbytes) throws IOException {
104 String fixedPathName = fixPath(name);
105 DirectClassFile cf = new DirectClassFile(allbytes, fixedPathName, true);
106 bw = new BufferedWriter(w);
107 String className = fixedPathName.substring(0, fixedPathName.lastIndexOf("."));
108 out("//@class:" + className, 0);
109 cf.setObserver(new ParseObserver() {
110 private int cur_indent = 0;
111 private int checkpos = 0;
112
113 /**
114 * Indicate that the level of indentation for a dump should increase
115 * or decrease (positive or negative argument, respectively).
116 *
117 * @param indentDelta the amount to change indentation
118 */
119 public void changeIndent(int indentDelta) {
120 cur_indent += indentDelta;
121 }
122
123 /**
124 * Indicate that a particular member is now being parsed.
125 *
126 * @param bytes non-null; the source that is being parsed
127 * @param offset offset into <code>bytes</code> for the start of
128 * the member
129 * @param name non-null; name of the member
130 * @param descriptor non-null; descriptor of the member
131 */
132 public void startParsingMember(ByteArray bytes, int offset,
133 String name, String descriptor) {
134 // ByteArray ba = bytes.slice(offset, bytes.size());
135 out("// ========== start-ParseMember:" + name + ", offset "
136 + offset + ", len:" + (bytes.size() - offset)
137 + ",desc: " + descriptor);
138 // out("// "+dumpReadableString(ba));
139 // out(" "+dumpBytes(ba));
140 }
141
142 /**
143 * Indicate that a particular member is no longer being parsed.
144 *
145 * @param bytes non-null; the source that was parsed
146 * @param offset offset into <code>bytes</code> for the end of the
147 * member
148 * @param name non-null; name of the member
149 * @param descriptor non-null; descriptor of the member
150 * @param member non-null; the actual member that was parsed
151 */
152 public void endParsingMember(ByteArray bytes, int offset,
153 String name, String descriptor, Member member) {
154 ByteArray ba = bytes.slice(offset, bytes.size());
155 out("// ========== end-ParseMember:" + name + ", desc: "
156 + descriptor);
157 // out("// "+dumpReadableString(ba));
158 // out(" "+dumpBytes(ba));
159 }
160
161 /**
162 * Indicate that some parsing happened.
163 *
164 * @param bytes non-null; the source that was parsed
165 * @param offset offset into <code>bytes</code> for what was
166 * parsed
167 * @param len number of bytes parsed
168 * @param human non-null; human form for what was parsed
169 */
170 public void parsed(ByteArray bytes, int offset, int len,
171 String human) {
172 human = human.replace('\n', ' ');
173 out("// parsed:" + ", offset " + offset + ", len " + len
174 + ", h: " + human);
175 if (len > 0) {
176 ByteArray ba = bytes.slice(offset, offset + len);
177 check(ba);
178 out("// " + dumpReadableString(ba));
179 out(" " + dumpBytes(ba));
180 }
181 }
182
183 private void out(String msg) {
184 ClassFileParser.this.out(msg, cur_indent);
185
186 }
187
188 private void check(ByteArray ba) {
189 int len = ba.size();
190 int offset = checkpos;
191 for (int i = 0; i < len; i++) {
192 int b = ba.getByte(i);
193 byte b2 = allbytes[i + offset];
194 if (b != b2)
195 throw new RuntimeException("byte dump mismatch at pos "
196 + (i + offset));
197 }
198 checkpos += len;
199 }
200
201
202
203 private String dumpBytes(ByteArray ba) {
204 String s = "";
205 for (int i = 0; i < ba.size(); i++) {
206 int byt = ba.getUnsignedByte(i);
207 String hexVal = Integer.toHexString(byt);
208 if (hexVal.length() == 1) {
209 hexVal = "0" + hexVal;
210 }
211 s += hexVal + " ";
212 }
213 return s;
214 }
215
216 private String dumpReadableString(ByteArray ba) {
217 String s = "";
218 for (int i = 0; i < ba.size(); i++) {
219 int bb = ba.getUnsignedByte(i);
220 if (bb > 31 && bb < 127) {
221 s += (char) bb;
222 } else {
223 s += ".";
224 }
225 s += " ";
226 }
227 return s;
228 }
229
230
231 });
232 cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
233 // what is needed to force parsing to the end?
234 cf.getMagic();
235 // cf.getFields();
236 // cf.getAttributes();
237 // cf.getMethods();
238 bw.close();
239 }
240
241
242 private String getIndent(int indent) {
243 StringBuilder sb = new StringBuilder();
244 for (int i = 0; i < indent * 4; i++) {
245 sb.append(' ');
246 }
247 return sb.toString();
248 }
249
250 private void out(String msg, int cur_indent) {
251 try {
252 bw.write(getIndent(cur_indent) + msg);
253 bw.newLine();
254 } catch (IOException ioe) {
255 throw new RuntimeException("error while writing to the writer", ioe);
256 }
257 }
258
259 private static String fixPath(String path) {
260 /*
261 * If the path separator is \ (like on windows), we convert the path to
262 * a standard '/' separated path.
263 */
264 if (File.separatorChar == '\\') {
265 path = path.replace('\\', '/');
266 }
267
268 int index = path.lastIndexOf("/./");
269
270 if (index != -1) {
271 return path.substring(index + 3);
272 }
273
274 if (path.startsWith("./")) {
275 return path.substring(2);
276 }
277
278 return path;
279 }
280
281
282
283}