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