diff --git a/jode/jode/expr/ClassFieldOperator.java b/jode/jode/expr/ClassFieldOperator.java new file mode 100644 index 0000000..5b99635 --- /dev/null +++ b/jode/jode/expr/ClassFieldOperator.java @@ -0,0 +1,40 @@ +/* + * ClassFieldOperator (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.decompiler; +import jode.Type; +import jode.CodeAnalyzer; + +public class ClassFieldOperator extends NoArgOperator { + Type classType; + + public ClassFieldOperator(Type classType) { + super(Type.tJavaLangClass); + this.classType = classType; + classType.useType(); + } + + public int getPriority() { + return 950; + } + + public String toString(String[] operands) { + return classType.toString() + ".class"; + } +} diff --git a/jode/jode/obfuscator/ClassIdentifier.java b/jode/jode/obfuscator/ClassIdentifier.java new file mode 100644 index 0000000..6c8983e --- /dev/null +++ b/jode/jode/obfuscator/ClassIdentifier.java @@ -0,0 +1,391 @@ +/* + * ClassIdentifier (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.obfuscator; +import jode.Obfuscator; +import jode.bytecode.ClassInfo; +import jode.bytecode.FieldInfo; +import jode.bytecode.MethodInfo; +import java.lang.reflect.Modifier; +import java.util.*; +import java.io.*; + +public class ClassIdentifier extends Identifier { + ClassBundle bundle; + PackageIdentifier pack; + String name; + ClassInfo info; + + int preserveRule; + Identifier[] identifiers; + Vector knownSubClasses = new Vector(); + Vector virtualReachables = new Vector(); + + public ClassIdentifier(ClassBundle bundle, PackageIdentifier pack, + String name, ClassInfo info) { + super(name); + this.bundle = bundle; + this.pack = pack; + this.name = name; + this.info = info; + + preserveRule = bundle.preserveRule; + if ((preserveRule & (info.getModifiers() ^ Modifier.PRIVATE)) != 0) { + setReachable(); + setPreserved(); + } + } + + public void addSubClass(ClassIdentifier ci) { + knownSubClasses.addElement(ci); + Enumeration enum = virtualReachables.elements(); + while (enum.hasMoreElements()) { + String[] method = (String[]) enum.nextElement(); + ci.reachableIdentifier(method[0], method[1], true); + } + } + + public void preserveIdentifier(String name, String typeSig) { + for (int i=0; i< identifiers.length; i++) { + if (identifiers[i].getName().equals(name) + && (typeSig == null + || identifiers[i] .getType().equals(typeSig))) + identifiers[i].setPreserved(); + } + } + + public void reachableIdentifier(String name, String typeSig, + boolean isVirtual) { + for (int i=0; i < identifiers.length; i++) { + if (identifiers[i].getName().equals(name) + && (typeSig == null + || identifiers[i] .getType().equals(typeSig))) + identifiers[i].setReachable(); + } + if (isVirtual) { + Enumeration enum = knownSubClasses.elements(); + while (enum.hasMoreElements()) + ((ClassIdentifier)enum.nextElement()) + .reachableIdentifier(name, typeSig, isVirtual); + virtualReachables.addElement + (new String[] { name, typeSig }); + } + } + + public void chainIdentifier(Identifier ident) { + String name = ident.getName(); + String typeSig = ident.getType(); + for (int i=0; i< identifiers.length; i++) { + if (identifiers[i].getName().equals(ident.getName()) + && (identifiers[i].getType().equals(typeSig))) + ident.addShadow(identifiers[i]); + } + } + + /** + * Preserve all fields, that are necessary, to serialize + * a compatible class. + */ + public void preserveSerializable() { + /*XXX*/ + /* add a field serializableVersionUID if not existent */ + } + + public void initSuperClasses(ClassInfo superclass) { + while (superclass != null) { + if (superclass.getName().equals("java.lang.Serializable")) + preserveSerializable(); + + ClassIdentifier superident = (ClassIdentifier) + bundle.getIdentifier(superclass.getName()); + if (superident != null) { + for (int i=0; i < superident.identifiers.length; i++) + if (superident.identifiers[i] + instanceof MethodIdentifier) { + MethodIdentifier mid = (MethodIdentifier) + superident.identifiers[i]; + // all virtual methods in superclass must be chained. + int modif = mid.info.getModifiers(); + if (((Modifier.PRIVATE + | Modifier.STATIC + | Modifier.FINAL) & modif) == 0 + && !(mid.getName().equals(""))) { + // chain the preserved/same name lists. + chainIdentifier(superident.identifiers[i]); + } + } + } else { + // all methods and fields in superclass are preserved! + MethodInfo[] topmethods = superclass.getMethods(); + FieldInfo[] topfields = superclass.getFields(); + for (int i=0; i< topmethods.length; i++) { + // all virtual methods in superclass may be + // virtually reachable + int modif = topmethods[i].getModifiers(); + if (((Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL) + & modif) == 0 + && !topmethods[i].getName().equals("")) { + reachableIdentifier + (topmethods[i].getName(), + topmethods[i].getType().getTypeSignature(), + true); + preserveIdentifier + (topmethods[i].getName(), + topmethods[i].getType().getTypeSignature()); + } + } + } + ClassInfo[] ifaces = superclass.getInterfaces(); + for (int i=0; i < ifaces.length; i++) + initSuperClasses(ifaces[i]); + superclass = superclass.getSuperclass(); + } + } + + public void setSingleReachable() { + info.loadInfo(info.FULLINFO); + + if (Obfuscator.isVerbose) + Obfuscator.err.println(this); + + FieldInfo[] finfos = info.getFields(); + MethodInfo[] minfos = info.getMethods(); + identifiers = new Identifier[finfos.length + minfos.length]; + for (int i=0; i< finfos.length; i++) { + identifiers[i] = new FieldIdentifier(this, finfos[i]); + if ((preserveRule & (finfos[i].getModifiers() ^ Modifier.PRIVATE)) + != 0) { + identifiers[i].setReachable(); + identifiers[i].setPreserved(); + } + } + for (int i=0; i< minfos.length; i++) { + identifiers[finfos.length + i] + = new MethodIdentifier(this, minfos[i]); + if ((preserveRule & (minfos[i].getModifiers() ^ Modifier.PRIVATE)) + != 0) { + identifiers[i].setReachable(); + identifiers[i].setPreserved(); + } + if (identifiers[i].getName().equals("")) + identifiers[i].setPreserved(); + } + + ClassInfo[] ifaces = info.getInterfaces(); + for (int i=0; i < ifaces.length; i++) { + ClassIdentifier ifaceident = (ClassIdentifier) + bundle.getIdentifier(ifaces[i].getName()); + if (ifaceident != null) { + ifaceident.setReachable(); + ifaceident.addSubClass(this); + } + initSuperClasses(ifaces[i]); + } + + if (info.getSuperclass() != null) { + ClassIdentifier superident = (ClassIdentifier) + bundle.getIdentifier(info.getSuperclass().getName()); + if (superident != null) { + superident.setReachable(); + superident.addSubClass(this); + } + initSuperClasses(info.getSuperclass()); + } + preserveIdentifier("", "()V"); + } + + public void strip() { + int newLength = 0; + for (int i=0; i < identifiers.length; i++) { + if (identifiers[i].isReachable()) + newLength++; + else { + if (Obfuscator.isDebugging) + Obfuscator.err.println(identifiers[i].toString() + + " is stripped"); + } + } + Identifier[] newIdents = new Identifier[newLength]; + int index = 0; + for (int i=0; i < identifiers.length; i++) { + if (identifiers[i].isReachable()) + newIdents[index++] = identifiers[i]; + } + identifiers = newIdents; + } + + public void buildTable(int renameRule) { + super.buildTable(renameRule); + for (int i=0; i < identifiers.length; i++) + identifiers[i].buildTable(renameRule); + } + + public void writeTable(PrintWriter out) throws IOException { + if (getName() != getAlias()) + out.println("" + getFullAlias() + " = " + getName()); + for (int i=0; i < identifiers.length; i++) + identifiers[i].writeTable(out); + } + + public void storeClass(OutputStream out) throws IOException { + /*XXX*/ + } + + /** + * @return the full qualified name, excluding trailing dot. + */ + public String getFullName() { + return pack.getFullName() + getName(); + } + + /** + * @return the full qualified alias, excluding trailing dot. + */ + public String getFullAlias() { + return pack.getFullAlias() + getAlias(); + } + + public String getName() { + return name; + } + + public String getType() { + return "Ljava/lang/Class;"; + } + + public String toString() { + return "ClassIdentifier "+getFullName(); + } + + public static boolean containsField(ClassInfo clazz, String newAlias) { + FieldInfo[] finfos = clazz.getFields(); + for (int i=0; i< finfos.length; i++) { + if (finfos[i].getName().equals(newAlias)) + return true; + } + + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i=0; i < ifaces.length; i++) { + if (containsField(ifaces[i], newAlias)) + return true; + } + + if (clazz.getSuperclass() != null) { + if (containsField(clazz.getSuperclass(), newAlias)) + return true; + } + return false; + } + + public boolean containsField(String newAlias) { + for (int i=0; i< identifiers.length; i++) { + if (identifiers[i] instanceof FieldIdentifier + && identifiers[i].getAlias().equals(newAlias)) + return true; + } + + ClassInfo[] ifaces = info.getInterfaces(); + for (int i=0; i < ifaces.length; i++) { + ClassIdentifier ifaceident = (ClassIdentifier) + bundle.getIdentifier(ifaces[i].getName()); + if (ifaceident != null) { + if (ifaceident.containsField(newAlias)) + return true; + } else { + if (containsField(ifaces[i], newAlias)) + return true; + } + } + + if (info.getSuperclass() != null) { + ClassIdentifier superident = (ClassIdentifier) + bundle.getIdentifier(info.getSuperclass().getName()); + if (superident != null) { + if (superident.containsField(newAlias)) + return true; + } else { + if (containsField(info.getSuperclass(), newAlias)) + return true; + } + } + return false; + } + + public static boolean containsMethod(ClassInfo clazz, + String newAlias, String paramType) { + MethodInfo[] minfos = clazz.getMethods(); + for (int i=0; i< minfos.length; i++) { + if (minfos[i].getName().equals(newAlias) + && minfos[i].getType().getTypeSignature() + .startsWith(paramType)) + return true; + } + + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i=0; i < ifaces.length; i++) { + if (containsMethod(ifaces[i], newAlias, paramType)) + return true; + } + + if (clazz.getSuperclass() != null) { + if (containsMethod(clazz.getSuperclass(), newAlias, paramType)) + return true; + } + return false; + } + + public boolean containsMethod(String newAlias, String paramType) { + for (int i=0; i< identifiers.length; i++) { + if (identifiers[i] instanceof MethodIdentifier + && identifiers[i].getAlias().equals(newAlias) + && identifiers[i].getType().startsWith(paramType)) + return true; + } + + ClassInfo[] ifaces = info.getInterfaces(); + for (int i=0; i < ifaces.length; i++) { + ClassIdentifier ifaceident = (ClassIdentifier) + bundle.getIdentifier(ifaces[i].getName()); + if (ifaceident != null) { + if (ifaceident.containsMethod(newAlias, paramType)) + return true; + } else { + if (containsMethod(ifaces[i], newAlias, paramType)) + return true; + } + } + + if (info.getSuperclass() != null) { + ClassIdentifier superident = (ClassIdentifier) + bundle.getIdentifier(info.getSuperclass().getName()); + if (superident != null) { + if (superident.containsMethod(newAlias, paramType)) + return true; + } else { + if (containsMethod(info.getSuperclass(), newAlias, paramType)) + return true; + } + } + return false; + } + + public boolean conflicting(String newAlias) { + return pack.contains(newAlias); + } +} diff --git a/jode/jode/obfuscator/FieldIdentifier.java b/jode/jode/obfuscator/FieldIdentifier.java new file mode 100644 index 0000000..45c238d --- /dev/null +++ b/jode/jode/obfuscator/FieldIdentifier.java @@ -0,0 +1,56 @@ +/* + * jode.obfuscator.FieldIdentifier (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ +package jode.obfuscator; +import jode.bytecode.FieldInfo; + +public class FieldIdentifier extends Identifier{ + FieldInfo info; + ClassIdentifier clazz; + + public FieldIdentifier(ClassIdentifier clazz, FieldInfo info) { + super(info.getName()); + this.info = info; + this.clazz = clazz; + } + + public String getFullName() { + return clazz.getFullName() + "." + getName(); + } + + public String getFullAlias() { + return clazz.getFullAlias() + "." + getAlias(); + } + + public String getName() { + return info.getName(); + } + + public String getType() { + return info.getType().getTypeSignature(); + } + + public String toString() { + return "MethodIdentifier "+getFullName()+"."+getType(); + } + + public boolean conflicting(String newAlias) { + return clazz.containsField(newAlias) + || clazz.containsMethod(newAlias, ""); + } +} diff --git a/jode/jode/obfuscator/LoadedClassIdentifier.java b/jode/jode/obfuscator/LoadedClassIdentifier.java new file mode 100644 index 0000000..14d6064 --- /dev/null +++ b/jode/jode/obfuscator/LoadedClassIdentifier.java @@ -0,0 +1,28 @@ +/* + * LoadedClassIdentifier (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.obfuscator; +import jode.Obfuscator; +import jode.bytecode.ClassInfo; +import jode.bytecode.FieldInfo; +import jode.bytecode.MethodInfo; +import java.lang.reflect.Modifier; +import java.util.*; +import java.io.*; + diff --git a/jode/jode/obfuscator/MethodIdentifier.java b/jode/jode/obfuscator/MethodIdentifier.java new file mode 100644 index 0000000..7bcb7f8 --- /dev/null +++ b/jode/jode/obfuscator/MethodIdentifier.java @@ -0,0 +1,141 @@ +/* + * MethodIdentifier (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ +package jode.obfuscator; +import jode.Obfuscator; +import jode.bytecode.*; +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; + +public class MethodIdentifier extends Identifier { + ClassIdentifier clazz; + MethodInfo info; + + public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) { + super(info.getName()); + this.clazz = clazz; + this.info = info; + } + + public Vector analyzeCode(CodeInfo codeinfo) { + Vector references = new Vector(); + byte[] code = codeinfo.getCode(); + try { + DataInputStream stream = + new DataInputStream(new ByteArrayInputStream(code)); + Opcodes.getReferences(stream, references); + } catch (IOException ex) { + ex.printStackTrace(); + throw new ClassFormatError(ex.getMessage()); + } + return references; + } + + public void setSingleReachable() { + super.setSingleReachable(); + + if (Obfuscator.isDebugging) + Obfuscator.err.println("Reachable: "+this); + + ConstantPool cp = clazz.info.getConstantPool(); + AttributeInfo codeattr = info.findAttribute("Code"); + if (codeattr == null) + return; + + DataInputStream stream = new DataInputStream + (new ByteArrayInputStream(codeattr.getContents())); + CodeInfo codeinfo = new CodeInfo(); + try { + codeinfo.read(clazz.info.getConstantPool(), stream); + Vector references = analyzeCode(codeinfo); + Enumeration enum = references.elements(); + while (enum.hasMoreElements()) { + int[] ref = (int[]) enum.nextElement(); + int tag = cp.getTag(ref[0]); + switch (tag) { + case ConstantPool.FIELDREF: + case ConstantPool.METHODREF: + case ConstantPool.INTERFACEMETHODREF: { + String[] names = cp.getRef(ref[0]); + clazz.bundle.reachableIdentifier + (names[0].replace('/','.')+"."+names[1]+"."+names[2], + ref[1] == 1); + break; + } + case ConstantPool.CLASS: { + String clName = cp.getClassName(ref[0]).replace('/','.'); + if (clName.charAt(0) == '[') { + int i; + for (i=0; i< clName.length(); i++) + if (clName.charAt(i) != '[') + break; + if (i >= clName.length() || clName.charAt(i) != 'L') + break; + int index = clName.indexOf(';', i); + if (index != clName.length()-1) + break; + clName = clName.substring(i+1, index); + } + clazz.bundle.reachableIdentifier(clName, false); + break; + } + default: + throw new jode.AssertError + ("Unknown reference: "+ref+" tag: "+tag); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void writeTable(PrintWriter out) throws IOException { + if (getName() != getAlias()) + out.println("" + getFullAlias() + + "." + clazz.bundle.getTypeAlias(getType()) + + " = " + getName()); + } + + public String getFullName() { + return clazz.getFullName() + "." + getName(); + } + + public String getFullAlias() { + return clazz.getFullAlias() + "." + getAlias(); + } + + public String getName() { + return info.getName(); + } + + public String getType() { + return info.getType().getTypeSignature(); + } + + public boolean conflicting(String newAlias) { + String type = getType(); + String paramType = type.substring(0, type.indexOf(')')+1); + return clazz.containsMethod(newAlias, paramType) + || clazz.containsField(newAlias); + } + + public String toString() { + return "MethodIdentifier "+getFullName()+"."+getType(); + } +} diff --git a/jode/jode/obfuscator/PackageIdentifier.java b/jode/jode/obfuscator/PackageIdentifier.java new file mode 100644 index 0000000..de40912 --- /dev/null +++ b/jode/jode/obfuscator/PackageIdentifier.java @@ -0,0 +1,301 @@ +/* + * PackageIdentifier (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.obfuscator; +import jode.Obfuscator; +import jode.bytecode.ClassInfo; +import jode.bytecode.FieldInfo; +import jode.bytecode.MethodInfo; +import java.lang.reflect.Modifier; +import java.util.*; +import java.io.*; + +public class PackageIdentifier extends Identifier { + ClassBundle bundle; + PackageIdentifier parent; + String name; + + boolean loadOnDemand; + Hashtable loadedClasses; + + public PackageIdentifier(ClassBundle bundle, + PackageIdentifier parent, + String name, boolean loadOnDemand) { + super(name); + this.bundle = bundle; + this.parent = parent; + this.name = name; + this.loadOnDemand = loadOnDemand; + this.loadedClasses = new Hashtable(); + } + + public Identifier getIdentifier(String name) { + if (loadOnDemand) + return loadClass(name); + + int index = name.indexOf('.'); + if (index == -1) + return (Identifier) loadedClasses.get(name); + else { + PackageIdentifier pack = (PackageIdentifier) + loadedClasses.get(name.substring(0, index)); + if (pack != null) + return pack.getIdentifier(name.substring(index+1)); + else + return null; + } + } + + public Identifier loadClass(String name) { + int index = name.indexOf('.'); + if (index == -1) { + Identifier ident = (Identifier) loadedClasses.get(name); + if (ident == null) { + String fullname = getFullName() + name; + if (ClassInfo.isPackage(fullname)) { + ident = new PackageIdentifier(bundle, this, name, true); + } else if (!ClassInfo.exists(fullname)) { + throw new IllegalArgumentException + ("Can't find class"+fullname); + } else { + ident = new ClassIdentifier(bundle, this, name, + ClassInfo.forName(fullname)); + } + loadedClasses.put(name, ident); + } + return ident; + } else { + String subpack = name.substring(0, index); + PackageIdentifier pack = + (PackageIdentifier) loadedClasses.get(subpack); + if (pack == null) { + pack = new PackageIdentifier(bundle, this, + subpack, loadOnDemand); + loadedClasses.put(subpack, pack); + } + + if (pack != null) + return pack.getIdentifier(name.substring(index+1)); + else + return null; + } + } + + public Identifier loadClasses(String packageOrClass) { + int index = packageOrClass.indexOf('.'); + if (index == -1) { + return loadClass(packageOrClass); + } else { + String subpack = packageOrClass.substring(0, index); + PackageIdentifier pack = (PackageIdentifier) + loadedClasses.get(subpack); + if (pack == null) { + pack = new PackageIdentifier(bundle, this, + subpack, loadOnDemand); + loadedClasses.put(subpack, pack); + } + return pack.loadClasses(packageOrClass.substring(index+1)); + } + } + + public void reachableIdentifier(String fqn, boolean isVirtual) { + int index = fqn.indexOf('.'); + String component = index == -1 ? fqn : fqn.substring(0, index); + Identifier ident = getIdentifier(component); + if (ident == null) + return; + + ident.setReachable(); + if (index == -1) + return; + + if (ident instanceof PackageIdentifier) + ((PackageIdentifier) ident).reachableIdentifier + (fqn.substring(index+1), isVirtual); + else { + String method = fqn.substring(index+1); + index = method.indexOf('.'); + if (index == -1) { + ((ClassIdentifier) ident).reachableIdentifier + (method, null, isVirtual); + } else { + ((ClassIdentifier) ident).reachableIdentifier + (method.substring(0, index), method.substring(index+1), + isVirtual); + } + } + } + + public void preserveIdentifier(String fqn) { + int index = fqn.indexOf('.'); + String component = index == -1 ? fqn : fqn.substring(0, index); + Identifier ident = getIdentifier(component); + if (ident == null) + return; + ident.setReachable(); + ident.setPreserved(); + if (index == -1) + return; + + if (ident instanceof PackageIdentifier) + ((PackageIdentifier) ident).preserveIdentifier + (fqn.substring(index+1)); + else { + String method = fqn.substring(index+1); + index = method.indexOf('.'); + if (index == -1) { + ((ClassIdentifier) ident).reachableIdentifier + (method, null, false); + ((ClassIdentifier) ident).preserveIdentifier + (method, null); + } else { + ((ClassIdentifier) ident).reachableIdentifier + (method.substring(0, index), method.substring(index+1), + false); + ((ClassIdentifier) ident).preserveIdentifier + (method.substring(0, index), method.substring(index+1)); + } + } + } + + /** + * @return the full qualified name, including trailing dot. + */ + public String getFullName() { + if (parent != null) + return parent.getFullName() + getName() + "."; + else + return ""; + } + + /** + * @return the full qualified alias, including trailing dot. + */ + public String getFullAlias() { + if (parent != null) + return parent.getFullAlias() + getAlias() + "."; + else + return ""; + } + + public String findAlias(String className) { + int index = className.indexOf('.'); + if (index == -1) { + Identifier ident = getIdentifier(className); + if (ident != null) + return ident.getFullAlias(); + } else { + Identifier pack = getIdentifier(className.substring(0, index)); + if (pack != null) + return ((PackageIdentifier)pack) + .findAlias(className.substring(index+1)); + } + return className; + } + + public void strip() { + Hashtable ht = new Hashtable(); + Enumeration enum = loadedClasses.elements(); + while (enum.hasMoreElements()) { + Identifier ident = (Identifier) enum.nextElement(); + if (ident.isReachable()) { + ht.put(ident.getName(), ident); + if (ident instanceof ClassIdentifier) { + ((ClassIdentifier) ident).strip(); + } else + ((PackageIdentifier) ident).strip(); + } else { + if (Obfuscator.isDebugging) + Obfuscator.err.println("Class/Package " + + ident.getFullName() + + " is not reachable"); + } + } + loadedClasses = ht; + } + + public void buildTable(int renameRule) { + super.buildTable(renameRule); + Enumeration enum = loadedClasses.elements(); + while (enum.hasMoreElements()) { + Identifier ident = (Identifier) enum.nextElement(); + if (ident instanceof ClassIdentifier) { + ((ClassIdentifier) ident).buildTable(renameRule); + } else + ((PackageIdentifier) ident).buildTable(renameRule); + } + } + + public void writeTable(PrintWriter out) throws IOException { + if (parent != null && getName() != getAlias()) + out.println("" + parent.getFullAlias() + getAlias() + + " = " + getName()); + Enumeration enum = loadedClasses.elements(); + while (enum.hasMoreElements()) { + ((Identifier)enum.nextElement()).writeTable(out); + } + } + + public String getName() { + return name; + } + + public String getType() { + return "package"; + } + + public void storeClasses(File destination) { + File newDest = (parent == null) ? destination + : new File(destination, getName()); + Enumeration enum = loadedClasses.elements(); + while (enum.hasMoreElements()) { + Identifier ident = (Identifier) enum.nextElement(); + if (ident instanceof PackageIdentifier) + ((PackageIdentifier) ident) + .storeClasses(newDest); + else { + try { + ((ClassIdentifier) ident).storeClass(null); + } catch (java.io.IOException ex) { + Obfuscator.err.println("Can't write Class " + + ident.getName()); + ex.printStackTrace(); + } + } + } + } + + public String toString() { + return (parent == null) ? "base package" + : parent.getFullName()+getName(); + } + + public boolean contains(String newAlias) { + Enumeration enum = loadedClasses.elements(); + while (enum.hasMoreElements()) { + if (((Identifier)enum.nextElement()).getAlias().equals(newAlias)) + return true; + } + return false; + } + + public boolean conflicting(String newAlias) { + return parent.contains(newAlias); + } +}