0001: /**
0002: * JavaGuard -- an obfuscation package for Java classfiles.
0003: *
0004: * Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
0005: * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0020: *
0021: * The author may be contacted at theit@gmx.de.
0022: *
0023: *
0024: * $Id: ClassFile.java,v 1.9 2002/06/04 09:56:47 glurk Exp $
0025: */package net.sf.javaguard.classfile;
0026:
0027: import java.io.*;
0028: import java.util.*;
0029: import net.sf.javaguard.Cl;
0030: import net.sf.javaguard.KeyValue;
0031: import net.sf.javaguard.ClassTree;
0032: import net.sf.javaguard.log.FileLogger;
0033: import net.sf.javaguard.Tools;
0034:
0035: /** This is a representation of the data in a Java class file (*.class).
0036: * A ClassFile instance representing a *.class file can be generated
0037: * using the static create(DataInput) method, manipulated using various
0038: * operators, and persisted back using the write(DataOutput) method.
0039: *
0040: * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
0041: * @author <a href="mailto:markw@retrologic.com">Mark Welsh</a>
0042: */
0043: public class ClassFile implements ClassConstants {
0044: // Constants -------------------------------------------------------------
0045: private static final String[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
0046: "forName(Ljava/lang/String;)Ljava/lang/Class;",
0047: "getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
0048: "getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
0049: "getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
0050: "getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;" };
0051: private static final String LOG_DANGER_CLASS_PRE = " Your class ";
0052: private static final String LOG_DANGER_CLASS_MID = " calls the java/lang/Class method ";
0053: private static final String[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
0054: "defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
0055: "findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
0056: "findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
0057: "loadClass(Ljava/lang/String;)Ljava/lang/Class;",
0058: "loadClass(Ljava/lang/String;Z)Ljava/lang/Class;" };
0059: private static final String LOG_DANGER_CLASSLOADER_PRE = " Your class ";
0060: private static final String LOG_DANGER_CLASSLOADER_MID = " calls the java/lang/ClassLoader method ";
0061:
0062: /** Holds the class tree the class file is assigned to. */
0063: private ClassTree classTree;
0064: /** Holds the magic number stored in the class file (must be 0xcafebabe). */
0065: private int magic;
0066: /** Holds the minor version number. */
0067: private int minorVersion;
0068: /** Holds the major version number. */
0069: private int majorVersion;
0070: /** Holds the constant pool of the class file. */
0071: private ConstantPool constantPool;
0072: /** Holds the access flags for the class. */
0073: private int accessFlags;
0074: /** Index into the constant pool. The referenced element holds an index that
0075: * references the class name. */
0076: private int this ClassIndex;
0077: /** Index into the constant pool. The referenced element holds an index that
0078: * references the name of the super class. */
0079: private int super ClassIndex;
0080:
0081: /** Holds the number of direct implemented interfaces. */
0082: private int interfacesCount;
0083: /** Index array of interface names in the constant pool. */
0084: private int interfaces[];
0085: /** Holds the number of direct declared fields. */
0086: private int fieldsCount;
0087: /** Holds information about the declared fields. */
0088: private FieldInfo fieldInfo[];
0089: /** Holds the number of declared methods. */
0090: private int methodsCount;
0091: /** Holds information about the declared methods. */
0092: private MethodInfo methodInfo[];
0093: /** Holds the number of attributes for the class file. */
0094: private int attributesCount;
0095: /** Holds information about the attributes for the class file. */
0096: private AttrInfo attributesInfo[];
0097:
0098: /** Create a new ClassFile from the class file format data in the DataInput
0099: * stream.
0100: * @param classTree the class tree the class file is assigned to
0101: * @param din the input stream
0102: * @return the class file created from the input stream
0103: * @throws IOException if class file is corrupt or incomplete
0104: */
0105: public static ClassFile create(ClassTree classTree, DataInput din)
0106: throws IOException {
0107: if (null == din)
0108: throw new IOException("No input stream was provided.");
0109: ClassFile cf = new ClassFile(classTree);
0110: cf.read(din);
0111: return cf;
0112: }
0113:
0114: /** Private constructor.
0115: * @param classTree the class tree the class file is assigned to
0116: */
0117: private ClassFile(ClassTree classTree) {
0118: this .classTree = classTree;
0119: }
0120:
0121: /** Returns the class tree the class file is assigned to.
0122: * @return the class tree
0123: */
0124: private ClassTree getClassTree() {
0125: return classTree;
0126: }
0127:
0128: /** Import the class data to internal representation.
0129: * @param din the input stream
0130: * @throws IOException if an I/O error occurs
0131: */
0132: private void read(DataInput din) throws IOException {
0133: // Read the class file
0134: setMagic(din.readInt());
0135: setMinorVersion(din.readUnsignedShort());
0136: setMajorVersion(din.readUnsignedShort());
0137:
0138: // Check this is a valid classfile that we can handle
0139: if (getMagic() != MAGIC) {
0140: throw new IOException("Invalid magic number in class file.");
0141: }
0142: if (getMajorVersion() > MAJOR_VERSION
0143: || (MAJOR_VERSION == getMajorVersion() && getMinorVersion() > MINOR_VERSION_MAX)) {
0144: throw new IOException(
0145: "Incompatible version number for class file format: "
0146: + getMajorVersion() + " / "
0147: + getMinorVersion());
0148: }
0149:
0150: int u2constantPoolCount = din.readUnsignedShort();
0151: CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
0152: // Fill the constant pool, recalling the zero entry
0153: // is not persisted, nor are the entries following a Long or Double
0154: for (int i = 1; i < u2constantPoolCount; i++) {
0155: cpInfo[i] = CpInfo.create(din);
0156: if ((cpInfo[i] instanceof LongCpInfo)
0157: || (cpInfo[i] instanceof DoubleCpInfo)) {
0158: i++;
0159: }
0160: }
0161: constantPool = new ConstantPool(this , cpInfo);
0162:
0163: setAccessFlags(din.readUnsignedShort());
0164: setClassIndex(din.readUnsignedShort());
0165: setSuperClassIndex(din.readUnsignedShort());
0166: // read the information about the implemented interfaces
0167: setInterfacesCount(din.readUnsignedShort());
0168: int[] u2interfaces = new int[getInterfacesCount()];
0169: for (int i = 0; i < getInterfacesCount(); i++) {
0170: u2interfaces[i] = din.readUnsignedShort();
0171: }
0172: setInterfaces(u2interfaces);
0173: // read the direct declared fields
0174: setFieldsCount(din.readUnsignedShort());
0175: FieldInfo[] fields = new FieldInfo[getFieldsCount()];
0176: for (int i = 0; i < getFieldsCount(); i++) {
0177: fields[i] = FieldInfo.create(din, this );
0178: }
0179: setFieldInfo(fields);
0180: // read the direct declared methods
0181: setMethodsCount(din.readUnsignedShort());
0182: MethodInfo[] methods = new MethodInfo[getMethodsCount()];
0183: for (int i = 0; i < getMethodsCount(); i++) {
0184: methods[i] = MethodInfo.create(din, this );
0185: }
0186: setMethodInfo(methods);
0187: // read the attributes for the class
0188: setAttributesCount(din.readUnsignedShort());
0189: AttrInfo[] attrs = new AttrInfo[getAttributesCount()];
0190: for (int i = 0; i < getAttributesCount(); i++) {
0191: attrs[i] = AttrInfo.create(din, this );
0192: }
0193: setAttributeInfo(attrs);
0194: }
0195:
0196: /** Sets the magic number of the class file (normally 0xcafebabe).
0197: * @param num the magic number
0198: * @see #getMagic
0199: */
0200: private void setMagic(int num) {
0201: this .magic = num;
0202: }
0203:
0204: /** Returns the magic number of the class file (normally 0xcafebabe).
0205: * @return magic number
0206: * @see #setMagic
0207: */
0208: private int getMagic() {
0209: return magic;
0210: }
0211:
0212: /** Sets the major version of the class file.
0213: * @param ver major class file version
0214: * @see #getMajorVersion
0215: */
0216: private void setMajorVersion(int ver) {
0217: majorVersion = ver;
0218: }
0219:
0220: /** Returns the major version of the class file.
0221: * @return major class file version
0222: * @see #setMajorVersion
0223: */
0224: private int getMajorVersion() {
0225: return majorVersion;
0226: }
0227:
0228: /** Sets the minor version of the class file.
0229: * @param ver minor class file version
0230: * @see #setMinorVersion
0231: */
0232: private void setMinorVersion(int ver) {
0233: minorVersion = ver;
0234: }
0235:
0236: /** Returns the minor version of the class file.
0237: * @return minor class file version
0238: * @see #setMinorVersion
0239: */
0240: private int getMinorVersion() {
0241: return minorVersion;
0242: }
0243:
0244: /** Sets the access flags for the class.
0245: * @param flags the access flags for the class
0246: * @see #getAccessFlags
0247: */
0248: private void setAccessFlags(int flags) {
0249: accessFlags = flags;
0250: }
0251:
0252: /** Returns the access flags for the class.
0253: * @return access flags for the class
0254: * @see #setAccessFlags
0255: */
0256: private int getAccessFlags() {
0257: return accessFlags;
0258: }
0259:
0260: /** Sets the index for the element in the constant pool that knows the class
0261: * name.
0262: * @param index index into the constant pool
0263: * @see #getClassIndex
0264: */
0265: private void setClassIndex(int index) {
0266: this ClassIndex = index;
0267: }
0268:
0269: /** Returns the index for the element in the constant pool that knows the
0270: * class name.
0271: * @return index into the constant pool
0272: * @see #setClassIndex
0273: */
0274: private int getClassIndex() {
0275: return this ClassIndex;
0276: }
0277:
0278: /** Sets the index for the element in the constant pool that knows the name
0279: * of the super class.
0280: * @param index index into the constant pool
0281: * @see #getSuperClassIndex
0282: */
0283: private void setSuperClassIndex(int index) {
0284: super ClassIndex = index;
0285: }
0286:
0287: /** Returns the index for the element in the constant pool that knows the
0288: * name of the super class.
0289: * @return index into the constant pool
0290: */
0291: private int getSuperClassIndex() {
0292: return super ClassIndex;
0293: }
0294:
0295: /** Sets the number of direct implemented interfaces.
0296: * @param num the number of direct implemented interfaces
0297: * @see #getInterfacesCount
0298: */
0299: private void setInterfacesCount(int num) {
0300: interfacesCount = num;
0301: }
0302:
0303: /** Returns the number of direct implemented interfaces.
0304: * @return number of direct implemented interfaces
0305: * @see #setInterfacesCount
0306: */
0307: private int getInterfacesCount() {
0308: return interfacesCount;
0309: }
0310:
0311: /** Sets the index array of interface names in the constant pool.
0312: * @param interfaces index array of interface names in the constant pool
0313: * @see #getInterfaces
0314: */
0315: private void setInterfaces(int[] interfaces) {
0316: this .interfaces = interfaces;
0317: }
0318:
0319: /** Returns the index array of interface names in the constant pool.
0320: * @return index array of interface names in the constant pool
0321: * @see #setInterfaces
0322: * @see #getInterface
0323: */
0324: private int[] getInterfaces() {
0325: return interfaces;
0326: }
0327:
0328: /** Return the index of the specified interface from the index array.
0329: * @param pos the position of the interface in the interface name index array
0330: * @return index into the constant pool
0331: * @see #getInterfaces
0332: */
0333: private int getInterface(int pos) {
0334: return interfaces[pos];
0335: }
0336:
0337: /** Returns an array with the names of the direct implemented interfaces.
0338: * @return array with interface names
0339: */
0340: public String[] getInterfaceNames() {
0341: String[] names = new String[getInterfacesCount()];
0342: for (int i = 0; i < getInterfacesCount(); i++) {
0343: names[i] = toName(getInterface(i));
0344: }
0345: return names;
0346: }
0347:
0348: /** Sets the number of declared fields.
0349: * @param num number of declared fields
0350: * @see #getFieldsCount
0351: */
0352: private void setFieldsCount(int num) {
0353: fieldsCount = num;
0354: }
0355:
0356: /** Returns the number of declared fields.
0357: * @return number of declared fields
0358: */
0359: public int getFieldsCount() {
0360: return fieldsCount;
0361: }
0362:
0363: /** Sets the information about the declared fields.
0364: * @param fields array with information about the declared fields
0365: * @see #getFieldInfo
0366: */
0367: private void setFieldInfo(FieldInfo[] fields) {
0368: this .fieldInfo = fields;
0369: }
0370:
0371: /** Returns an array with information about the declared fields.
0372: * @return array with information about the declared fields
0373: * @see #setFieldInfo
0374: */
0375: private FieldInfo[] getFieldInfo() {
0376: return fieldInfo;
0377: }
0378:
0379: /** Return the information about a declared field.
0380: * @param pos the position in the array of declared fields
0381: * @return information about the declared fieldd
0382: * @see #getFieldInfo
0383: */
0384: public FieldInfo getFieldInfo(int pos) {
0385: return fieldInfo[pos];
0386: }
0387:
0388: /** Sets the number of declared methods.
0389: * @param num the number of declared methods
0390: * @see #getMethodsCount
0391: */
0392: private void setMethodsCount(int num) {
0393: methodsCount = num;
0394: }
0395:
0396: /** Returns the number of declared methods.
0397: * @return the number of declared methods
0398: */
0399: public int getMethodsCount() {
0400: return methodsCount;
0401: }
0402:
0403: /** Sets the information about the declared methods.
0404: * @param methods array with information about the declared methods
0405: * @see #getMethodInfo
0406: */
0407: private void setMethodInfo(MethodInfo[] methods) {
0408: this .methodInfo = methods;
0409: }
0410:
0411: /** Returns an array with information about the declared methods.
0412: * @return array with information about the declared methods
0413: * @see #setMethodInfo
0414: */
0415: private MethodInfo[] getMethodInfo() {
0416: return methodInfo;
0417: }
0418:
0419: /** Return the information about a declared method.
0420: * @param pos the position in the array of declared methods
0421: * @return information about the declared method
0422: * @see #getMethodInfo
0423: */
0424: public MethodInfo getMethodInfo(int pos) {
0425: return methodInfo[pos];
0426: }
0427:
0428: /** Sets the number of attributes for the class file.
0429: * @param num the number of attributes for the class file
0430: * @see #getAttributesCount
0431: */
0432: private void setAttributesCount(int num) {
0433: attributesCount = num;
0434: }
0435:
0436: /** Returns the number of attributes for the class file.
0437: * @return the number of attributes for the class file
0438: * @see #setAttributesCount
0439: */
0440: private int getAttributesCount() {
0441: return attributesCount;
0442: }
0443:
0444: /** Sets the information about the attributes for the class file.
0445: * @param attrs array with information about the attributes for the class file
0446: * @see #getAttributeInfo
0447: */
0448: private void setAttributeInfo(AttrInfo[] attrs) {
0449: this .attributesInfo = attrs;
0450: }
0451:
0452: /** Returns an array with information about the attributes for the class file.
0453: * @return array with information about the attributes for the class file
0454: * @see #setAttributeInfo
0455: */
0456: private AttrInfo[] getAttributeInfo() {
0457: return attributesInfo;
0458: }
0459:
0460: /** Return the information about an attribute for the class file.
0461: * @param pos the position in the array of attributes
0462: * @return information about the attribute
0463: * @see #getAttributeInfo
0464: * @see #setAttributeInfo(int, AttrInfo)
0465: */
0466: private AttrInfo getAttributeInfo(int pos) {
0467: return attributesInfo[pos];
0468: }
0469:
0470: /** Sets the information about a certain attribute.
0471: * @param pos the position in the array of attributes
0472: * @param attrInfo the new attribute information
0473: * @see #getAttributeInfo(int)
0474: * @see #setAttributeInfo
0475: */
0476: private void setAttributeInfo(int pos, AttrInfo attrInfo) {
0477: attributesInfo[pos] = attrInfo;
0478: }
0479:
0480: /** Return the name of the class file.
0481: * @return the name of the class file
0482: */
0483: public String getName() {
0484: return toName(getClassIndex());
0485: }
0486:
0487: /** Return the name of this class's super class.
0488: * @return the name of the super class
0489: */
0490: public String getSuper() {
0491: // This may be java/lang/Object, in which case there is no super
0492: return (0 == getSuperClassIndex()) ? null
0493: : toName(getSuperClassIndex());
0494: }
0495:
0496: /** Convert a constant pool index to a class name.
0497: * @param index the index into the constant pool
0498: * @return the name of the specified class
0499: * @throws IllegalStateException if an error occurs
0500: */
0501: protected String toName(int index) throws IllegalStateException {
0502: CpInfo classEntry = getCpEntry(index);
0503: if (classEntry instanceof ClassCpInfo) {
0504: CpInfo nameEntry = getCpEntry(((ClassCpInfo) classEntry)
0505: .getClassNameIndex());
0506: if (nameEntry instanceof Utf8CpInfo) {
0507: return ((Utf8CpInfo) nameEntry).getString();
0508: }
0509: }
0510: throw new IllegalStateException(
0511: "Inconsistent Constant Pool in class file.");
0512: }
0513:
0514: /** Lookup the entry in the constant pool and return the object.
0515: * @param index the index into the constant pool
0516: * @return the entry at the specified index in the constant pool
0517: */
0518: protected CpInfo getCpEntry(int index) {
0519: return constantPool.getCpEntry(index);
0520: }
0521:
0522: /** Check for methods which can break the obfuscated code, and log them to
0523: * a string array.
0524: * @return string array with methods which can break the obfuscated code
0525: */
0526: public String[] dangerousMethods() {
0527: Vector warningVec = new Vector();
0528:
0529: // Need only check CONSTANT_Methodref entries of constant pool since
0530: // dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
0531: for (Iterator iter = constantPool.iterator(); iter.hasNext();) {
0532: Object obj = iter.next();
0533: if (obj instanceof MethodrefCpInfo) {
0534: // Get the method class name, simple name and descriptor
0535: MethodrefCpInfo entry = (MethodrefCpInfo) obj;
0536: ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry
0537: .getClassIndex());
0538: String className = ((Utf8CpInfo) getCpEntry(classEntry
0539: .getClassNameIndex())).getString();
0540: NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry
0541: .getNameAndTypeIndex());
0542: String name = ((Utf8CpInfo) getCpEntry(ntEntry
0543: .getNameIndex())).getString();
0544: String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry
0545: .getDescriptorIndex())).getString();
0546:
0547: // Check if this is on the proscribed list
0548: if (className.equals("java/lang/Class")
0549: && Tools
0550: .isInArray(name + descriptor,
0551: DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
0552: warningVec.addElement(LOG_DANGER_CLASS_PRE
0553: + getName() + LOG_DANGER_CLASS_MID + name
0554: + descriptor);
0555: } else if (Tools
0556: .isInArray(name + descriptor,
0557: DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY)) {
0558: warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE
0559: + getName() + LOG_DANGER_CLASSLOADER_MID
0560: + name + descriptor);
0561: }
0562: }
0563: }
0564:
0565: // Copy any warnings to a String[]
0566: String[] warnings = new String[warningVec.size()];
0567: for (int i = 0; i < warnings.length; i++) {
0568: warnings[i] = (String) warningVec.elementAt(i);
0569: }
0570: return warnings;
0571: }
0572:
0573: /** Check for methods which can break the obfuscated code, and log them. */
0574: private static boolean hasHeader = false;
0575:
0576: public static void resetDangerHeader() {
0577: hasHeader = false;
0578: }
0579:
0580: public void logDangerousMethods() {
0581: // Get any warnings and print them to the logfile
0582: String[] warnings = dangerousMethods();
0583: if (warnings != null && warnings.length > 0) {
0584: FileLogger logfile = FileLogger.getInstance();
0585: if (!hasHeader) {
0586: logfile.addMethodWarning("#");
0587: logfile
0588: .addMethodWarning("# WARNING - Methods are called which may unavoidably break in obfuscated version at runtime.");
0589: logfile
0590: .addMethodWarning("# Please review your source code to ensure that the dangerous methods are not intended");
0591: logfile
0592: .addMethodWarning("# to act on classes which are within the obfuscated Jar file.");
0593: logfile.addMethodWarning("#");
0594: hasHeader = true;
0595: }
0596: for (int i = 0; i < warnings.length; i++) {
0597: logfile.addMethodWarning("# " + warnings[i]);
0598: }
0599: }
0600: }
0601:
0602: /** Check for direct references to Utf8 constant pool entries.
0603: * @param pool the constant pool
0604: * @throws IllegalStateException if a reference is invalid
0605: */
0606: public void markUtf8Refs(ConstantPool pool)
0607: throws IllegalStateException {
0608: try {
0609: // Check for references to Utf8 from outside the constant pool
0610: for (int i = 0; i < getFieldsCount(); i++) {
0611: getFieldInfo(i).markUtf8Refs(pool);
0612: }
0613: for (int i = 0; i < getMethodsCount(); i++) {
0614: getMethodInfo(i).markUtf8Refs(pool); // also checks Code/LVT attrs here
0615: }
0616: for (int i = 0; i < getAttributesCount(); i++) {
0617: getAttributeInfo(i).markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
0618: }
0619:
0620: // Now check for references from other CP entries
0621: for (Iterator iter = pool.iterator(); iter.hasNext();) {
0622: Object obj = iter.next();
0623: if (obj instanceof NameAndTypeCpInfo
0624: || obj instanceof ClassCpInfo
0625: || obj instanceof StringCpInfo) {
0626: ((CpInfo) obj).markUtf8Refs(pool);
0627: }
0628: }
0629: } catch (ArrayIndexOutOfBoundsException e) {
0630: throw new IllegalStateException(
0631: "Inconsistent reference to constant pool.");
0632: }
0633: }
0634:
0635: /** Check for direct references to NameAndType constant pool entries.
0636: * @param pool the constant pool
0637: * @throws IllegalStateException if a reference is invalid
0638: */
0639: public void markNTRefs(ConstantPool pool)
0640: throws IllegalStateException {
0641: try {
0642: // Now check the method and field CP entries
0643: for (Iterator iter = pool.iterator(); iter.hasNext();) {
0644: Object obj = iter.next();
0645: if (obj instanceof RefCpInfo) {
0646: ((CpInfo) obj).markNTRefs(pool);
0647: }
0648: }
0649: } catch (ArrayIndexOutOfBoundsException e) {
0650: throw new IllegalStateException(
0651: "Inconsistent reference to constant pool.");
0652: }
0653: }
0654:
0655: /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
0656: * are preserved, all others except the list in the String[] are killed).
0657: * @param extraAttrs array with additional attributes to keep
0658: */
0659: public void trimAttrsExcept(String[] extraAttrs) {
0660: // Merge additional attributes with required list
0661: String[] keepAttrs = REQUIRED_ATTRS;
0662: if (extraAttrs != null && extraAttrs.length > 0) {
0663: String[] tmp = new String[keepAttrs.length
0664: + extraAttrs.length];
0665: System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
0666: System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length,
0667: extraAttrs.length);
0668: keepAttrs = tmp;
0669: }
0670:
0671: // Traverse all attributes, removing all except those on 'keep' list
0672: for (int i = 0; i < getFieldsCount(); i++) {
0673: getFieldInfo(i).trimAttrsExcept(keepAttrs);
0674: }
0675: for (int i = 0; i < getMethodsCount(); i++) {
0676: getMethodInfo(i).trimAttrsExcept(keepAttrs);
0677: }
0678: for (int i = 0; i < getAttributesCount(); i++) {
0679: if (Tools.isInArray(getAttributeInfo(i).getAttrName(),
0680: keepAttrs)) {
0681: getAttributeInfo(i).trimAttrsExcept(keepAttrs);
0682: } else {
0683: setAttributeInfo(i, null);
0684: }
0685: }
0686:
0687: // Delete the marked attributes
0688: AttrInfo[] left = new AttrInfo[getAttributesCount()];
0689: int j = 0;
0690: for (int i = 0; i < getAttributesCount(); i++) {
0691: if (null != getAttributeInfo(i)) {
0692: left[j++] = getAttributeInfo(i);
0693: }
0694: }
0695: AttrInfo[] attributes = new AttrInfo[j];
0696: System.arraycopy(left, 0, attributes, 0, j);
0697: setAttributesCount(j);
0698: setAttributeInfo(attributes);
0699:
0700: // Update the constant pool reference counts
0701: constantPool.updateRefCount();
0702: }
0703:
0704: /** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
0705: * are preserved, all others are killed).
0706: */
0707: public void trimAttrs() {
0708: trimAttrsExcept(null);
0709: }
0710:
0711: /** Remap the entities in the current class file.
0712: * @param nm the name mapper for the class file
0713: */
0714: public void remap(NameMapper nm) {
0715: // Remove unnecessary attributes from the class
0716: String[] attrs = nm.getAttrsToKeep();
0717: if (attrs.length > 0) {
0718: trimAttrsExcept(attrs);
0719: } else {
0720: trimAttrs();
0721: }
0722:
0723: // Remap all the package/interface/class/method/field names
0724: //
0725: String this ClassName = ((Utf8CpInfo) getCpEntry(((ClassCpInfo) getCpEntry(getClassIndex()))
0726: .getClassNameIndex())).getString();
0727:
0728: // Remap the 'inner name' reference of the 'InnerClasses' attribute
0729: for (int i = 0; i < getAttributesCount(); i++) {
0730: AttrInfo attrInfo = getAttributeInfo(i);
0731: if (attrInfo instanceof InnerClassesAttrInfo) {
0732: // For each inner class referemce,
0733: InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo)
0734: .getInnerClasses();
0735: for (int j = 0; j < info.length; j++) {
0736: // Get the 'inner name' (it is a CONSTANT_Utf8)
0737: CpInfo cpInfo = getCpEntry(info[j]
0738: .getInnerNameIndex());
0739: if (cpInfo instanceof Utf8CpInfo) {
0740: // Get the remapped class name
0741: Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
0742: String origClass = utf.getString();
0743:
0744: // Only remap non-anonymous classes (anon are "")
0745: if (!origClass.equals("")) {
0746: // Get the full inner class name
0747: ClassCpInfo innerClassInfo = (ClassCpInfo) getCpEntry(info[j]
0748: .getInnerClassInfoIndex());
0749: String innerClassName = ((Utf8CpInfo) getCpEntry(innerClassInfo
0750: .getClassNameIndex())).getString();
0751:
0752: // It is the remapped simple name that must be stored, so truncate it
0753: String remapClass = nm
0754: .mapClass(innerClassName);
0755: remapClass = remapClass
0756: .substring(remapClass
0757: .lastIndexOf('$') + 1);
0758: int remapIndex = constantPool.remapUtf8To(
0759: remapClass, info[j]
0760: .getInnerNameIndex());
0761: info[j].setInnerNameIndex(remapIndex);
0762: }
0763: }
0764: }
0765: }
0766: }
0767:
0768: // Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
0769: // attribute, in the 'Code' attribute of method structures.
0770: for (int i = 0; i < getMethodsCount(); i++) {
0771: for (int j = 0; j < getMethodInfo(i).getAttributesLength(); j++) {
0772: AttrInfo attrInfo = getMethodInfo(i).getAttribute(j);
0773: if (attrInfo instanceof CodeAttrInfo) {
0774: CodeAttrInfo codeAttrInfo = (CodeAttrInfo) attrInfo;
0775: for (int k = 0; k < codeAttrInfo
0776: .getAttributesLength(); k++) {
0777: AttrInfo innerAttrInfo = codeAttrInfo
0778: .getAttributes()[k];
0779: if (innerAttrInfo instanceof LocalVariableTableAttrInfo) {
0780: LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo) innerAttrInfo;
0781: LocalVariableInfo[] lvts = lvtAttrInfo
0782: .getLocalVariableTable();
0783: for (int m = 0; m < lvts.length; m++) {
0784: // Remap name
0785: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0786: .getNameIndex());
0787: String remapName = nm.mapField(
0788: this ClassName, nameUtf
0789: .getString());
0790: // if no mapping is found use the original name
0791: if (null == remapName) {
0792: remapName = nameUtf.getString();
0793: }
0794: lvts[m].setNameIndex(constantPool
0795: .remapUtf8To(remapName, lvts[m]
0796: .getNameIndex()));
0797:
0798: // Remap descriptor
0799: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0800: .getDescriptorIndex());
0801: String remapDesc = nm
0802: .mapDescriptor(descUtf
0803: .getString());
0804: lvts[m].setDescriptorIndex(constantPool
0805: .remapUtf8To(remapDesc, lvts[m]
0806: .getDescriptorIndex()));
0807: }
0808: }
0809: }
0810: }
0811: }
0812: }
0813:
0814: // Go through all of class's fields and methods mapping 'name' and 'descriptor' references
0815: for (int i = 0; i < getFieldsCount(); i++) {
0816: // Remap field 'name', unless it is 'Synthetic'
0817: FieldInfo field = getFieldInfo(i);
0818: if (!field.isSynthetic()) {
0819: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(field
0820: .getNameIndex());
0821: String remapName = nm.mapField(this ClassName, nameUtf
0822: .getString());
0823: // if no mapping is found use the original name
0824: if (null == remapName) {
0825: remapName = nameUtf.getString();
0826: }
0827: field.setNameIndex(constantPool.remapUtf8To(remapName,
0828: field.getNameIndex()));
0829: }
0830:
0831: // Remap field 'descriptor'
0832: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(field
0833: .getDescriptorIndex());
0834: String remapDesc = nm.mapDescriptor(descUtf.getString());
0835: field.setDescriptorIndex(constantPool.remapUtf8To(
0836: remapDesc, field.getDescriptorIndex()));
0837: }
0838: for (int i = 0; i < getMethodsCount(); i++) {
0839: // Remap method 'name', unless it is 'Synthetic'
0840: MethodInfo method = getMethodInfo(i);
0841: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(method
0842: .getDescriptorIndex());
0843: if (!method.isSynthetic()) {
0844: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(method
0845: .getNameIndex());
0846: String remapName = nm.mapMethod(this ClassName, nameUtf
0847: .getString(), descUtf.getString());
0848: // if no mapping is found use the original name
0849: if (null == remapName) {
0850: remapName = nameUtf.getString();
0851: }
0852: method.setNameIndex(constantPool.remapUtf8To(remapName,
0853: method.getNameIndex()));
0854: }
0855:
0856: // Remap method 'descriptor'
0857: String remapDesc = nm.mapDescriptor(descUtf.getString());
0858: method.setDescriptorIndex(constantPool.remapUtf8To(
0859: remapDesc, method.getDescriptorIndex()));
0860: }
0861:
0862: // Remap all field/method names and descriptors in the constant pool (depends on class names)
0863: int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
0864: for (int i = 0; i < currentCpLength; i++) {
0865: CpInfo cpInfo = getCpEntry(i);
0866: if (null != cpInfo) {
0867: // If this is a CONSTANT_Fieldref, CONSTANT_Methodref or CONSTANT_InterfaceMethodref
0868: // get the CONSTANT_NameAndType and remap the name and the components of the
0869: // descriptor string.
0870: if (cpInfo instanceof RefCpInfo) {
0871: // Get the unmodified class name
0872: ClassCpInfo classInfo = (ClassCpInfo) getCpEntry(((RefCpInfo) cpInfo)
0873: .getClassIndex());
0874: Utf8CpInfo classUtf = (Utf8CpInfo) getCpEntry(classInfo
0875: .getClassNameIndex());
0876: String className = classUtf.getString();
0877:
0878: // Get the current N&T reference and its 'name' and 'descriptor' utf's
0879: int ntIndex = ((RefCpInfo) cpInfo)
0880: .getNameAndTypeIndex();
0881: NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
0882: Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
0883: .getNameIndex());
0884: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
0885: .getDescriptorIndex());
0886:
0887: // Get the remapped versions of 'name' and 'descriptor'
0888: String remapRef;
0889: if (cpInfo instanceof FieldrefCpInfo) {
0890: remapRef = nm.mapField(className, refUtf
0891: .getString());
0892: // if no mapping is found use the original name
0893: if (null == remapRef) {
0894: remapRef = refUtf.getString();
0895: }
0896:
0897: // cloder - check if this is a compiler generated field
0898: // supporting the JDK1.2-or-later '.class' construct
0899: if (refUtf.getString().startsWith("class$")) {
0900: String realClassName = refUtf.getString()
0901: .substring(6).replace('$', '.');
0902: // check whether the hardcoded class name is not a class from the
0903: // Java SDK, i.e. doesn't start with "java." or "javax."
0904: if (!realClassName.startsWith("java.")
0905: && !realClassName
0906: .startsWith("javax.")
0907: && !realClassName
0908: .startsWith("sun.")
0909: && !realClassName
0910: .startsWith("com.sun.")) {
0911: // check whether the hardcoded class name exists in the main tree
0912: // whether it is be obfuscated
0913: Cl cl = getClassTree().findClass(
0914: realClassName);
0915: if (null != cl) {
0916: if (cl.isBeingObfuscated()) {
0917: FileLogger
0918: .getInstance()
0919: .addWarning(
0920: "# WARNING: "
0921: + realClassName
0922: + " shouldn't be obfuscated: it is referenced as "
0923: + realClassName
0924: + ".class from "
0925: + this ClassName);
0926: }
0927: }
0928: }
0929: }
0930: // end cloder
0931: } else {
0932: // instance of method mapping
0933: remapRef = nm.mapMethod(className, refUtf
0934: .getString(), descUtf.getString());
0935: // if no mapping is found use the original name
0936: if (null == remapRef) {
0937: remapRef = refUtf.getString();
0938: }
0939: }
0940: String remapDesc = nm.mapDescriptor(descUtf
0941: .getString());
0942:
0943: // If a remap is required, make a new N&T (increment ref count on 'name' and
0944: // 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
0945: // remap new N&T's utf's
0946: if (!remapRef.equals(refUtf.getString())
0947: || !remapDesc.equals(descUtf.getString())) {
0948: // Get the new N&T guy
0949: NameAndTypeCpInfo newNameTypeInfo;
0950: if (nameTypeInfo.getRefCount() == 1) {
0951: newNameTypeInfo = nameTypeInfo;
0952: } else {
0953: // Create the new N&T info
0954: newNameTypeInfo = (NameAndTypeCpInfo) nameTypeInfo
0955: .clone();
0956:
0957: // Adjust its reference counts of its utf's
0958: ((CpInfo) getCpEntry(newNameTypeInfo
0959: .getNameIndex())).incRefCount();
0960: ((CpInfo) getCpEntry(newNameTypeInfo
0961: .getDescriptorIndex()))
0962: .incRefCount();
0963:
0964: // Append it to the Constant Pool, and
0965: // point the RefCpInfo entry to the new N&T data
0966: ((RefCpInfo) cpInfo)
0967: .setNameAndTypeIndex(constantPool
0968: .addEntry(newNameTypeInfo));
0969:
0970: // Adjust reference counts from RefCpInfo
0971: newNameTypeInfo.incRefCount();
0972: nameTypeInfo.decRefCount();
0973: }
0974:
0975: // Remap the 'name' and 'descriptor' utf's in N&T
0976: newNameTypeInfo.setNameIndex(constantPool
0977: .remapUtf8To(remapRef, newNameTypeInfo
0978: .getNameIndex()));
0979: newNameTypeInfo.setDescriptorIndex(constantPool
0980: .remapUtf8To(remapDesc, newNameTypeInfo
0981: .getDescriptorIndex()));
0982: }
0983: }
0984: }
0985: }
0986:
0987: // Finally, remap all class references to Utf
0988: for (int i = 0; i < constantPool.length(); i++) {
0989: CpInfo cpInfo = getCpEntry(i);
0990: if (cpInfo != null) {
0991: // If this is CONSTANT_Class, remap the class-name Utf8 entry
0992: if (cpInfo instanceof ClassCpInfo) {
0993: ClassCpInfo classInfo = (ClassCpInfo) cpInfo;
0994: Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(classInfo
0995: .getClassNameIndex());
0996: String remapClass = nm.mapClass(utf.getString());
0997: int remapIndex = constantPool.remapUtf8To(
0998: remapClass, classInfo.getClassNameIndex());
0999: classInfo.setClassNameIndex(remapIndex);
1000: }
1001: }
1002: }
1003: }
1004:
1005: /** Export the representation of the class file to a DataOutput stream.
1006: * @param dout the output stream
1007: * @throws IOException if an I/O error occurs
1008: */
1009: public void write(DataOutput dout) throws IOException {
1010: if (dout == null)
1011: throw new IOException("No output stream was provided.");
1012: dout.writeInt(getMagic());
1013: dout.writeShort(getMinorVersion());
1014: dout.writeShort(getMajorVersion());
1015: dout.writeShort(constantPool.length());
1016: for (Iterator iter = constantPool.iterator(); iter.hasNext();) {
1017: CpInfo cpInfo = (CpInfo) iter.next();
1018: if (cpInfo != null) {
1019: cpInfo.write(dout);
1020: }
1021: }
1022: dout.writeShort(getAccessFlags());
1023: dout.writeShort(getClassIndex());
1024: dout.writeShort(getSuperClassIndex());
1025: dout.writeShort(getInterfacesCount());
1026: for (int i = 0; i < getInterfacesCount(); i++) {
1027: dout.writeShort(getInterface(i));
1028: }
1029: dout.writeShort(getFieldsCount());
1030: for (int i = 0; i < getFieldsCount(); i++) {
1031: getFieldInfo(i).write(dout);
1032: }
1033: dout.writeShort(getMethodsCount());
1034: for (int i = 0; i < getMethodsCount(); i++) {
1035: getMethodInfo(i).write(dout);
1036: }
1037: dout.writeShort(getAttributesCount());
1038: for (int i = 0; i < getAttributesCount(); i++) {
1039: getAttributeInfo(i).write(dout);
1040: }
1041: }
1042:
1043: /** Dump the content of the class file to the specified file (used for
1044: * debugging).
1045: * @param pw the print writer
1046: */
1047: public void dump(PrintWriter pw) {
1048: pw
1049: .println("_____________________________________________________________________");
1050: pw.println("CLASS: " + getName());
1051: pw.println("Magic: " + Integer.toHexString(getMagic()) + " ("
1052: + getMagic() + ")");
1053: pw.println("Minor version: "
1054: + Integer.toHexString(getMinorVersion()) + " ("
1055: + getMinorVersion() + ")");
1056: pw.println("Major version: "
1057: + Integer.toHexString(getMajorVersion()) + " ("
1058: + getMajorVersion() + ")");
1059: pw.println();
1060: pw.println("Access: " + getAccessFlags());
1061: pw
1062: .println("This class: " + getClassIndex() + " / "
1063: + getName());
1064: pw.println("Superclass: " + getSuperClassIndex() + " / "
1065: + getSuper());
1066: pw.println();
1067: pw.println("CP length: " + constantPool.length());
1068: for (int i = 0; i < constantPool.length(); i++) {
1069: CpInfo cpInfo = (CpInfo) constantPool.getCpEntry(i);
1070: if (cpInfo != null) {
1071: cpInfo.dump(pw, this , i);
1072: }
1073: }
1074: pw.println("Attributes: " + getAttributesCount());
1075: for (int i = 0; i < getAttributesCount(); i++) {
1076: pw.println();
1077: pw.print("attribute[" + i + "]: ");
1078: getAttributeInfo(i).dump(pw, this );
1079: }
1080: pw.println();
1081: pw.println("Direct implemented interfaces: "
1082: + getInterfacesCount());
1083: for (int i = 0; i < getInterfacesCount(); i++) {
1084: CpInfo info = getCpEntry(getInterface(i));
1085: pw.print(" interface[" + i + "]: ");
1086: if (null == info) {
1087: pw.println("(null)");
1088: } else {
1089: pw
1090: .println(((Utf8CpInfo) getCpEntry(((ClassCpInfo) info)
1091: .getClassNameIndex())).getString());
1092: }
1093: }
1094: pw.println();
1095: pw.println("Declared fields: " + getFieldsCount());
1096: for (int i = 0; i < getFieldsCount(); i++) {
1097: ClassItemInfo info = getFieldInfo(i);
1098: pw.print(" field[" + i + "]: ");
1099: if (null == info) {
1100: pw.println("(null)");
1101: } else {
1102: pw
1103: .println(" name: "
1104: + ((Utf8CpInfo) getCpEntry(info
1105: .getNameIndex())).getString());
1106: pw.println(" descriptor: "
1107: + ((Utf8CpInfo) getCpEntry(info
1108: .getDescriptorIndex())).getString());
1109: }
1110: pw.println(" attributes: " + info.getAttributesLength());
1111: for (int j = 0; j < info.getAttributesLength(); j++) {
1112: pw.print(" attribute[" + j + "]: ");
1113: info.getAttribute(j).dump(pw, this );
1114: pw.println();
1115: }
1116: }
1117: pw.println();
1118: pw.println("Declared methods: " + getMethodsCount());
1119: for (int i = 0; i < getMethodsCount(); i++) {
1120: ClassItemInfo info = getMethodInfo(i);
1121: pw.print(" method[" + i + "]: ");
1122: if (info == null) {
1123: pw.println("(null)");
1124: } else {
1125: pw.println();
1126: pw
1127: .println(" name: "
1128: + ((Utf8CpInfo) getCpEntry(info
1129: .getNameIndex())).getString());
1130: pw.println(" descriptor: "
1131: + ((Utf8CpInfo) getCpEntry(info
1132: .getDescriptorIndex())).getString());
1133: pw
1134: .println(" access flags: "
1135: + info.getAccessFlags());
1136: }
1137: pw.println(" attributes: "
1138: + info.getAttributesLength());
1139: for (int j = 0; j < info.getAttributesLength(); j++) {
1140: pw.print(" attribute[" + j + "]: ");
1141: info.getAttribute(j).dump(pw, this );
1142: pw.println();
1143: }
1144: }
1145: }
1146:
1147: /** Retrieve a list of classes that are used in hardcoded references.
1148: * @return a vector containing strings with class names
1149: * @see #remap
1150: */
1151: public Set getHardcodedClassNames() {
1152: HashSet result = new HashSet();
1153:
1154: int currentCpLength = constantPool.length();
1155: for (int i = 0; i < currentCpLength; i++) {
1156: CpInfo cpInfo = getCpEntry(i);
1157: if (cpInfo != null && cpInfo instanceof RefCpInfo) {
1158: // Get the current name and type reference and its 'name' utf's
1159: int ntIndex = ((RefCpInfo) cpInfo)
1160: .getNameAndTypeIndex();
1161: NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
1162: Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
1163: .getNameIndex());
1164:
1165: if (cpInfo instanceof FieldrefCpInfo) {
1166: // cloder - check if this is a compiler generated field
1167: // supporting the JDK1.2-or-later '.class' construct
1168: if (refUtf.getString().startsWith("class$")) {
1169: String realClassName = refUtf.getString()
1170: .substring(6).replace('$', '.');
1171: if (!realClassName.startsWith("java.")
1172: && !realClassName.startsWith("javax.")
1173: && !realClassName.startsWith("sun.")
1174: && !realClassName
1175: .startsWith("com.sun.")) {
1176: result.add(realClassName);
1177: }
1178: }
1179: // end cloder
1180: }
1181: }
1182: }
1183: return result;
1184: }
1185: }
|