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