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