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 }