Home | History | Annotate | Download | only in dxconvext
      1 /*
      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 
     17 package dxconvext;
     18 
     19 import com.android.dx.cf.direct.ClassPathOpener;
     20 import com.android.dx.cf.direct.DirectClassFile;
     21 import com.android.dx.cf.direct.StdAttributeFactory;
     22 import com.android.dx.cf.iface.Member;
     23 import com.android.dx.cf.iface.ParseObserver;
     24 import com.android.dx.util.ByteArray;
     25 import com.android.dx.util.FileUtils;
     26 
     27 import java.io.BufferedWriter;
     28 import java.io.File;
     29 import java.io.FileNotFoundException;
     30 import java.io.FileOutputStream;
     31 import java.io.IOException;
     32 import java.io.OutputStreamWriter;
     33 import java.io.Writer;
     34 
     35 public 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 }
    284