0N/Aimport java.io.*;
0N/A
0N/Aclass InstallSDE {
0N/A static final boolean verbose = true;
0N/A static final String nameSDE = "SourceDebugExtension";
0N/A
0N/A byte[] orig;
0N/A byte[] sdeAttr;
0N/A byte[] gen;
0N/A
0N/A int origPos = 0;
0N/A int genPos = 0;
0N/A
0N/A int sdeIndex;
0N/A
0N/A public static void main(String[] args) throws IOException {
0N/A if (args.length == 2) {
0N/A install(new File(args[0]), new File(args[1]));
0N/A } else if (args.length == 3) {
0N/A install(new File(args[0]), new File(args[1]), new File(args[2]));
0N/A } else {
0N/A abort("Usage: <command> <input class file> " +
0N/A "<attribute file> <output class file name>\n" +
0N/A "<command> <input/output class file> <attribute file>");
0N/A }
0N/A }
0N/A
0N/A static void install(File inClassFile, File attrFile, File outClassFile)
0N/A throws IOException {
0N/A new InstallSDE(inClassFile, attrFile, outClassFile);
0N/A }
0N/A
0N/A static void install(File inOutClassFile, File attrFile) throws IOException {
0N/A File tmpFile = new File(inOutClassFile.getPath() + "tmp");
0N/A new InstallSDE(inOutClassFile, attrFile, tmpFile);
0N/A if (!inOutClassFile.delete()) {
0N/A throw new IOException("inOutClassFile.delete() failed");
0N/A }
0N/A if (!tmpFile.renameTo(inOutClassFile)) {
0N/A throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
0N/A }
0N/A }
0N/A
0N/A static void abort(String msg) {
0N/A System.err.println(msg);
0N/A System.exit(1);
0N/A }
0N/A
0N/A InstallSDE(File inClassFile, File attrFile, File outClassFile) throws IOException {
0N/A if (!inClassFile.exists()) {
0N/A abort("no such file: " + inClassFile);
0N/A }
0N/A if (!attrFile.exists()) {
0N/A abort("no such file: " + attrFile);
0N/A }
0N/A
0N/A // get the bytes
0N/A orig = readWhole(inClassFile);
0N/A sdeAttr = readWhole(attrFile);
0N/A gen = new byte[orig.length + sdeAttr.length + 100];
0N/A
0N/A // do it
0N/A addSDE();
0N/A
0N/A // write result
0N/A FileOutputStream outStream = new FileOutputStream(outClassFile);
0N/A outStream.write(gen, 0, genPos);
0N/A outStream.close();
0N/A }
0N/A
0N/A byte[] readWhole(File input) throws IOException {
0N/A FileInputStream inStream = new FileInputStream(input);
0N/A int len = (int)input.length();
0N/A byte[] bytes = new byte[len];
0N/A if (inStream.read(bytes, 0, len) != len) {
0N/A abort("expected size: " + len);
0N/A }
0N/A inStream.close();
0N/A return bytes;
0N/A }
0N/A
0N/A void addSDE() throws UnsupportedEncodingException {
0N/A int i;
0N/A copy(4 + 2 + 2); // magic min/maj version
0N/A int constantPoolCountPos = genPos;
0N/A int constantPoolCount = readU2();
0N/A writeU2(constantPoolCount);
0N/A // copy old constant pool return index of SDE symbol, if found
0N/A sdeIndex = copyConstantPool(constantPoolCount);
0N/A if (sdeIndex < 0) {
0N/A // if "SourceDebugExtension" symbol not there add it
0N/A writeUtf8ForSDE();
0N/A
0N/A // increment the countantPoolCount
0N/A sdeIndex = constantPoolCount;
0N/A ++constantPoolCount;
0N/A randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
0N/A
0N/A if (verbose) {
0N/A System.out.println("SourceDebugExtension not found, installed at: " +
0N/A sdeIndex);
0N/A }
0N/A } else {
0N/A if (verbose) {
0N/A System.out.println("SourceDebugExtension found at: " +
0N/A sdeIndex);
0N/A }
0N/A }
0N/A copy(2 + 2 + 2); // access, this, super
0N/A int interfaceCount = readU2();
0N/A writeU2(interfaceCount);
0N/A if (verbose) {
0N/A System.out.println("interfaceCount: " + interfaceCount);
0N/A }
0N/A copy(interfaceCount * 2);
0N/A copyMembers(); // fields
0N/A copyMembers(); // methods
0N/A int attrCountPos = genPos;
0N/A int attrCount = readU2();
0N/A writeU2(attrCount);
0N/A if (verbose) {
0N/A System.out.println("class attrCount: " + attrCount);
0N/A }
0N/A // copy the class attributes, return true if SDE attr found (not copied)
0N/A if (!copyAttrs(attrCount)) {
0N/A // we will be adding SDE and it isn't already counted
0N/A ++attrCount;
0N/A randomAccessWriteU2(attrCountPos, attrCount);
0N/A if (verbose) {
0N/A System.out.println("class attrCount incremented");
0N/A }
0N/A }
0N/A writeAttrForSDE(sdeIndex);
0N/A }
0N/A
0N/A void copyMembers() {
0N/A int count = readU2();
0N/A writeU2(count);
0N/A if (verbose) {
0N/A System.out.println("members count: " + count);
0N/A }
0N/A for (int i = 0; i < count; ++i) {
0N/A copy(6); // access, name, descriptor
0N/A int attrCount = readU2();
0N/A writeU2(attrCount);
0N/A if (verbose) {
0N/A System.out.println("member attr count: " + attrCount);
0N/A }
0N/A copyAttrs(attrCount);
0N/A }
0N/A }
0N/A
0N/A boolean copyAttrs(int attrCount) {
0N/A boolean sdeFound = false;
0N/A for (int i = 0; i < attrCount; ++i) {
0N/A int nameIndex = readU2();
0N/A // don't write old SDE
0N/A if (nameIndex == sdeIndex) {
0N/A sdeFound = true;
0N/A if (verbose) {
0N/A System.out.println("SDE attr found");
0N/A }
0N/A } else {
0N/A writeU2(nameIndex); // name
0N/A int len = readU4();
0N/A writeU4(len);
0N/A copy(len);
0N/A if (verbose) {
0N/A System.out.println("attr len: " + len);
0N/A }
0N/A }
0N/A }
0N/A return sdeFound;
0N/A }
0N/A
0N/A void writeAttrForSDE(int index) {
0N/A writeU2(index);
0N/A writeU4(sdeAttr.length);
0N/A for (int i = 0; i < sdeAttr.length; ++i) {
0N/A writeU1(sdeAttr[i]);
0N/A }
0N/A }
0N/A
0N/A void randomAccessWriteU2(int pos, int val) {
0N/A int savePos = genPos;
0N/A genPos = pos;
0N/A writeU2(val);
0N/A genPos = savePos;
0N/A }
0N/A
0N/A int readU1() {
0N/A return ((int)orig[origPos++]) & 0xFF;
0N/A }
0N/A
0N/A int readU2() {
0N/A int res = readU1();
0N/A return (res << 8) + readU1();
0N/A }
0N/A
0N/A int readU4() {
0N/A int res = readU2();
0N/A return (res << 16) + readU2();
0N/A }
0N/A
0N/A void writeU1(int val) {
0N/A gen[genPos++] = (byte)val;
0N/A }
0N/A
0N/A void writeU2(int val) {
0N/A writeU1(val >> 8);
0N/A writeU1(val & 0xFF);
0N/A }
0N/A
0N/A void writeU4(int val) {
0N/A writeU2(val >> 16);
0N/A writeU2(val & 0xFFFF);
0N/A }
0N/A
0N/A void copy(int count) {
0N/A for (int i = 0; i < count; ++i) {
0N/A gen[genPos++] = orig[origPos++];
0N/A }
0N/A }
0N/A
0N/A byte[] readBytes(int count) {
0N/A byte[] bytes = new byte[count];
0N/A for (int i = 0; i < count; ++i) {
0N/A bytes[i] = orig[origPos++];
0N/A }
0N/A return bytes;
0N/A }
0N/A
0N/A void writeBytes(byte[] bytes) {
0N/A for (int i = 0; i < bytes.length; ++i) {
0N/A gen[genPos++] = bytes[i];
0N/A }
0N/A }
0N/A
0N/A int copyConstantPool(int constantPoolCount) throws UnsupportedEncodingException {
0N/A int sdeIndex = -1;
0N/A // copy const pool index zero not in class file
0N/A for (int i = 1; i < constantPoolCount; ++i) {
0N/A int tag = readU1();
0N/A writeU1(tag);
0N/A switch (tag) {
0N/A case 7: // Class
0N/A case 8: // String
0N/A copy(2);
0N/A break;
0N/A case 9: // Field
0N/A case 10: // Method
0N/A case 11: // InterfaceMethod
0N/A case 3: // Integer
0N/A case 4: // Float
0N/A case 12: // NameAndType
0N/A copy(4);
0N/A break;
0N/A case 5: // Long
0N/A case 6: // Double
0N/A copy(8);
0N/A break;
0N/A case 1: // Utf8
0N/A int len = readU2();
0N/A writeU2(len);
0N/A byte[] utf8 = readBytes(len);
0N/A String str = new String(utf8, "UTF-8");
0N/A if (verbose) {
0N/A System.out.println(i + " read class attr -- '" + str + "'");
0N/A }
0N/A if (str.equals(nameSDE)) {
0N/A sdeIndex = i;
0N/A }
0N/A writeBytes(utf8);
0N/A break;
0N/A default:
0N/A abort("unexpected tag: " + tag);
0N/A break;
0N/A }
0N/A }
0N/A return sdeIndex;
0N/A }
0N/A
0N/A void writeUtf8ForSDE() {
0N/A int len = nameSDE.length();
0N/A writeU1(1); // Utf8 tag
0N/A writeU2(len);
0N/A for (int i = 0; i < len; ++i) {
0N/A writeU1(nameSDE.charAt(i));
0N/A }
0N/A }
0N/A}