diff --git a/jode/jode/obfuscator/ClassIdentifier.java b/jode/jode/obfuscator/ClassIdentifier.java index 7921240..84d9957 100644 --- a/jode/jode/obfuscator/ClassIdentifier.java +++ b/jode/jode/obfuscator/ClassIdentifier.java @@ -19,27 +19,36 @@ package jode.obfuscator; import jode.GlobalOptions; -import jode.Obfuscator; import jode.bytecode.*; - ///#ifdef JDK12 +///import java.util.Comparator; +///import java.util.Collection; ///import java.util.Collections; ///import java.util.Arrays; +///import java.util.Iterator; +///import java.util.List; +///import java.util.LinkedList; +///import java.util.Map; ///#else +import jode.util.Comparator; +import jode.util.Collection; import jode.util.Collections; import jode.util.Arrays; +import jode.util.Iterator; +import jode.util.List; +import jode.util.LinkedList; +import jode.util.Map; ///#endif import java.lang.reflect.Modifier; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.io.OutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Random; -import java.util.Hashtable; -import java.util.Vector; -import java.util.Enumeration; public class ClassIdentifier extends Identifier { - ClassBundle bundle; PackageIdentifier pack; String name; ClassInfo info; @@ -52,23 +61,21 @@ public class ClassIdentifier extends Identifier { * are MethodIdentifier */ Identifier[] identifiers; - Vector knownSubClasses = new Vector(); - Vector virtualReachables = new Vector(); + List knownSubClasses = new LinkedList(); + List virtualReachables = new LinkedList(); - public ClassIdentifier(ClassBundle bundle, PackageIdentifier pack, + public ClassIdentifier(PackageIdentifier pack, String name, ClassInfo info) { super(name); - this.bundle = bundle; this.pack = pack; this.name = name; this.info = info; } public void addSubClass(ClassIdentifier ci) { - knownSubClasses.addElement(ci); - Enumeration enum = virtualReachables.elements(); - while (enum.hasMoreElements()) { - String[] method = (String[]) enum.nextElement(); + knownSubClasses.add(ci); + for(Iterator i = virtualReachables.iterator(); i.hasNext(); ) { + String[] method = (String[]) i.next(); ci.reachableIdentifier(method[0], method[1], true); } } @@ -96,15 +103,6 @@ public class ClassIdentifier extends Identifier { } } - public void applyPreserveRule(int preserveRule) { - if ((preserveRule & (info.getModifiers() ^ Modifier.PRIVATE)) != 0) { - setReachable(); - setPreserved(); - } - for (int i=0; i< identifiers.length; i++) - identifiers[i].applyPreserveRule(preserveRule); - } - public void reachableIdentifier(String name, String typeSig, boolean isVirtual) { boolean found = false; @@ -119,12 +117,10 @@ public class ClassIdentifier extends Identifier { /*XXXXXXXX super reachableIdentifier */ } /*ELSE*/ if (isVirtual) { - Enumeration enum = knownSubClasses.elements(); - while (enum.hasMoreElements()) - ((ClassIdentifier)enum.nextElement()) + for (Iterator i = knownSubClasses.iterator(); i.hasNext(); ) + ((ClassIdentifier)i.next()) .reachableIdentifier(name, typeSig, false); - virtualReachables.addElement - (new String[] { name, typeSig }); + virtualReachables.add(new String[] { name, typeSig }); } } @@ -138,6 +134,120 @@ public class ClassIdentifier extends Identifier { } } + /** + * This is partly taken from the classpath project. + */ + public long calcSerialVersionUID() { + final MessageDigest md; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + GlobalOptions.err.println("Can't calculate serialVersionUID"); + return 0L; + } + OutputStream digest = new OutputStream() { + + public void write(int b) { + md.update((byte) b); + } + + public void write(byte[] data, int offset, int length) { + md.update(data, offset, length); + } + }; + DataOutputStream out = new DataOutputStream(digest); + try { + out.writeUTF(info.getName()); + + int modifiers = info.getModifiers(); + // just look at interesting bits + modifiers = modifiers & ( Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC ); + out.writeInt(modifiers); + + ClassInfo[] interfaces + = (ClassInfo[]) info.getInterfaces().clone(); + Arrays.sort(interfaces, new Comparator() { + public int compare( Object o1, Object o2 ) { + return ((ClassInfo)o1).getName() + .compareTo(((ClassInfo)o2).getName()); + } + }); + for( int i=0; i < interfaces.length; i++ ) { + out.writeUTF(interfaces[i].getName()); + } + + + Comparator identCmp = new Comparator() { + public int compare(Object o1, Object o2) { + Identifier i1 = (Identifier)o1; + Identifier i2 = (Identifier)o2; + boolean special1 = (i1.equals("") + || i1.equals("")); + boolean special2 = (i2.equals("") + || i2.equals("")); + // Put constructors at the beginning + if (special1 != special2) { + return special1 ? -1 : 1; + } + + int comp = i1.getName().compareTo(i2.getName()); + if (comp != 0) { + return comp; + } else { + return i1.getType().compareTo(i2.getType()); + } + } + }; + + List idents = Arrays.asList((Object[]) identifiers.clone()); + List fields = idents.subList(0, fieldCount); + List methods = idents.subList(fieldCount, idents.size()); + Collections.sort(fields, identCmp); + Collections.sort(methods, identCmp); + + for (Iterator i = fields.iterator(); i.hasNext();) { + FieldIdentifier field = (FieldIdentifier) i.next(); + modifiers = field.info.getModifiers(); + if ((modifiers & Modifier.PRIVATE) != 0 + && (modifiers & (Modifier.STATIC + | Modifier.TRANSIENT)) != 0) + continue; + + out.writeUTF(field.getName()); + out.writeInt(modifiers); + out.writeUTF(field.getType()); + } + for(Iterator i = methods.iterator(); i.hasNext(); ) { + MethodIdentifier method = (MethodIdentifier) i.next(); + modifiers = method.info.getModifiers(); + if( Modifier.isPrivate(modifiers)) + continue; + + out.writeUTF(method.getName()); + out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + out.writeUTF(method.getType().replace('/', '.')); + } + + out.close(); + + byte[] sha = md.digest(); + long result = 0; + for (int i=0; i < 8; i++) { + result += (long)(sha[i] & 0xFF) << (8 * i); + } + return result; + } catch (IOException ex) { + ex.printStackTrace(); + GlobalOptions.err.println("Can't calculate serialVersionUID"); + return 0L; + } + } + /** * Preserve all fields, that are necessary, to serialize * a compatible class. @@ -145,16 +255,46 @@ public class ClassIdentifier extends Identifier { public void preserveSerializable() { preserveIdentifier("writeObject", "(Ljava.io.ObjectOutputStream)V"); preserveIdentifier("readObject", "(Ljava.io.ObjectOutputStream)V"); - if (Obfuscator.preserveSerial) { - /* XXX - add a field serializableVersionUID if not existent */ - preserveIdentifier("serializableVersionUID", "I"); + if ((Main.options & Main.OPTION_PRESERVESERIAL) != 0) { + setPreserved(); + boolean hasSerialUID = false; + for (int i=0; i< fieldCount; i++) { + if ("serialVersionUID".equals(identifiers[i].getName()) + && "J".equals(identifiers[i].getType())) { + identifiers[i].setReachable(); + identifiers[i].setPreserved(); + hasSerialUID = true; + break; + } + } + if (!hasSerialUID) { + /* add a field serializableVersionUID if not existent */ + long serialVersion = calcSerialVersionUID(); + Identifier[] newIdents = new Identifier[identifiers.length+1]; + System.arraycopy(identifiers, 0, newIdents, 0, fieldCount); + System.arraycopy(identifiers, fieldCount, + newIdents, fieldCount + 1, + identifiers.length - fieldCount); + FieldInfo UIDField = new FieldInfo + (info, "serialVersionUID", "J", + Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); + UIDField.setConstant(new Long(serialVersion)); + FieldIdentifier fident = new FieldIdentifier(this, UIDField); + fident.setPreserved(); + fident.setReachable(); + newIdents[fieldCount++] = fident; + identifiers = newIdents; + } for (int i=0; i < fieldCount; i++) { FieldIdentifier ident = (FieldIdentifier) identifiers[i]; if ((ident.info.getModifiers() - & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) - identifiers[i].setPreserved(); + & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { + ident.setPreserved(); + ident.setNotConstant(); + } /* XXX - only preserve them if writeObject not existent - * or if writeObject calls defaultWriteObject + * or if writeObject calls defaultWriteObject, and similar + * for readObject */ } } @@ -169,16 +309,16 @@ public class ClassIdentifier extends Identifier { public void setSingleReachable() { super.setSingleReachable(); - bundle.analyzeIdentifier(this); + Main.getClassBundle().analyzeIdentifier(this); } public void analyzeSuperClasses(ClassInfo superclass) { while (superclass != null) { - if (superclass.getName().equals("java.lang.Serializable")) + if (superclass.getName().equals("java.io.Serializable")) preserveSerializable(); - ClassIdentifier superident - = bundle.getClassIdentifier(superclass.getName()); + ClassIdentifier superident = Main.getClassBundle() + .getClassIdentifier(superclass.getName()); if (superident != null) { superident.addSubClass(this); } else { @@ -217,8 +357,8 @@ public class ClassIdentifier extends Identifier { if (superclass.getName().equals("java.lang.Serializable")) preserveSerializable(); - ClassIdentifier superident - = bundle.getClassIdentifier(superclass.getName()); + ClassIdentifier superident = Main.getClassBundle() + .getClassIdentifier(superclass.getName()); if (superident != null) { for (int i=superident.fieldCount; i < superident.identifiers.length; i++) { @@ -261,7 +401,7 @@ public class ClassIdentifier extends Identifier { FieldInfo[] finfos = info.getFields(); MethodInfo[] minfos = info.getMethods(); - if (Obfuscator.swapOrder) { + if (Main.swapOrder) { Random rand = new Random(); Collections.shuffle(Arrays.asList(finfos), rand); Collections.shuffle(Arrays.asList(minfos), rand); @@ -275,7 +415,7 @@ public class ClassIdentifier extends Identifier { identifiers[fieldCount + i] = new MethodIdentifier(this, minfos[i]); if (identifiers[fieldCount + i].getName().equals("")) { - /* If there is a static initializer, it is automagically + /* If there is a static initializer, it is automatically * reachable (even if this class wouldn't be otherwise). */ identifiers[fieldCount + i].setPreserved(); @@ -289,18 +429,26 @@ public class ClassIdentifier extends Identifier { ifaceNames = new String[ifaces.length]; for (int i=0; i < ifaces.length; i++) { ifaceNames[i] = ifaces[i].getName(); - ClassIdentifier ifaceident - = bundle.getClassIdentifier(ifaceNames[i]); + ClassIdentifier ifaceident = Main.getClassBundle() + .getClassIdentifier(ifaceNames[i]); initSuperClasses(ifaces[i]); } if (info.getSuperclass() != null) { superName = info.getSuperclass().getName(); - ClassIdentifier superident - = bundle.getClassIdentifier(superName); + ClassIdentifier superident = Main.getClassBundle() + .getClassIdentifier(superName); initSuperClasses(info.getSuperclass()); } + if ((Main.stripping & Main.STRIP_SOURCE) != 0) { + info.setSourceFile(null); + } + if ((Main.stripping & Main.STRIP_INNERINFO) != 0) { + info.setInnerClasses(new InnerClassInfo[0]); + info.setOuterClasses(new InnerClassInfo[0]); + info.setExtraClasses(new InnerClassInfo[0]); + } // load inner classes InnerClassInfo[] innerClasses = info.getInnerClasses(); InnerClassInfo[] outerClasses = info.getOuterClasses(); @@ -308,53 +456,69 @@ public class ClassIdentifier extends Identifier { if (outerClasses != null) { for (int i=0; i < outerClasses.length; i++) { if (outerClasses[i].outer != null) { - bundle.getClassIdentifier(outerClasses[i].outer); + Main.getClassBundle() + .getClassIdentifier(outerClasses[i].outer); } } } if (innerClasses != null) { for (int i=0; i < innerClasses.length; i++) { - bundle.getClassIdentifier(innerClasses[i].inner); + Main.getClassBundle() + .getClassIdentifier(innerClasses[i].inner); } } if (extraClasses != null) { for (int i=0; i < extraClasses.length; i++) { - bundle.getClassIdentifier(extraClasses[i].inner); + Main.getClassBundle() + .getClassIdentifier(extraClasses[i].inner); if (extraClasses[i].outer != null) - bundle.getClassIdentifier(extraClasses[i].outer); + Main.getClassBundle() + .getClassIdentifier(extraClasses[i].outer); } } } - public void buildTable(int renameRule) { + public void buildTable(Renamer renameRule) { super.buildTable(renameRule); for (int i=0; i < identifiers.length; i++) - if (!Obfuscator.shouldStrip || identifiers[i].isReachable()) + if ((Main.stripping & Main.STRIP_UNREACH) == 0 + || identifiers[i].isReachable()) identifiers[i].buildTable(renameRule); } - public void readTable(Hashtable table) { + public void readTable(Map table) { super.readTable(table); for (int i=0; i < identifiers.length; i++) - if (!Obfuscator.shouldStrip || identifiers[i].isReachable()) + if ((Main.stripping & Main.STRIP_UNREACH) == 0 + || identifiers[i].isReachable()) identifiers[i].readTable(table); } - public void writeTable(Hashtable table) { + public void writeTable(Map table) { super.writeTable(table); for (int i=0; i < identifiers.length; i++) - if (!Obfuscator.shouldStrip || identifiers[i].isReachable()) + if ((Main.stripping & Main.STRIP_UNREACH) == 0 + || identifiers[i].isReachable()) identifiers[i].writeTable(table); } - public void addIfaces(Vector result, String[] ifaces) { + /** + * Add the ClassInfo objects of the interfaces of ancestor. But if + * an interface of ancestor is not reachable it will add its interfaces + * instead. + * @param result The Collection where the interfaces should be added to. + * @param ancestor The ancestor whose interfaces should be added. + */ + public void addIfaces(Collection result, ClassIdentifier ancestor) { + String[] ifaces = ancestor.ifaceNames; + ClassInfo[] ifaceInfos = ancestor.info.getInterfaces(); for (int i=0; i < ifaces.length; i++) { ClassIdentifier ifaceident - = bundle.getClassIdentifier(ifaces[i]); + = Main.getClassBundle().getClassIdentifier(ifaces[i]); if (ifaceident != null && !ifaceident.isReachable()) - addIfaces(result, ifaceident.ifaceNames); + addIfaces(result, ifaceident); else - result.addElement(ClassInfo.forName(ifaces[i])); + result.add(ifaceInfos[i]); } } @@ -366,24 +530,23 @@ public class ClassIdentifier extends Identifier { * other entries are the interfaces. */ public void transformSuperIfaces() { - if (!Obfuscator.shouldStrip) + if ((Main.stripping & Main.STRIP_UNREACH) == 0) return; - Vector newIfaces = new Vector(); - addIfaces(newIfaces, ifaceNames); - String nameOfSuper = superName; - while (true) { - ClassIdentifier superident - = bundle.getClassIdentifier(nameOfSuper); + Collection newIfaces = new LinkedList(); + ClassIdentifier ancestor = this; + while(true) { + addIfaces(newIfaces, ancestor); + ClassIdentifier superident + = Main.getClassBundle().getClassIdentifier(ancestor.superName); if (superident == null || superident.isReachable()) break; - - addIfaces(newIfaces, superident.ifaceNames); - nameOfSuper = superident.superName; + ancestor = superident; } - ClassInfo[] ifaces = new ClassInfo[newIfaces.size()]; - newIfaces.copyInto(ifaces); - info.setSuperclass(ClassInfo.forName(nameOfSuper)); + ClassInfo superInfo = ancestor.info.getSuperclass(); + ClassInfo[] ifaces = (ClassInfo[]) + newIfaces.toArray(new ClassInfo[newIfaces.size()]); + info.setSuperclass(superInfo); info.setInterfaces(ifaces); } @@ -391,11 +554,11 @@ public class ClassIdentifier extends Identifier { InnerClassInfo[] outerClasses = info.getOuterClasses(); if (outerClasses != null) { int newOuterCount = outerClasses.length; - if (Obfuscator.shouldStrip) { + if ((Main.stripping & Main.STRIP_UNREACH) != 0) { for (int i=0; i < outerClasses.length; i++) { if (outerClasses[i].outer != null) { - ClassIdentifier outerIdent - = bundle.getClassIdentifier(outerClasses[i].outer); + ClassIdentifier outerIdent = Main.getClassBundle() + .getClassIdentifier(outerClasses[i].outer); if (outerIdent != null && !outerIdent.isReachable()) newOuterCount--; } @@ -409,7 +572,8 @@ public class ClassIdentifier extends Identifier { String lastClass = getFullAlias(); for (int i=0; i