ClassFile.java :  » Development » javaguard » net » sf » javaguard » classfile » Java Open Source

Java Open Source » Development » javaguard 
javaguard » net » sf » javaguard » classfile » ClassFile.java
/**
 * JavaGuard -- an obfuscation package for Java classfiles.
 *
 * Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
 * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * The author may be contacted at theit@gmx.de.
 *
 *
 * $Id: ClassFile.java,v 1.9 2002/06/04 09:56:47 glurk Exp $
 */
package net.sf.javaguard.classfile;

import java.io.*;
import java.util.*;
import net.sf.javaguard.Cl;
import net.sf.javaguard.KeyValue;
import net.sf.javaguard.ClassTree;
import net.sf.javaguard.log.FileLogger;
import net.sf.javaguard.Tools;

/** This is a representation of the data in a Java class file (*.class).
 * A ClassFile instance representing a *.class file can be generated
 * using the static create(DataInput) method, manipulated using various
 * operators, and persisted back using the write(DataOutput) method.
 *
 * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
 * @author <a href="mailto:markw@retrologic.com">Mark Welsh</a>
 */
public class ClassFile implements ClassConstants {
  // Constants -------------------------------------------------------------
  private static final String[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
    "forName(Ljava/lang/String;)Ljava/lang/Class;",
    "getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
    "getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
    "getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
    "getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
  };
  private static final String LOG_DANGER_CLASS_PRE = "     Your class ";
  private static final String LOG_DANGER_CLASS_MID = " calls the java/lang/Class method ";
  private static final String[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
    "defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
    "findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
    "findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
    "loadClass(Ljava/lang/String;)Ljava/lang/Class;",
    "loadClass(Ljava/lang/String;Z)Ljava/lang/Class;"
  };
  private static final String LOG_DANGER_CLASSLOADER_PRE = "     Your class ";
  private static final String LOG_DANGER_CLASSLOADER_MID = " calls the java/lang/ClassLoader method ";
  
  
  /** Holds the class tree the class file is assigned to. */
  private ClassTree classTree;
  /** Holds the magic number stored in the class file (must be 0xcafebabe). */
  private int magic;
  /** Holds the minor version number. */
  private int minorVersion;
  /** Holds the major version number. */
  private int majorVersion;
  /** Holds the constant pool of the class file. */
  private ConstantPool constantPool;
  /** Holds the access flags for the class. */
  private int accessFlags;
  /** Index into the constant pool. The referenced element holds an index that
   * references the class name. */
  private int thisClassIndex;
  /** Index into the constant pool. The referenced element holds an index that
   * references the name of the super class. */
  private int superClassIndex;
  
  /** Holds the number of direct implemented interfaces. */
  private int interfacesCount;
  /** Index array of interface names in the constant pool. */
  private int interfaces[];
  /** Holds the number of direct declared fields. */
  private int fieldsCount;
  /** Holds information about the declared fields. */
  private FieldInfo fieldInfo[];
  /** Holds the number of declared methods. */
  private int methodsCount;
  /** Holds information about the declared methods. */
  private MethodInfo methodInfo[];
  /** Holds the number of attributes for the class file. */
  private int attributesCount;
  /** Holds information about the attributes for the class file. */
  private AttrInfo attributesInfo[];
  
  
  
  
  /** Create a new ClassFile from the class file format data in the DataInput
   * stream.
   * @param classTree the class tree the class file is assigned to
   * @param din the input stream
   * @return the class file created from the input stream
   * @throws IOException if class file is corrupt or incomplete
   */
  public static ClassFile create(ClassTree classTree, DataInput din)
  throws IOException {
    if (null == din) throw new IOException("No input stream was provided.");
    ClassFile cf = new ClassFile(classTree);
    cf.read(din);
    return cf;
  }
  
  
  
  
  /** Private constructor.
   * @param classTree the class tree the class file is assigned to
   */
  private ClassFile(ClassTree classTree) {
    this.classTree = classTree;
  }
  
  
  
  
  /** Returns the class tree the class file is assigned to.
   * @return the class tree
   */
  private ClassTree getClassTree() {
    return classTree;
  }
  
  
  
  
  /** Import the class data to internal representation.
   * @param din the input stream
   * @throws IOException if an I/O error occurs
   */
  private void read(DataInput din)
  throws IOException {
    // Read the class file
    setMagic(din.readInt());
    setMinorVersion(din.readUnsignedShort());
    setMajorVersion(din.readUnsignedShort());
    
    // Check this is a valid classfile that we can handle
    if (getMagic() != MAGIC) {
      throw new IOException("Invalid magic number in class file.");
    }
    if (getMajorVersion() > MAJOR_VERSION ||
    (MAJOR_VERSION == getMajorVersion() && getMinorVersion() > MINOR_VERSION_MAX)) {
      throw new IOException("Incompatible version number for class file format: "
      + getMajorVersion() + " / " + getMinorVersion());
    }
    
    
    int u2constantPoolCount = din.readUnsignedShort();
    CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
    // Fill the constant pool, recalling the zero entry
    // is not persisted, nor are the entries following a Long or Double
    for (int i = 1; i < u2constantPoolCount; i++) {
      cpInfo[i] = CpInfo.create(din);
      if ((cpInfo[i] instanceof LongCpInfo) ||
      (cpInfo[i] instanceof DoubleCpInfo)) {
        i++;
      }
    }
    constantPool = new ConstantPool(this, cpInfo);
    
    setAccessFlags(din.readUnsignedShort());
    setClassIndex(din.readUnsignedShort());
    setSuperClassIndex(din.readUnsignedShort());
    // read the information about the implemented interfaces
    setInterfacesCount(din.readUnsignedShort());
    int[] u2interfaces = new int[getInterfacesCount()];
    for (int i = 0; i < getInterfacesCount(); i++) {
      u2interfaces[i] = din.readUnsignedShort();
    }
    setInterfaces(u2interfaces);
    // read the direct declared fields
    setFieldsCount(din.readUnsignedShort());
    FieldInfo[] fields = new FieldInfo[getFieldsCount()];
    for (int i = 0; i < getFieldsCount(); i++) {
      fields[i] = FieldInfo.create(din, this);
    }
    setFieldInfo(fields);
    // read the direct declared methods
    setMethodsCount(din.readUnsignedShort());
    MethodInfo[] methods = new MethodInfo[getMethodsCount()];
    for (int i = 0; i < getMethodsCount(); i++) {
      methods[i] = MethodInfo.create(din, this);
    }
    setMethodInfo(methods);
    // read the attributes for the class
    setAttributesCount(din.readUnsignedShort());
    AttrInfo[] attrs = new AttrInfo[getAttributesCount()];
    for (int i = 0; i < getAttributesCount(); i++) {
      attrs[i] = AttrInfo.create(din, this);
    }
    setAttributeInfo(attrs);
  }
  
  
  
  
  /** Sets the magic number of the class file (normally 0xcafebabe).
   * @param num the magic number
   * @see #getMagic
   */
  private void setMagic(int num) {
    this.magic = num;
  }
  
  
  /** Returns the magic number of the class file (normally 0xcafebabe).
   * @return magic number
   * @see #setMagic
   */
  private int getMagic() {
    return magic;
  }
  
  
  
  
  /** Sets the major version of the class file.
   * @param ver major class file version
   * @see #getMajorVersion
   */
  private void setMajorVersion(int ver) {
    majorVersion = ver;
  }
  
  
  /** Returns the major version of the class file.
   * @return major class file version
   * @see #setMajorVersion
   */
  private int getMajorVersion() {
    return majorVersion;
  }
  
  
  
  
  /** Sets the minor version of the class file.
   * @param ver minor class file version
   * @see #setMinorVersion
   */
  private void setMinorVersion(int ver) {
    minorVersion = ver;
  }
  
  
  /** Returns the minor version of the class file.
   * @return minor class file version
   * @see #setMinorVersion
   */
  private int getMinorVersion() {
    return minorVersion;
  }
  
  
  
  
  /** Sets the access flags for the class.
   * @param flags the access flags for the class
   * @see #getAccessFlags
   */
  private void setAccessFlags(int flags) {
    accessFlags = flags;
  }
  
  
  /** Returns the access flags for the class.
   * @return access flags for the class
   * @see #setAccessFlags
   */
  private int getAccessFlags() {
    return accessFlags;
  }
  
  
  
  
  /** Sets the index for the element in the constant pool that knows the class
   * name.
   * @param index index into the constant pool
   * @see #getClassIndex
   */
  private void setClassIndex(int index) {
    thisClassIndex = index;
  }
  
  
  /** Returns the index for the element in the constant pool that knows the
   * class name.
   * @return index into the constant pool
   * @see #setClassIndex
   */
  private int getClassIndex() {
    return thisClassIndex;
  }
  
  
  
  
  /** Sets the index for the element in the constant pool that knows the name
   * of the super class.
   * @param index index into the constant pool
   * @see #getSuperClassIndex
   */
  private void setSuperClassIndex(int index) {
    superClassIndex = index;
  }
  
  
  /** Returns the index for the element in the constant pool that knows the
   * name of the super class.
   * @return index into the constant pool
   */
  private int getSuperClassIndex() {
    return superClassIndex;
  }
  
  
  
  
  /** Sets the number of direct implemented interfaces.
   * @param num the number of direct implemented interfaces
   * @see #getInterfacesCount
   */
  private void setInterfacesCount(int num) {
    interfacesCount = num;
  }
  
  
  /** Returns the number of direct implemented interfaces.
   * @return number of direct implemented interfaces
   * @see #setInterfacesCount
   */
  private int getInterfacesCount() {
    return interfacesCount;
  }
  
  
  
  
  /** Sets the index array of interface names in the constant pool.
   * @param interfaces index array of interface names in the constant pool
   * @see #getInterfaces
   */
  private void setInterfaces(int[] interfaces) {
    this.interfaces = interfaces;
  }
  
  
  /** Returns the index array of interface names in the constant pool.
   * @return index array of interface names in the constant pool
   * @see #setInterfaces
   * @see #getInterface
   */
  private int[] getInterfaces() {
    return interfaces;
  }
  
  
  /** Return the index of the specified interface from the index array.
   * @param pos the position of the interface in the interface name index array
   * @return index into the constant pool
   * @see #getInterfaces
   */
  private int getInterface(int pos) {
    return interfaces[pos];
  }
  
  
  /** Returns an array with the names of the direct implemented interfaces.
   * @return array with interface names
   */
  public String[] getInterfaceNames() {
    String[] names = new String[getInterfacesCount()];
    for (int i=0; i<getInterfacesCount(); i++) {
      names[i] = toName(getInterface(i));
    }
    return names;
  }
  
  
  
  
  /** Sets the number of declared fields.
   * @param num number of declared fields
   * @see #getFieldsCount
   */
  private void setFieldsCount(int num) {
    fieldsCount = num;
  }
  
  
  /** Returns the number of declared fields.
   * @return number of declared fields
   */
  public int getFieldsCount() {
    return fieldsCount;
  }
  
  
  
  
  /** Sets the information about the declared fields.
   * @param fields array with information about the declared fields
   * @see #getFieldInfo
   */
  private void setFieldInfo(FieldInfo[] fields) {
    this.fieldInfo = fields;
  }
  
  
  /** Returns an array with information about the declared fields.
   * @return array with information about the declared fields
   * @see #setFieldInfo
   */
  private FieldInfo[] getFieldInfo() {
    return fieldInfo;
  }
  
  
  /** Return the information about a declared field.
   * @param pos the position in the array of declared fields
   * @return information about the declared fieldd
   * @see #getFieldInfo
   */
  public FieldInfo getFieldInfo(int pos) {
    return fieldInfo[pos];
  }
  
  
  
  
  /** Sets the number of declared methods.
   * @param num the number of declared methods
   * @see #getMethodsCount
   */
  private void setMethodsCount(int num) {
    methodsCount = num;
  }
  
  
  /** Returns the number of declared methods.
   * @return the number of declared methods
   */
  public int getMethodsCount() {
    return methodsCount;
  }
  
  
  
  
  /** Sets the information about the declared methods.
   * @param methods array with information about the declared methods
   * @see #getMethodInfo
   */
  private void setMethodInfo(MethodInfo[] methods) {
    this.methodInfo = methods;
  }
  
  
  /** Returns an array with information about the declared methods.
   * @return array with information about the declared methods
   * @see #setMethodInfo
   */
  private MethodInfo[] getMethodInfo() {
    return methodInfo;
  }
  
  
  /** Return the information about a declared method.
   * @param pos the position in the array of declared methods
   * @return information about the declared method
   * @see #getMethodInfo
   */
  public MethodInfo getMethodInfo(int pos) {
    return methodInfo[pos];
  }
  
  
  
  
  /** Sets the number of attributes for the class file.
   * @param num the number of attributes for the class file
   * @see #getAttributesCount
   */
  private void setAttributesCount(int num) {
    attributesCount = num;
  }
  
  
  /** Returns the number of attributes for the class file.
   * @return the number of attributes for the class file
   * @see #setAttributesCount
   */
  private int getAttributesCount() {
    return attributesCount;
  }
  
  
  
  
  /** Sets the information about the attributes for the class file.
   * @param attrs array with information about the attributes for the class file
   * @see #getAttributeInfo
   */
  private void setAttributeInfo(AttrInfo[] attrs) {
    this.attributesInfo = attrs;
  }
  
  
  /** Returns an array with information about the attributes for the class file.
   * @return array with information about the attributes for the class file
   * @see #setAttributeInfo
   */
  private AttrInfo[] getAttributeInfo() {
    return attributesInfo;
  }
  
  
  /** Return the information about an attribute for the class file.
   * @param pos the position in the array of attributes
   * @return information about the attribute
   * @see #getAttributeInfo
   * @see #setAttributeInfo(int, AttrInfo)
   */
  private AttrInfo getAttributeInfo(int pos) {
    return attributesInfo[pos];
  }
  
  
  /** Sets the information about a certain attribute.
   * @param pos the position in the array of attributes
   * @param attrInfo the new attribute information
   * @see #getAttributeInfo(int)
   * @see #setAttributeInfo
   */
  private void setAttributeInfo(int pos, AttrInfo attrInfo) {
    attributesInfo[pos] = attrInfo;
  }
  
  
  
  
  /** Return the name of the class file.
   * @return the name of the class file
   */
  public String getName() {
    return toName(getClassIndex());
  }
  
  
  
  
  /** Return the name of this class's super class.
   * @return the name of the super class
   */
  public String getSuper() {
    // This may be java/lang/Object, in which case there is no super
    return (0 == getSuperClassIndex()) ? null : toName(getSuperClassIndex());
  }
  
  
  
  
  /** Convert a constant pool index to a class name.
   * @param index the index into the constant pool
   * @return the name of the specified class
   * @throws IllegalStateException if an error occurs
   */
  protected String toName(int index)
  throws IllegalStateException {
    CpInfo classEntry = getCpEntry(index);
    if (classEntry instanceof ClassCpInfo) {
      CpInfo nameEntry = getCpEntry( ((ClassCpInfo) classEntry).getClassNameIndex());
      if (nameEntry instanceof Utf8CpInfo) {
        return ((Utf8CpInfo) nameEntry).getString();
      }
    }
    throw new IllegalStateException("Inconsistent Constant Pool in class file.");
  }
  
  
  
  
  /** Lookup the entry in the constant pool and return the object.
   * @param index the index into the constant pool
   * @return the entry at the specified index in the constant pool
   */
  protected CpInfo getCpEntry(int index) {
    return constantPool.getCpEntry(index);
  }
  
  
  
  
  /** Check for methods which can break the obfuscated code, and log them to
   * a string array.
   * @return string array with methods which can break the obfuscated code
   */
  public String[] dangerousMethods() {
    Vector warningVec = new Vector();
    
    // Need only check CONSTANT_Methodref entries of constant pool since
    // dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
    for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
      Object obj = iter.next();
      if (obj instanceof MethodrefCpInfo) {
        // Get the method class name, simple name and descriptor
        MethodrefCpInfo entry = (MethodrefCpInfo) obj;
        ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry.getClassIndex());
        String className = ((Utf8CpInfo) getCpEntry(classEntry.getClassNameIndex())).getString();
        NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry.getNameAndTypeIndex());
        String name = ((Utf8CpInfo) getCpEntry(ntEntry.getNameIndex())).getString();
        String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry.getDescriptorIndex())).getString();
        
        // Check if this is on the proscribed list
        if (className.equals("java/lang/Class") &&
        Tools.isInArray(name + descriptor, DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
          warningVec.addElement(LOG_DANGER_CLASS_PRE + getName() + LOG_DANGER_CLASS_MID + name + descriptor);
        }
        else if (Tools.isInArray(name + descriptor, DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY)) {
          warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE + getName() + LOG_DANGER_CLASSLOADER_MID + name + descriptor);
        }
      }
    }
    
    // Copy any warnings to a String[]
    String[] warnings = new String[warningVec.size()];
    for (int i = 0; i < warnings.length; i++) {
      warnings[i] = (String)warningVec.elementAt(i);
    }
    return warnings;
  }
  
  
  
  /** Check for methods which can break the obfuscated code, and log them. */
  private static boolean hasHeader = false;
  
  public static void resetDangerHeader() {
    hasHeader = false;
  }
  
  public void logDangerousMethods() {
    // Get any warnings and print them to the logfile
    String[] warnings = dangerousMethods();
    if (warnings != null && warnings.length > 0) {
      FileLogger logfile = FileLogger.getInstance();
      if (!hasHeader) {
        logfile.addMethodWarning("#");
        logfile.addMethodWarning("# WARNING - Methods are called which may unavoidably break in obfuscated version at runtime.");
        logfile.addMethodWarning("#          Please review your source code to ensure that the dangerous methods are not intended");
        logfile.addMethodWarning("#          to act on classes which are within the obfuscated Jar file.");
        logfile.addMethodWarning("#");
        hasHeader = true;
      }
      for (int i = 0; i < warnings.length; i++) {
        logfile.addMethodWarning("# " + warnings[i]);
      }
    }
  }
  
  
  /** Check for direct references to Utf8 constant pool entries.
   * @param pool the constant pool
   * @throws IllegalStateException if a reference is invalid
   */
  public void markUtf8Refs(ConstantPool pool)
  throws IllegalStateException {
    try {
      // Check for references to Utf8 from outside the constant pool
      for (int i = 0; i < getFieldsCount(); i++) {
        getFieldInfo(i).markUtf8Refs(pool);
      }
      for (int i = 0; i < getMethodsCount(); i++) {
        getMethodInfo(i).markUtf8Refs(pool); // also checks Code/LVT attrs here
      }
      for (int i = 0; i < getAttributesCount(); i++) {
        getAttributeInfo(i).markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
      }
      
      // Now check for references from other CP entries
      for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
        Object obj = iter.next();
        if (obj instanceof NameAndTypeCpInfo ||
        obj instanceof ClassCpInfo ||
        obj instanceof StringCpInfo) {
          ((CpInfo) obj).markUtf8Refs(pool);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalStateException("Inconsistent reference to constant pool.");
    }
  }
  
  
  
  
  /** Check for direct references to NameAndType constant pool entries.
   * @param pool the constant pool
   * @throws IllegalStateException if a reference is invalid
   */
  public void markNTRefs(ConstantPool pool)
  throws IllegalStateException {
    try {
      // Now check the method and field CP entries
      for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
        Object obj = iter.next();
        if (obj instanceof RefCpInfo) {
          ((CpInfo) obj).markNTRefs(pool);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalStateException("Inconsistent reference to constant pool.");
    }
  }
  
  
  
  
  /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
   * are preserved, all others except the list in the String[] are killed).
   * @param extraAttrs array with additional attributes to keep
   */
  public void trimAttrsExcept(String[] extraAttrs) {
    // Merge additional attributes with required list
    String[] keepAttrs = REQUIRED_ATTRS;
    if (extraAttrs != null && extraAttrs.length > 0) {
      String[] tmp = new String[keepAttrs.length + extraAttrs.length];
      System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
      System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length, extraAttrs.length);
      keepAttrs = tmp;
    }
    
    // Traverse all attributes, removing all except those on 'keep' list
    for (int i = 0; i < getFieldsCount(); i++) {
      getFieldInfo(i).trimAttrsExcept(keepAttrs);
    }
    for (int i = 0; i < getMethodsCount(); i++) {
      getMethodInfo(i).trimAttrsExcept(keepAttrs);
    }
    for (int i = 0; i < getAttributesCount(); i++) {
      if (Tools.isInArray(getAttributeInfo(i).getAttrName(), keepAttrs)) {
        getAttributeInfo(i).trimAttrsExcept(keepAttrs);
      } else {
        setAttributeInfo(i, null);
      }
    }
    
    // Delete the marked attributes
    AttrInfo[] left = new AttrInfo[getAttributesCount()];
    int j = 0;
    for (int i = 0; i < getAttributesCount(); i++) {
      if (null != getAttributeInfo(i)) {
        left[j++] = getAttributeInfo(i);
      }
    }
    AttrInfo[] attributes = new AttrInfo[j];
    System.arraycopy(left, 0, attributes, 0, j);
    setAttributesCount(j);
    setAttributeInfo(attributes);
    
    // Update the constant pool reference counts
    constantPool.updateRefCount();
  }
  
  
  
  
  /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
   * are preserved, all others are killed).
   */
  public void trimAttrs() {
    trimAttrsExcept(null);
  }
  
  
  /** Remap the entities in the current class file.
   * @param nm the name mapper for the class file
   */
  public void remap(NameMapper nm) {
    // Remove unnecessary attributes from the class
    String[] attrs = nm.getAttrsToKeep();
    if (attrs.length > 0) {
      trimAttrsExcept(attrs);
    } else {
      trimAttrs();
    }
    
    // Remap all the package/interface/class/method/field names
    //
    String thisClassName = ((Utf8CpInfo)getCpEntry(((ClassCpInfo)getCpEntry(getClassIndex())).getClassNameIndex())).getString();
    
    // Remap the 'inner name' reference of the 'InnerClasses' attribute
    for (int i = 0; i < getAttributesCount(); i++) {
      AttrInfo attrInfo = getAttributeInfo(i);
      if (attrInfo instanceof InnerClassesAttrInfo) {
        // For each inner class referemce,
        InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo).getInnerClasses();
        for (int j = 0; j < info.length; j++) {
          // Get the 'inner name' (it is a CONSTANT_Utf8)
          CpInfo cpInfo = getCpEntry(info[j].getInnerNameIndex());
          if (cpInfo instanceof Utf8CpInfo) {
            // Get the remapped class name
            Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
            String origClass = utf.getString();
            
            // Only remap non-anonymous classes (anon are "")
            if (!origClass.equals("")) {
              // Get the full inner class name
              ClassCpInfo innerClassInfo = (ClassCpInfo) getCpEntry(info[j].getInnerClassInfoIndex());
              String innerClassName = ((Utf8CpInfo) getCpEntry(innerClassInfo.getClassNameIndex())).getString();
              
              // It is the remapped simple name that must be stored, so truncate it
              String remapClass = nm.mapClass(innerClassName);
              remapClass = remapClass.substring(remapClass.lastIndexOf('$') + 1);
              int remapIndex = constantPool.remapUtf8To(remapClass, info[j].getInnerNameIndex());
              info[j].setInnerNameIndex(remapIndex);
            }
          }
        }
      }
    }
    
    // Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
    // attribute, in the 'Code' attribute of method structures.
    for (int i = 0; i < getMethodsCount(); i++) {
      for (int j = 0; j < getMethodInfo(i).getAttributesLength(); j++) {
        AttrInfo attrInfo = getMethodInfo(i).getAttribute(j);
        if (attrInfo instanceof CodeAttrInfo) {
          CodeAttrInfo codeAttrInfo = (CodeAttrInfo)attrInfo;
          for (int k = 0; k < codeAttrInfo.getAttributesLength(); k++) {
            AttrInfo innerAttrInfo = codeAttrInfo.getAttributes()[k];
            if (innerAttrInfo instanceof LocalVariableTableAttrInfo) {
              LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo)innerAttrInfo;
              LocalVariableInfo[] lvts = lvtAttrInfo.getLocalVariableTable();
              for (int m = 0; m < lvts.length; m++) {
                // Remap name
                Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(lvts[m].getNameIndex());
                String remapName = nm.mapField(thisClassName, nameUtf.getString());
                // if no mapping is found use the original name
                if (null == remapName) {
                  remapName = nameUtf.getString();
                }
                lvts[m].setNameIndex(constantPool.remapUtf8To(remapName, lvts[m].getNameIndex()));
                
                // Remap descriptor
                Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(lvts[m].getDescriptorIndex());
                String remapDesc = nm.mapDescriptor(descUtf.getString());
                lvts[m].setDescriptorIndex(constantPool.remapUtf8To(remapDesc, lvts[m].getDescriptorIndex()));
              }
            }
          }
        }
      }
    }
    
    // Go through all of class's fields and methods mapping 'name' and 'descriptor' references
    for (int i = 0; i < getFieldsCount(); i++) {
      // Remap field 'name', unless it is 'Synthetic'
      FieldInfo field = getFieldInfo(i);
      if (!field.isSynthetic()) {
        Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(field.getNameIndex());
        String remapName = nm.mapField(thisClassName, nameUtf.getString());
        // if no mapping is found use the original name
        if (null == remapName) {
          remapName = nameUtf.getString();
        }
        field.setNameIndex(constantPool.remapUtf8To(remapName, field.getNameIndex()));
      }
      
      // Remap field 'descriptor'
      Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(field.getDescriptorIndex());
      String remapDesc = nm.mapDescriptor(descUtf.getString());
      field.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, field.getDescriptorIndex()));
    }
    for (int i = 0; i < getMethodsCount(); i++) {
      // Remap method 'name', unless it is 'Synthetic'
      MethodInfo method = getMethodInfo(i);
      Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(method.getDescriptorIndex());
      if (!method.isSynthetic()) {
        Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(method.getNameIndex());
        String remapName = nm.mapMethod(thisClassName, nameUtf.getString(), descUtf.getString());
        // if no mapping is found use the original name
        if (null == remapName) {
          remapName = nameUtf.getString();
        }
        method.setNameIndex(constantPool.remapUtf8To(remapName, method.getNameIndex()));
      }
      
      // Remap method 'descriptor'
      String remapDesc = nm.mapDescriptor(descUtf.getString());
      method.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, method.getDescriptorIndex()));
    }
    
    // Remap all field/method names and descriptors in the constant pool (depends on class names)
    int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
    for (int i = 0; i < currentCpLength; i++) {
      CpInfo cpInfo = getCpEntry(i);
      if (null != cpInfo) {
        // If this is a CONSTANT_Fieldref, CONSTANT_Methodref or CONSTANT_InterfaceMethodref
        // get the CONSTANT_NameAndType and remap the name and the components of the
        // descriptor string.
        if (cpInfo instanceof RefCpInfo) {
          // Get the unmodified class name
          ClassCpInfo classInfo = (ClassCpInfo)getCpEntry(((RefCpInfo)cpInfo).getClassIndex());
          Utf8CpInfo classUtf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
          String className = classUtf.getString();
          
          // Get the current N&T reference and its 'name' and 'descriptor' utf's
          int ntIndex = ((RefCpInfo)cpInfo).getNameAndTypeIndex();
          NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo)getCpEntry(ntIndex);
          Utf8CpInfo refUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getNameIndex());
          Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getDescriptorIndex());
          
          // Get the remapped versions of 'name' and 'descriptor'
          String remapRef;
          if (cpInfo instanceof FieldrefCpInfo) {
            remapRef = nm.mapField(className, refUtf.getString());
            // if no mapping is found use the original name
            if (null == remapRef) {
              remapRef = refUtf.getString();
            }
            
            // cloder - check if this is a compiler generated field
            // supporting the JDK1.2-or-later '.class' construct
            if (refUtf.getString().startsWith("class$")) {
              String realClassName = refUtf.getString().substring(6).replace('$', '.');
              // check whether the hardcoded class name is not a class from the
              // Java SDK, i.e. doesn't start with "java." or "javax."
              if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
              && !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
                // check whether the hardcoded class name exists in the main tree
                // whether it is be obfuscated
                Cl cl = getClassTree().findClass(realClassName);
                if (null != cl) {
                  if (cl.isBeingObfuscated()) {
                    FileLogger.getInstance().addWarning("# WARNING: " + realClassName +
                    " shouldn't be obfuscated: it is referenced as "
                    + realClassName + ".class from " + thisClassName);
                  }
                }
              }
            }
            // end cloder
          } else {
            // instance of method mapping
            remapRef = nm.mapMethod(className, refUtf.getString(), descUtf.getString());
            // if no mapping is found use the original name
            if (null == remapRef) {
              remapRef = refUtf.getString();
            }
          }
          String remapDesc = nm.mapDescriptor(descUtf.getString());
          
          // If a remap is required, make a new N&T (increment ref count on 'name' and
          // 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
          // remap new N&T's utf's
          if (!remapRef.equals(refUtf.getString()) ||
          !remapDesc.equals(descUtf.getString())) {
            // Get the new N&T guy
            NameAndTypeCpInfo newNameTypeInfo;
            if (nameTypeInfo.getRefCount() == 1) {
              newNameTypeInfo = nameTypeInfo;
            }
            else {
              // Create the new N&T info
              newNameTypeInfo = (NameAndTypeCpInfo)nameTypeInfo.clone();
              
              // Adjust its reference counts of its utf's
              ((CpInfo)getCpEntry(newNameTypeInfo.getNameIndex())).incRefCount();
              ((CpInfo)getCpEntry(newNameTypeInfo.getDescriptorIndex())).incRefCount();
              
              // Append it to the Constant Pool, and
              // point the RefCpInfo entry to the new N&T data
              ((RefCpInfo)cpInfo).setNameAndTypeIndex(
              constantPool.addEntry(newNameTypeInfo));
              
              // Adjust reference counts from RefCpInfo
              newNameTypeInfo.incRefCount();
              nameTypeInfo.decRefCount();
            }
            
            // Remap the 'name' and 'descriptor' utf's in N&T
            newNameTypeInfo.setNameIndex(constantPool.remapUtf8To(remapRef, newNameTypeInfo.getNameIndex()));
            newNameTypeInfo.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, newNameTypeInfo.getDescriptorIndex()));
          }
        }
      }
    }
    
    // Finally, remap all class references to Utf
    for (int i = 0; i < constantPool.length(); i++) {
      CpInfo cpInfo = getCpEntry(i);
      if (cpInfo != null) {
        // If this is CONSTANT_Class, remap the class-name Utf8 entry
        if (cpInfo instanceof ClassCpInfo) {
          ClassCpInfo classInfo = (ClassCpInfo)cpInfo;
          Utf8CpInfo utf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
          String remapClass = nm.mapClass(utf.getString());
          int remapIndex = constantPool.remapUtf8To(remapClass, classInfo.getClassNameIndex());
          classInfo.setClassNameIndex(remapIndex);
        }
      }
    }
  }
  
  
  
  
  /** Export the representation of the class file to a DataOutput stream.
   * @param dout the output stream
   * @throws IOException if an I/O error occurs
   */
  public void write(DataOutput dout)
  throws IOException {
    if (dout == null) throw new IOException("No output stream was provided.");
    dout.writeInt(getMagic());
    dout.writeShort(getMinorVersion());
    dout.writeShort(getMajorVersion());
    dout.writeShort(constantPool.length());
    for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
      CpInfo cpInfo = (CpInfo) iter.next();
      if (cpInfo != null) {
        cpInfo.write(dout);
      }
    }
    dout.writeShort(getAccessFlags());
    dout.writeShort(getClassIndex());
    dout.writeShort(getSuperClassIndex());
    dout.writeShort(getInterfacesCount());
    for (int i = 0; i < getInterfacesCount(); i++) {
      dout.writeShort(getInterface(i));
    }
    dout.writeShort(getFieldsCount());
    for (int i = 0; i < getFieldsCount(); i++) {
      getFieldInfo(i).write(dout);
    }
    dout.writeShort(getMethodsCount());
    for (int i = 0; i < getMethodsCount(); i++) {
      getMethodInfo(i).write(dout);
    }
    dout.writeShort(getAttributesCount());
    for (int i = 0; i < getAttributesCount(); i++) {
      getAttributeInfo(i).write(dout);
    }
  }
  
  
  
  
  /** Dump the content of the class file to the specified file (used for
   * debugging).
   * @param pw the print writer
   */
  public void dump(PrintWriter pw) {
    pw.println("_____________________________________________________________________");
    pw.println("CLASS: " + getName());
    pw.println("Magic: " + Integer.toHexString(getMagic()) + " (" + getMagic() + ")");
    pw.println("Minor version: " + Integer.toHexString(getMinorVersion()) + " (" + getMinorVersion() + ")");
    pw.println("Major version: " + Integer.toHexString(getMajorVersion()) + " (" + getMajorVersion() + ")");
    pw.println();
    pw.println("Access: " + getAccessFlags());
    pw.println("This class: " + getClassIndex() +  " / " + getName());
    pw.println("Superclass: " + getSuperClassIndex() + " / " + getSuper());
    pw.println();
    pw.println("CP length: " + constantPool.length());
    for (int i = 0; i < constantPool.length(); i++) {
      CpInfo cpInfo = (CpInfo) constantPool.getCpEntry(i);
      if (cpInfo != null) {
        cpInfo.dump(pw, this, i);
      }
    }
    pw.println("Attributes: " + getAttributesCount());
    for (int i = 0; i < getAttributesCount(); i++) {
      pw.println();
      pw.print("attribute[" + i + "]: ");
      getAttributeInfo(i).dump(pw, this);
    }
    pw.println();
    pw.println("Direct implemented interfaces: " + getInterfacesCount());
    for (int i = 0; i < getInterfacesCount(); i++) {
      CpInfo info = getCpEntry(getInterface(i));
      pw.print("  interface[" + i + "]: ");
      if (null == info) {
        pw.println("(null)");
      } else {
        pw.println( ((Utf8CpInfo) getCpEntry( ((ClassCpInfo) info).getClassNameIndex())).getString() );
      }
    }
    pw.println();
    pw.println("Declared fields: " + getFieldsCount());
    for (int i = 0; i < getFieldsCount(); i++) {
      ClassItemInfo info = getFieldInfo(i);
      pw.print("  field[" + i + "]: ");
      if (null == info) {
        pw.println("(null)");
      } else {
        pw.println("    name:       " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
        pw.println("    descriptor: " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
      }
      pw.println("    attributes: " + info.getAttributesLength());
      for (int j = 0; j < info.getAttributesLength(); j++) {
        pw.print("    attribute[" + j + "]: ");
        info.getAttribute(j).dump(pw, this);
        pw.println();
      }
    }
    pw.println();
    pw.println("Declared methods: " + getMethodsCount());
    for (int i = 0; i < getMethodsCount(); i++) {
      ClassItemInfo info = getMethodInfo(i);
      pw.print("  method[" + i + "]: ");
      if (info == null) {
        pw.println("(null)");
      } else {
        pw.println();
        pw.println("    name:         " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
        pw.println("    descriptor:   " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
        pw.println("    access flags: " + info.getAccessFlags());
      }
      pw.println("    attributes:   " + info.getAttributesLength());
      for (int j = 0; j < info.getAttributesLength(); j++) {
        pw.print("    attribute[" + j + "]: ");
        info.getAttribute(j).dump(pw, this);
        pw.println();
      }
    }
  }
  
  
  
  /** Retrieve a list of classes that are used in hardcoded references.
   * @return a vector containing strings with class names
   * @see #remap
   */
  public Set getHardcodedClassNames() {
    HashSet result = new HashSet();
    
    int currentCpLength = constantPool.length();
    for (int i = 0; i < currentCpLength; i++) {
      CpInfo cpInfo = getCpEntry(i);
      if (cpInfo != null && cpInfo instanceof RefCpInfo) {
        // Get the current name and type reference and its 'name' utf's
        int ntIndex = ((RefCpInfo) cpInfo).getNameAndTypeIndex();
        NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
        Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo.getNameIndex());
        
        if (cpInfo instanceof FieldrefCpInfo) {
          // cloder - check if this is a compiler generated field
          // supporting the JDK1.2-or-later '.class' construct
          if (refUtf.getString().startsWith("class$")) {
            String realClassName = refUtf.getString().substring(6).replace('$', '.');
            if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
            && !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
              result.add(realClassName);
            }
          }
          // end cloder
        }
      }
    }
    return result;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.