1 import java.io.*;
   2 
   3 class InstallSDE {
   4     static final boolean verbose = true;
   5     static final String nameSDE = "SourceDebugExtension";
   6 
   7     byte[] orig;
   8     byte[] sdeAttr;
   9     byte[] gen;
  10 
  11     int origPos = 0;
  12     int genPos = 0;
  13 
  14     int sdeIndex;
  15 
  16     public static void main(String[] args) throws IOException {
  17         if (args.length == 2) {
  18             install(new File(args[0]), new File(args[1]));
  19         } else if (args.length == 3) {
  20             install(new File(args[0]), new File(args[1]), new File(args[2]));
  21         } else {
  22             abort("Usage: <command> <input class file> " +
  23                                "<attribute file> <output class file name>\n" +
  24                   "<command> <input/output class file> <attribute file>");
  25         }
  26     }
  27 
  28     static void install(File inClassFile, File attrFile, File outClassFile)
  29                                                             throws IOException {
  30         new InstallSDE(inClassFile, attrFile, outClassFile);
  31     }
  32 
  33     static void install(File inOutClassFile, File attrFile) throws IOException {
  34         File tmpFile = new File(inOutClassFile.getPath() + "tmp");
  35         new InstallSDE(inOutClassFile, attrFile, tmpFile);
  36         if (!inOutClassFile.delete()) {
  37             throw new IOException("inOutClassFile.delete() failed");
  38         }
  39         if (!tmpFile.renameTo(inOutClassFile)) {
  40             throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
  41         }
  42     }
  43 
  44     static void abort(String msg) {
  45         System.err.println(msg);
  46         System.exit(1);
  47     }
  48 
  49     InstallSDE(File inClassFile, File attrFile, File outClassFile) throws IOException {
  50         if (!inClassFile.exists()) {
  51             abort("no such file: " + inClassFile);
  52         }
  53         if (!attrFile.exists()) {
  54             abort("no such file: " + attrFile);
  55         }
  56 
  57         // get the bytes
  58         orig = readWhole(inClassFile);
  59         sdeAttr = readWhole(attrFile);
  60         gen = new byte[orig.length + sdeAttr.length + 100];
  61 
  62         // do it
  63         addSDE();
  64 
  65         // write result
  66         FileOutputStream outStream = new FileOutputStream(outClassFile);
  67         outStream.write(gen, 0, genPos);
  68         outStream.close();
  69     }
  70 
  71     byte[] readWhole(File input) throws IOException {
  72         FileInputStream inStream = new FileInputStream(input);
  73         int len = (int)input.length();
  74         byte[] bytes = new byte[len];
  75         if (inStream.read(bytes, 0, len) != len) {
  76             abort("expected size: " + len);
  77         }
  78         inStream.close();
  79         return bytes;
  80     }
  81 
  82     void addSDE() throws UnsupportedEncodingException {
  83         int i;
  84         copy(4 + 2 + 2); // magic min/maj version
  85         int constantPoolCountPos = genPos;
  86         int constantPoolCount = readU2();
  87         writeU2(constantPoolCount);
  88         // copy old constant pool return index of SDE symbol, if found
  89         sdeIndex = copyConstantPool(constantPoolCount);
  90         if (sdeIndex < 0) {
  91             // if "SourceDebugExtension" symbol not there add it
  92             writeUtf8ForSDE();
  93 
  94             // increment the countantPoolCount
  95             sdeIndex = constantPoolCount;
  96             ++constantPoolCount;
  97             randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
  98 
  99             if (verbose) {
 100                 System.out.println("SourceDebugExtension not found, installed at: " +
 101                                    sdeIndex);
 102             }
 103         } else {
 104             if (verbose) {
 105                 System.out.println("SourceDebugExtension found at: " +
 106                                    sdeIndex);
 107             }
 108         }
 109         copy(2 + 2 + 2);  // access, this, super
 110         int interfaceCount = readU2();
 111         writeU2(interfaceCount);
 112         if (verbose) {
 113             System.out.println("interfaceCount: " + interfaceCount);
 114         }
 115         copy(interfaceCount * 2);
 116         copyMembers(); // fields
 117         copyMembers(); // methods
 118         int attrCountPos = genPos;
 119         int attrCount = readU2();
 120         writeU2(attrCount);
 121         if (verbose) {
 122             System.out.println("class attrCount: " + attrCount);
 123         }
 124         // copy the class attributes, return true if SDE attr found (not copied)
 125         if (!copyAttrs(attrCount)) {
 126             // we will be adding SDE and it isn't already counted
 127             ++attrCount;
 128             randomAccessWriteU2(attrCountPos, attrCount);
 129             if (verbose) {
 130                 System.out.println("class attrCount incremented");
 131             }
 132         }
 133         writeAttrForSDE(sdeIndex);
 134     }
 135 
 136     void copyMembers() {
 137         int count = readU2();
 138         writeU2(count);
 139         if (verbose) {
 140             System.out.println("members count: " + count);
 141         }
 142         for (int i = 0; i < count; ++i) {
 143             copy(6); // access, name, descriptor
 144             int attrCount = readU2();
 145             writeU2(attrCount);
 146             if (verbose) {
 147                 System.out.println("member attr count: " + attrCount);
 148             }
 149             copyAttrs(attrCount);
 150         }
 151     }
 152 
 153     boolean copyAttrs(int attrCount) {
 154         boolean sdeFound = false;
 155         for (int i = 0; i < attrCount; ++i) {
 156             int nameIndex = readU2();
 157             // don't write old SDE
 158             if (nameIndex == sdeIndex) {
 159                 sdeFound = true;
 160                 if (verbose) {
 161                     System.out.println("SDE attr found");
 162                 }
 163             } else {
 164                 writeU2(nameIndex);  // name
 165                 int len = readU4();
 166                 writeU4(len);
 167                 copy(len);
 168                 if (verbose) {
 169                     System.out.println("attr len: " + len);
 170                 }
 171             }
 172         }
 173         return sdeFound;
 174     }
 175 
 176     void writeAttrForSDE(int index) {
 177         writeU2(index);
 178         writeU4(sdeAttr.length);
 179         for (int i = 0; i < sdeAttr.length; ++i) {
 180             writeU1(sdeAttr[i]);
 181         }
 182     }
 183 
 184     void randomAccessWriteU2(int pos, int val) {
 185         int savePos = genPos;
 186         genPos = pos;
 187         writeU2(val);
 188         genPos = savePos;
 189     }
 190 
 191     int readU1() {
 192         return ((int)orig[origPos++]) & 0xFF;
 193     }
 194 
 195     int readU2() {
 196          int res = readU1();
 197         return (res << 8) + readU1();
 198     }
 199 
 200     int readU4() {
 201         int res = readU2();
 202         return (res << 16) + readU2();
 203     }
 204 
 205     void writeU1(int val) {
 206         gen[genPos++] = (byte)val;
 207     }
 208 
 209     void writeU2(int val) {
 210         writeU1(val >> 8);
 211         writeU1(val & 0xFF);
 212     }
 213 
 214     void writeU4(int val) {
 215         writeU2(val >> 16);
 216         writeU2(val & 0xFFFF);
 217     }
 218 
 219     void copy(int count) {
 220         for (int i = 0; i < count; ++i) {
 221             gen[genPos++] = orig[origPos++];
 222         }
 223     }
 224 
 225     byte[] readBytes(int count) {
 226         byte[] bytes = new byte[count];
 227         for (int i = 0; i < count; ++i) {
 228             bytes[i] = orig[origPos++];
 229         }
 230         return bytes;
 231     }
 232 
 233     void writeBytes(byte[] bytes) {
 234         for (int i = 0; i < bytes.length; ++i) {
 235             gen[genPos++] = bytes[i];
 236         }
 237     }
 238 
 239     int copyConstantPool(int constantPoolCount) throws UnsupportedEncodingException {
 240         int sdeIndex = -1;
 241         // copy const pool index zero not in class file
 242         for (int i = 1; i < constantPoolCount; ++i) {
 243             int tag = readU1();
 244             writeU1(tag);
 245             switch (tag) {
 246                 case 7:  // Class
 247                 case 8:  // String
 248                     copy(2);
 249                     break;
 250                 case 9:  // Field
 251                 case 10: // Method
 252                 case 11: // InterfaceMethod
 253                 case 3:  // Integer
 254                 case 4:  // Float
 255                 case 12: // NameAndType
 256                 case 18: // InvokeDynamic
 257                     copy(4);
 258                     break;
 259                 case 5:  // Long
 260                 case 6:  // Double
 261                     copy(8);
 262                     break;
 263                 case 15: // MethodHandle
 264                     copy(3);
 265                     break;
 266                 case 1:  // Utf8
 267                     int len = readU2();
 268                     writeU2(len);
 269                     byte[] utf8 = readBytes(len);
 270                     String str = new String(utf8, "UTF-8");
 271                     if (verbose) {
 272                         System.out.println(i + " read class attr -- '" + str + "'");
 273                     }
 274                     if (str.equals(nameSDE)) {
 275                         sdeIndex = i;
 276                     }
 277                     writeBytes(utf8);
 278                     break;
 279                 default:
 280                     abort("unexpected tag: " + tag);
 281                     break;
 282             }
 283         }
 284         return sdeIndex;
 285     }
 286 
 287     void writeUtf8ForSDE() {
 288         int len = nameSDE.length();
 289         writeU1(1); // Utf8 tag
 290         writeU2(len);
 291         for (int i = 0; i < len; ++i) {
 292             writeU1(nameSDE.charAt(i));
 293         }
 294     }
 295 }