0001: /**
0002: * YGuard -- an obfuscation library for Java(TM) classfiles.
0003: *
0004: * Original Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
0005: * Modifications Copyright (c) 2002 yWorks GmbH (yguard@yworks.com)
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 yguard@yworks.com
0022: *
0023: * Java and all Java-based marks are trademarks or registered
0024: * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
0025: */package com.yworks.yguard.obf.classfile;
0026:
0027: import java.io.*;
0028: import java.util.*;
0029: import com.yworks.yguard.obf.*;
0030: import java.lang.reflect.Modifier;
0031: import com.yworks.yguard.Conversion;
0032: import com.yworks.yguard.ParseException;
0033:
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 Mark Welsh
0041: */
0042: public class ClassFile implements ClassConstants {
0043: // Constants -------------------------------------------------------------
0044: public static final String SEP_REGULAR = "/";
0045: public static final String SEP_INNER = "$";
0046: public static final String LOG_DANGER_HEADER1 = "Methods are called which may break in obfuscated version at runtime.";
0047: public static final String LOG_DANGER_HEADER2 = "Please review your source code to ensure that the dangerous methods are not intended";
0048: public static final String LOG_DANGER_HEADER3 = "to act on classes which have been obfuscated.";
0049: private static final String[] SEMI_DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
0050: "forName(Ljava/lang/String;)Ljava/lang/Class;",
0051: "forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", };
0052: private static final String[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
0053: "forName(Ljava/lang/String;)Ljava/lang/Class;",
0054: "forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
0055: "getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
0056: "getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
0057: "getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
0058: "getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;" };
0059: private static final String LOG_DANGER_CLASS_PRE = " Your class ";
0060: private static final String LOG_DANGER_CLASS_MID = " calls the java.lang.Class method ";
0061: private static final String[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
0062: "defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
0063: "findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
0064: "findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
0065: "loadClass(Ljava/lang/String;)Ljava/lang/Class;",
0066: "loadClass(Ljava/lang/String;Z)Ljava/lang/Class;" };
0067: private static final String LOG_DANGER_CLASSLOADER_PRE = " Your class ";
0068: private static final String LOG_DANGER_CLASSLOADER_MID = " calls the java.lang.ClassLoader method ";
0069:
0070: // Fields ----------------------------------------------------------------
0071: private int u4magic;
0072: private int u2minorVersion;
0073: private int u2majorVersion;
0074: private ConstantPool constantPool;
0075: private int u2accessFlags;
0076: private int u2this Class;
0077: private int u2super Class;
0078: private int u2interfacesCount;
0079: private int u2interfaces[];
0080: private int u2fieldsCount;
0081: private FieldInfo fields[];
0082: private int u2methodsCount;
0083: private MethodInfo methods[];
0084: private int u2attributesCount;
0085: private AttrInfo attributes[];
0086:
0087: private boolean isUnkAttrGone = false;
0088:
0089: private static boolean writeIdString = false;
0090: private static CpInfo cpIdString = null;
0091:
0092: // Class Methods ---------------------------------------------------------
0093: /**
0094: * Define a constant String to include in every output class file.
0095: */
0096: public static void defineIdString(String id) {
0097: if (id != null) {
0098: writeIdString = true;
0099: cpIdString = new Utf8CpInfo(id);
0100: } else {
0101: writeIdString = false;
0102: cpIdString = null;
0103: }
0104: }
0105:
0106: /**
0107: * Create a new ClassFile from the class file format data in the DataInput
0108: * stream.
0109: *
0110: * @throws IOException if class file is corrupt or incomplete
0111: */
0112: public static ClassFile create(DataInput din)
0113: throws java.io.IOException {
0114: if (din == null)
0115: throw new NullPointerException(
0116: "No input stream was provided.");
0117: ClassFile cf = new ClassFile();
0118: cf.read(din);
0119: return cf;
0120: }
0121:
0122: /** Parse a method or field descriptor into a list of parameter names (for methods)
0123: * and a return type, in same format as the Class.forName() method returns . */
0124: public static String[] parseDescriptor(String descriptor) {
0125: return parseDescriptor(descriptor, false);
0126: }
0127:
0128: /** Parse a method or field descriptor into a list of parameter names (for methods)
0129: * and a return type, in same format as the Class.forName() method returns . */
0130: public static String[] parseDescriptor(String descriptor,
0131: boolean isDisplay) {
0132: // Check for field descriptor
0133: String[] names = null;
0134: if (descriptor.charAt(0) != '(') {
0135: names = new String[1];
0136: names[0] = descriptor;
0137: } else {
0138: // Method descriptor
0139: Vector namesVec = new Vector();
0140: descriptor = descriptor.substring(1);
0141: String type = "";
0142: while (descriptor.length() > 0) {
0143: switch (descriptor.charAt(0)) {
0144: case '[':
0145: type = type + "[";
0146: descriptor = descriptor.substring(1);
0147: break;
0148:
0149: case 'B':
0150: case 'C':
0151: case 'D':
0152: case 'F':
0153: case 'I':
0154: case 'J':
0155: case 'S':
0156: case 'Z':
0157: case 'V':
0158: namesVec.addElement(type
0159: + descriptor.substring(0, 1));
0160: descriptor = descriptor.substring(1);
0161: type = "";
0162: break;
0163:
0164: case ')':
0165: descriptor = descriptor.substring(1);
0166: break;
0167:
0168: case 'L': {
0169: int pos = descriptor.indexOf(';') + 1;
0170: namesVec.addElement(type
0171: + descriptor.substring(0, pos));
0172: descriptor = descriptor.substring(pos);
0173: type = "";
0174: }
0175: break;
0176:
0177: default:
0178: throw new IllegalArgumentException(
0179: "Illegal field or method descriptor: "
0180: + descriptor);
0181: }
0182: }
0183: names = new String[namesVec.size()];
0184: for (int i = 0; i < names.length; i++) {
0185: names[i] = (String) namesVec.elementAt(i);
0186: }
0187: }
0188:
0189: // Translate the names from JVM to Class.forName() format.
0190: String[] translatedNames = new String[names.length];
0191: for (int i = 0; i < names.length; i++) {
0192: translatedNames[i] = translateType(names[i], isDisplay);
0193: }
0194: return translatedNames;
0195: }
0196:
0197: /** Translate a type specifier from the internal JVM convention to the Class.forName() one. */
0198: public static String translateType(String inName, boolean isDisplay) {
0199: String outName = null;
0200: switch (inName.charAt(0)) {
0201: case '[': // For array types, Class.forName() inconsistently uses the internal type name
0202: // but with '/' --> '.'
0203: if (!isDisplay) {
0204: // return the Class.forName() form
0205: outName = translate(inName);
0206: } else {
0207: // return the pretty display form
0208: outName = translateType(inName.substring(1), true)
0209: + "[]";
0210: }
0211: break;
0212:
0213: case 'B':
0214: outName = Byte.TYPE.getName();
0215: break;
0216:
0217: case 'C':
0218: outName = Character.TYPE.getName();
0219: break;
0220:
0221: case 'D':
0222: outName = Double.TYPE.getName();
0223: break;
0224:
0225: case 'F':
0226: outName = Float.TYPE.getName();
0227: break;
0228:
0229: case 'I':
0230: outName = Integer.TYPE.getName();
0231: break;
0232:
0233: case 'J':
0234: outName = Long.TYPE.getName();
0235: break;
0236:
0237: case 'S':
0238: outName = Short.TYPE.getName();
0239: break;
0240:
0241: case 'Z':
0242: outName = Boolean.TYPE.getName();
0243: break;
0244:
0245: case 'V':
0246: outName = Void.TYPE.getName();
0247: break;
0248:
0249: case 'L': {
0250: int pos = inName.indexOf(';');
0251: outName = translate(inName
0252: .substring(1, inName.indexOf(';')));
0253: }
0254: break;
0255:
0256: default:
0257: throw new IllegalArgumentException(
0258: "Illegal field or method name: " + inName);
0259: }
0260: return outName;
0261: }
0262:
0263: /** Translate a class name from the internal '/' convention to the regular '.' one. */
0264: public static String translate(String name) {
0265: return name.replace('/', '.');
0266: }
0267:
0268: // Instance Methods ------------------------------------------------------
0269: // Private constructor.
0270: private ClassFile() {
0271: }
0272:
0273: // Import the class data to internal representation.
0274: private void read(DataInput din) throws java.io.IOException {
0275: // Read the class file
0276: u4magic = din.readInt();
0277: u2minorVersion = din.readUnsignedShort();
0278: u2majorVersion = din.readUnsignedShort();
0279:
0280: // Check this is a valid classfile that we can handle
0281: if (u4magic != MAGIC) {
0282: throw new IOException("Invalid magic number in class file.");
0283: }
0284: if (u2majorVersion > MAJOR_VERSION) {
0285: throw new IOException(
0286: "Incompatible version number for class file format: "
0287: + u2majorVersion + "." + u2minorVersion);
0288: }
0289:
0290: int u2constantPoolCount = din.readUnsignedShort();
0291: CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
0292: // Fill the constant pool, recalling the zero entry
0293: // is not persisted, nor are the entries following a Long or Double
0294: for (int i = 1; i < u2constantPoolCount; i++) {
0295: cpInfo[i] = CpInfo.create(din);
0296: if ((cpInfo[i] instanceof LongCpInfo)
0297: || (cpInfo[i] instanceof DoubleCpInfo)) {
0298: i++;
0299: }
0300: }
0301: constantPool = new ConstantPool(this , cpInfo);
0302:
0303: u2accessFlags = din.readUnsignedShort();
0304: u2this Class = din.readUnsignedShort();
0305: u2super Class = din.readUnsignedShort();
0306: u2interfacesCount = din.readUnsignedShort();
0307: u2interfaces = new int[u2interfacesCount];
0308: for (int i = 0; i < u2interfacesCount; i++) {
0309: u2interfaces[i] = din.readUnsignedShort();
0310: }
0311: u2fieldsCount = din.readUnsignedShort();
0312: fields = new FieldInfo[u2fieldsCount];
0313: for (int i = 0; i < u2fieldsCount; i++) {
0314: fields[i] = FieldInfo.create(din, this );
0315: }
0316: u2methodsCount = din.readUnsignedShort();
0317: methods = new MethodInfo[u2methodsCount];
0318: for (int i = 0; i < u2methodsCount; i++) {
0319: methods[i] = MethodInfo.create(din, this );
0320: }
0321: u2attributesCount = din.readUnsignedShort();
0322: attributes = new AttrInfo[u2attributesCount];
0323: for (int i = 0; i < u2attributesCount; i++) {
0324: attributes[i] = AttrInfo.create(din, this );
0325: }
0326: }
0327:
0328: public int getClassFileAccess() {
0329: return u2accessFlags;
0330: }
0331:
0332: public int getModifiers() {
0333: int mods = 0;
0334: if ((u2accessFlags & 0x0001) == 0x0001)
0335: mods |= Modifier.PUBLIC;
0336: if ((u2accessFlags & 0x0010) == 0x0010)
0337: mods |= Modifier.FINAL;
0338: if ((u2accessFlags & 0x0200) == 0x0200)
0339: mods |= Modifier.INTERFACE;
0340: if ((u2accessFlags & 0x0400) == 0x0400)
0341: mods |= Modifier.ABSTRACT;
0342: return mods;
0343: }
0344:
0345: /** Return the name of this classfile. */
0346: public String getName() {
0347: return toName(u2this Class);
0348: }
0349:
0350: /** Return the name of this class's superclass. */
0351: public String getSuper() {
0352: // This may be java/lang/Object, in which case there is no super
0353: return (u2super Class == 0) ? null : toName(u2super Class);
0354: }
0355:
0356: /** Return the names of this class's interfaces. */
0357: public String[] getInterfaces() {
0358: String[] interfaces = new String[u2interfacesCount];
0359: for (int i = 0; i < u2interfacesCount; i++) {
0360: interfaces[i] = toName(u2interfaces[i]);
0361: }
0362: return interfaces;
0363: }
0364:
0365: // Convert a CP index to a class name.
0366: private String toName(int u2index) {
0367: CpInfo classEntry = getCpEntry(u2index);
0368: if (classEntry instanceof ClassCpInfo) {
0369: CpInfo nameEntry = getCpEntry(((ClassCpInfo) classEntry)
0370: .getNameIndex());
0371: if (nameEntry instanceof Utf8CpInfo) {
0372: return ((Utf8CpInfo) nameEntry).getString();
0373: } else {
0374: throw new ParseException(
0375: "Inconsistent Constant Pool in class file.");
0376: }
0377: } else {
0378: throw new ParseException(
0379: "Inconsistent Constant Pool in class file.");
0380: }
0381: }
0382:
0383: /** Return an enumeration of method name/descriptor pairs. */
0384: public Enumeration getMethodEnum() {
0385: Vector vec = new Vector();
0386: for (int i = 0; i < methods.length; i++) {
0387: vec.addElement(new Cons(new Cons(new Boolean(methods[i]
0388: .isSynthetic()), methods[i].getName()), new Cons(
0389: methods[i].getDescriptor(), new Integer(methods[i]
0390: .getAccessFlags()))));
0391: }
0392: return vec.elements();
0393: }
0394:
0395: /** Return an enumeration of field name/descriptor pairs. */
0396: public Enumeration getFieldEnum() {
0397: Vector vec = new Vector();
0398: for (int i = 0; i < fields.length; i++) {
0399: vec.addElement(new Cons(new Cons(new Boolean(fields[i]
0400: .isSynthetic()), fields[i].getName()), new Cons(
0401: fields[i].getDescriptor(), new Integer(fields[i]
0402: .getAccessFlags()))));
0403: }
0404: return vec.elements();
0405: }
0406:
0407: /** Lookup the entry in the constant pool and return as an Object. */
0408: protected CpInfo getCpEntry(int cpIndex) {
0409: return constantPool.getCpEntry(cpIndex);
0410: }
0411:
0412: /** Check for methods which can break the obfuscated code, and log them to a String[]. */
0413: public String[] logDangerousMethods(boolean replaceClassNameStrings) {
0414: Vector warningVec = new Vector();
0415:
0416: // Need only check CONSTANT_Methodref entries of constant pool since
0417: // dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
0418: for (Enumeration enumeration = constantPool.elements(); enumeration
0419: .hasMoreElements();) {
0420: Object o = enumeration.nextElement();
0421: if (o instanceof MethodrefCpInfo) {
0422: // Get the method class name, simple name and descriptor
0423: MethodrefCpInfo entry = (MethodrefCpInfo) o;
0424: ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry
0425: .getClassIndex());
0426: String className = ((Utf8CpInfo) getCpEntry(classEntry
0427: .getNameIndex())).getString();
0428: NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry
0429: .getNameAndTypeIndex());
0430: String name = ((Utf8CpInfo) getCpEntry(ntEntry
0431: .getNameIndex())).getString();
0432: String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry
0433: .getDescriptorIndex())).getString();
0434:
0435: // Check if this is on the proscribed list
0436: if (className.equals("java/lang/Class")
0437: && Tools
0438: .isInArray(name + descriptor,
0439: DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
0440: if (replaceClassNameStrings) {
0441: if (!Tools
0442: .isInArray(name + descriptor,
0443: SEMI_DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
0444: String jMethod = Conversion.toJavaMethod(
0445: name, descriptor);
0446: warningVec.addElement(LOG_DANGER_CLASS_PRE
0447: + Conversion.toJavaClass(getName())
0448: + LOG_DANGER_CLASS_MID + jMethod);
0449: }
0450: }
0451: } else if (Tools
0452: .isInArray(name + descriptor,
0453: DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY)) {
0454: String jMethod = Conversion.toJavaMethod(name,
0455: descriptor);
0456: warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE
0457: + Conversion.toJavaClass(getName())
0458: + LOG_DANGER_CLASSLOADER_MID + jMethod);
0459: } else if ("class$(Ljava/lang/String;)Ljava/lang/Class;"
0460: .equals(name + descriptor)) {
0461: if (!replaceClassNameStrings) {
0462: warningVec
0463: .addElement(LOG_DANGER_CLASS_PRE
0464: + Conversion
0465: .toJavaClass(getName())
0466: + " seems to be using the '.class' construct!");
0467: }
0468: }
0469: }
0470: }
0471:
0472: // Copy any warnings to a String[]
0473: String[] warnings = new String[warningVec.size()];
0474: for (int i = 0; i < warnings.length; i++) {
0475: warnings[i] = (String) warningVec.elementAt(i);
0476: }
0477: return warnings;
0478: }
0479:
0480: /** Check for methods which can break the obfuscated code, and log them. */
0481: private static boolean hasHeader = false;
0482:
0483: public static void resetDangerHeader() {
0484: hasHeader = false;
0485: }
0486:
0487: public void logDangerousMethods(PrintWriter log,
0488: boolean replaceClassNameStrings) {
0489: // Get any warnings and print them to the logfile
0490: String[] warnings = logDangerousMethods(replaceClassNameStrings);
0491: if (warnings != null && warnings.length > 0) {
0492: if (!hasHeader) {
0493: log.println("<!-- WARNING");
0494: log.println(LOG_DANGER_HEADER1);
0495: log.println(LOG_DANGER_HEADER2);
0496: log.println(LOG_DANGER_HEADER3);
0497:
0498: Logger logger = Logger.getInstance();
0499: logger
0500: .warning(LOG_DANGER_HEADER1
0501: + '\n'
0502: + LOG_DANGER_HEADER2
0503: + '\n'
0504: + LOG_DANGER_HEADER3
0505: + '\n'
0506: + "See the logfile for a list of these classes and methods.");
0507:
0508: log.println("-->");
0509: hasHeader = true;
0510: }
0511: if (warnings.length > 0) {
0512: log.println("<!--");
0513: for (int i = 0; i < warnings.length; i++) {
0514: log.println(" " + warnings[i]);
0515: }
0516: log.println("-->");
0517: }
0518: }
0519: }
0520:
0521: /** Check for direct references to Utf8 constant pool entries. */
0522: public void markUtf8Refs(ConstantPool pool) {
0523: try {
0524: // Check for references to Utf8 from outside the constant pool
0525: for (int i = 0; i < fields.length; i++) {
0526: fields[i].markUtf8Refs(pool);
0527: }
0528: for (int i = 0; i < methods.length; i++) {
0529: methods[i].markUtf8Refs(pool); // also checks Code/LVT attrs here
0530: }
0531: for (int i = 0; i < attributes.length; i++) {
0532: attributes[i].markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
0533: }
0534:
0535: // Now check for references from other CP entries
0536: for (Enumeration enumeration = pool.elements(); enumeration
0537: .hasMoreElements();) {
0538: Object o = enumeration.nextElement();
0539: if (o instanceof NameAndTypeCpInfo
0540: || o instanceof ClassCpInfo
0541: || o instanceof StringCpInfo) {
0542: ((CpInfo) o).markUtf8Refs(pool);
0543: }
0544: }
0545: } catch (ArrayIndexOutOfBoundsException e) {
0546: throw new ParseException(
0547: "Inconsistent reference to constant pool.");
0548: }
0549: }
0550:
0551: /** Check for direct references to NameAndType constant pool entries. */
0552: public void markNTRefs(ConstantPool pool) {
0553: try {
0554: // Now check the method and field CP entries
0555: for (Enumeration enumeration = pool.elements(); enumeration
0556: .hasMoreElements();) {
0557: Object o = enumeration.nextElement();
0558: if (o instanceof RefCpInfo) {
0559: ((CpInfo) o).markNTRefs(pool);
0560: }
0561: }
0562: } catch (ArrayIndexOutOfBoundsException e) {
0563: throw new ParseException(
0564: "Inconsistent reference to constant pool.");
0565: }
0566: }
0567:
0568: /**
0569: * Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
0570: * are preserved, all others except the list in the String[] are killed).
0571: */
0572: public void trimAttrsExcept(String[] extraAttrs) {
0573: // Merge additional attributes with required list
0574: String[] keepAttrs = REQUIRED_ATTRS;
0575: if (extraAttrs != null && extraAttrs.length > 0) {
0576: String[] tmp = new String[keepAttrs.length
0577: + extraAttrs.length];
0578: System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
0579: System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length,
0580: extraAttrs.length);
0581: keepAttrs = tmp;
0582: }
0583:
0584: // Traverse all attributes, removing all except those on 'keep' list
0585: for (int i = 0; i < fields.length; i++) {
0586: fields[i].trimAttrsExcept(keepAttrs);
0587: }
0588: for (int i = 0; i < methods.length; i++) {
0589: methods[i].trimAttrsExcept(keepAttrs);
0590: }
0591: for (int i = 0; i < attributes.length; i++) {
0592: if (Tools.isInArray(attributes[i].getAttrName(), keepAttrs)) {
0593: attributes[i].trimAttrsExcept(keepAttrs);
0594: } else {
0595: attributes[i] = null;
0596: }
0597: }
0598:
0599: // Delete the marked attributes
0600: AttrInfo[] left = new AttrInfo[attributes.length];
0601: int j = 0;
0602: for (int i = 0; i < attributes.length; i++) {
0603: if (attributes[i] != null) {
0604: left[j++] = attributes[i];
0605: }
0606: }
0607: attributes = new AttrInfo[j];
0608: System.arraycopy(left, 0, attributes, 0, j);
0609: u2attributesCount = j;
0610:
0611: // Signal that unknown attributes are gone
0612: isUnkAttrGone = true;
0613:
0614: // Update the constant pool reference counts
0615: constantPool.updateRefCount();
0616: }
0617:
0618: public Map getInnerClassModifiers() {
0619: Map map = new HashMap();
0620: for (int i = 0; i < u2attributesCount; i++) {
0621: AttrInfo attrInfo = attributes[i];
0622: if (attrInfo instanceof InnerClassesAttrInfo) {
0623: InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo)
0624: .getInfo();
0625: for (int j = 0; j < info.length; j++) {
0626: InnerClassesInfo ici = info[j];
0627: int index = info[j].getInnerNameIndex();
0628: if (index == 0) { // unnamed inner class
0629: continue;
0630: }
0631: CpInfo cpInfo = getCpEntry(info[j]
0632: .getInnerNameIndex());
0633: if (cpInfo instanceof Utf8CpInfo) {
0634: Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
0635: String origClass = utf.getString();
0636: map.put(origClass, new Integer(ici
0637: .getModifiers()));
0638: }
0639: }
0640: }
0641: }
0642: return map;
0643: }
0644:
0645: /**
0646: * Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
0647: * are preserved, all others are killed).
0648: */
0649: public void trimAttrs() {
0650: trimAttrsExcept(null);
0651: }
0652:
0653: private boolean containsDotClassMethodReference() {
0654: // Need only check CONSTANT_Methodref entries of constant pool since
0655: // dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
0656: for (Enumeration enumeration = constantPool.elements(); enumeration
0657: .hasMoreElements();) {
0658: Object o = enumeration.nextElement();
0659: if (o instanceof MethodrefCpInfo) {
0660: // Get the method class name, simple name and descriptor
0661: MethodrefCpInfo entry = (MethodrefCpInfo) o;
0662: ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry
0663: .getClassIndex());
0664: String className = ((Utf8CpInfo) getCpEntry(classEntry
0665: .getNameIndex())).getString();
0666: NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry
0667: .getNameAndTypeIndex());
0668: String name = ((Utf8CpInfo) getCpEntry(ntEntry
0669: .getNameIndex())).getString();
0670:
0671: if (name.equals("class$")) {
0672: String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry
0673: .getDescriptorIndex())).getString();
0674: if (descriptor
0675: .equals("(Ljava/lang/String;)Ljava/lang/Class;")) {
0676: return true;
0677: }
0678: }
0679: }
0680: }
0681: return false;
0682: }
0683:
0684: private boolean containsClassMethodReference(String cName,
0685: String des) {
0686: // Need only check CONSTANT_Methodref entries of constant pool since
0687: // dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
0688: for (Enumeration enumeration = constantPool.elements(); enumeration
0689: .hasMoreElements();) {
0690: Object o = enumeration.nextElement();
0691: if (o instanceof MethodrefCpInfo) {
0692: // Get the method class name, simple name and descriptor
0693: MethodrefCpInfo entry = (MethodrefCpInfo) o;
0694: ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry
0695: .getClassIndex());
0696: String className = ((Utf8CpInfo) getCpEntry(classEntry
0697: .getNameIndex())).getString();
0698: NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry
0699: .getNameAndTypeIndex());
0700: String name = ((Utf8CpInfo) getCpEntry(ntEntry
0701: .getNameIndex())).getString();
0702: String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry
0703: .getDescriptorIndex())).getString();
0704:
0705: // Check if this is on the proscribed list
0706: if (className.equals(cName)
0707: && (name + descriptor).equals(des)) {
0708: return true;
0709: }
0710: }
0711: }
0712: return false;
0713: }
0714:
0715: /** Remap the entities in the specified ClassFile. */
0716: public void remap(NameMapper nm, boolean replaceClassNameStrings,
0717: PrintWriter log) {
0718: // Remap all the package/interface/class/method/field names
0719: //
0720: String this ClassName = ((Utf8CpInfo) getCpEntry(((ClassCpInfo) getCpEntry(u2this Class))
0721: .getNameIndex())).getString();
0722:
0723: // Remove unnecessary attributes from the class
0724: final String[] attributesToKeep = nm
0725: .getAttrsToKeep(this ClassName);
0726: if (attributesToKeep.length > 0) {
0727: trimAttrsExcept(attributesToKeep);
0728: } else {
0729: trimAttrs();
0730: }
0731:
0732: // Remap the 'inner name' reference of the 'InnerClasses' attribute
0733: for (int i = 0; i < u2attributesCount; i++) {
0734: AttrInfo attrInfo = attributes[i];
0735: if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo) {
0736: remapAnnotations(
0737: (RuntimeVisibleAnnotationsAttrInfo) attrInfo,
0738: nm);
0739: } else if (attrInfo instanceof InnerClassesAttrInfo) {
0740: // For each inner class referemce,
0741: InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo)
0742: .getInfo();
0743: for (int j = 0; j < info.length; j++) {
0744: // Get the 'inner name' (it is a CONSTANT_Utf8)
0745: CpInfo cpInfo = getCpEntry(info[j]
0746: .getInnerNameIndex());
0747: if (cpInfo instanceof Utf8CpInfo) {
0748: // Get the remapped class name
0749: Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
0750: String origClass = utf.getString();
0751:
0752: // Only remap non-anonymous classes (anon are "")
0753: if (!origClass.equals("")) {
0754: // Get the full inner class name
0755: ClassCpInfo innerClassInfo = (ClassCpInfo) getCpEntry(info[j]
0756: .getInnerClassIndex());
0757: String innerClassName = ((Utf8CpInfo) getCpEntry(innerClassInfo
0758: .getNameIndex())).getString();
0759:
0760: // It is the remapped simple name that must be stored, so truncate it
0761: String remapClass = nm
0762: .mapClass(innerClassName);
0763: remapClass = remapClass
0764: .substring(remapClass
0765: .lastIndexOf('$') + 1);
0766: int remapIndex = constantPool.remapUtf8To(
0767: remapClass, info[j]
0768: .getInnerNameIndex());
0769: info[j].setInnerNameIndex(remapIndex);
0770: }
0771: }
0772: }
0773: } else if (attrInfo instanceof EnclosingMethodAttrInfo) {
0774: EnclosingMethodAttrInfo eam = (EnclosingMethodAttrInfo) attrInfo;
0775:
0776: // get the class name of the enclosing file:
0777: CpInfo cpi = getCpEntry(eam.getClassIndex());
0778: if (cpi instanceof ClassCpInfo) {
0779: ClassCpInfo ccpi = (ClassCpInfo) cpi;
0780: cpi = getCpEntry(ccpi.getNameIndex());
0781: if (cpi instanceof Utf8CpInfo) {
0782: Utf8CpInfo utf = (Utf8CpInfo) cpi;
0783: String origClass = utf.getString();
0784:
0785: // do not remap the ClassCpInfo now, it will be remapped automatically, later!
0786: String remapClass = nm.mapClass(origClass);
0787:
0788: // if NT > 0 there is a valid NT to be remapped
0789: if (eam.getNameAndTypeIndex() > 0) {
0790: cpi = getCpEntry(eam.getNameAndTypeIndex());
0791: if (cpi instanceof NameAndTypeCpInfo) {
0792: NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) cpi;
0793: Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
0794: .getNameIndex());
0795: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
0796: .getDescriptorIndex());
0797: String origMethodName = refUtf
0798: .getString();
0799: String origDescriptor = descUtf
0800: .getString();
0801: String remapRef = nm.mapMethod(
0802: origClass, origMethodName,
0803: origDescriptor);
0804: String remapDesc = nm
0805: .mapDescriptor(descUtf
0806: .getString());
0807: eam
0808: .setNameAndTypeIndex(remapNT(
0809: refUtf,
0810: remapRef,
0811: descUtf,
0812: remapDesc,
0813: nameTypeInfo,
0814: eam
0815: .getNameAndTypeIndex()));
0816: }
0817: }
0818: }
0819: }
0820: } else if (attrInfo instanceof SignatureAttrInfo) {
0821: remapSignature(nm, (SignatureAttrInfo) attrInfo);
0822: } else if (attrInfo instanceof SourceFileAttrInfo) {
0823: SourceFileAttrInfo source = (SourceFileAttrInfo) attrInfo;
0824: CpInfo cpInfo = getCpEntry(source.getSourceFileIndex());
0825: if (cpInfo instanceof Utf8CpInfo) {
0826: Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
0827: String origName = utf.getString();
0828: if (origName != null && origName.length() > 0) {
0829: String newName = nm.mapSourceFile(
0830: this ClassName, origName);
0831: if (!origName.equals(newName)) {
0832: if (newName == null || newName.length() < 1) {
0833: AttrInfo[] newAttributes = new AttrInfo[attributes.length - 1];
0834: System.arraycopy(attributes, 0,
0835: newAttributes, 0, i);
0836: if (newAttributes.length > i) {
0837: System.arraycopy(attributes, i + 1,
0838: newAttributes, i,
0839: newAttributes.length - i);
0840: }
0841: attributes = newAttributes;
0842: u2attributesCount--;
0843: i--;
0844: constantPool.decRefCount(source
0845: .getAttrNameIndex());
0846: utf.decRefCount();
0847: } else {
0848: int remapIndex = constantPool
0849: .remapUtf8To(newName, source
0850: .getSourceFileIndex());
0851: source.setSourceFileIndex(remapIndex);
0852: }
0853: }
0854: }
0855: }
0856: }
0857: }
0858:
0859: // Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
0860: // attribute, in the 'Code' attribute of method structures.
0861: for (int i = 0; i < u2methodsCount; i++) {
0862: for (int j = 0; j < methods[i].u2attributesCount; j++) {
0863: final String methodName = methods[i].getName();
0864: final String descriptor = methods[i].getDescriptor();
0865: AttrInfo attrInfo = methods[i].attributes[j];
0866:
0867: if (attrInfo instanceof AnnotationDefaultAttrInfo) {
0868: remapAnnotationDefault(
0869: (AnnotationDefaultAttrInfo) attrInfo, nm);
0870: } else if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo) {
0871: remapAnnotations(
0872: (RuntimeVisibleAnnotationsAttrInfo) attrInfo,
0873: nm);
0874: } else if (attrInfo instanceof RuntimeVisibleParameterAnnotationsAttrInfo) {
0875: remapAnnotations(
0876: (RuntimeVisibleParameterAnnotationsAttrInfo) attrInfo,
0877: nm);
0878: } else if (attrInfo instanceof SignatureAttrInfo) {
0879: remapSignature(nm, (SignatureAttrInfo) attrInfo);
0880: } else if (attrInfo instanceof CodeAttrInfo) {
0881: CodeAttrInfo codeAttrInfo = (CodeAttrInfo) attrInfo;
0882: for (int k = 0; k < codeAttrInfo.u2attributesCount; k++) {
0883: AttrInfo innerAttrInfo = codeAttrInfo.attributes[k];
0884: if (innerAttrInfo instanceof LocalVariableTableAttrInfo) {
0885: LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo) innerAttrInfo;
0886: LocalVariableInfo[] lvts = lvtAttrInfo
0887: .getLocalVariableTable();
0888: for (int m = 0; m < lvts.length; m++) {
0889: // Remap name
0890: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0891: .getNameIndex());
0892: String remapName = nm
0893: .mapLocalVariable(
0894: this ClassName,
0895: methodName, descriptor,
0896: nameUtf.getString());
0897: if (remapName == null
0898: || remapName.length() < 1) {
0899: constantPool.decRefCount(lvts[m]
0900: .getNameIndex());
0901: constantPool.decRefCount(lvts[m]
0902: .getDescriptorIndex());
0903: LocalVariableInfo[] newArray = new LocalVariableInfo[lvts.length - 1];
0904: System.arraycopy(lvts, 0, newArray,
0905: 0, m);
0906: if (newArray.length > m) {
0907: System.arraycopy(lvts, m + 1,
0908: newArray, m,
0909: newArray.length - m);
0910: }
0911: lvts = newArray;
0912: lvtAttrInfo
0913: .setLocalVariableTable(lvts);
0914: m--;
0915: } else {
0916: lvts[m]
0917: .setNameIndex(constantPool
0918: .remapUtf8To(
0919: remapName,
0920: lvts[m]
0921: .getNameIndex()));
0922:
0923: // Remap descriptor
0924: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0925: .getDescriptorIndex());
0926: String remapDesc = nm
0927: .mapDescriptor(descUtf
0928: .getString());
0929: lvts[m]
0930: .setDescriptorIndex(constantPool
0931: .remapUtf8To(
0932: remapDesc,
0933: lvts[m]
0934: .getDescriptorIndex()));
0935: }
0936: }
0937: } else if (innerAttrInfo instanceof LocalVariableTypeTableAttrInfo) {
0938: LocalVariableTypeTableAttrInfo lvttAttrInfo = (LocalVariableTypeTableAttrInfo) innerAttrInfo;
0939: LocalVariableTypeInfo[] lvts = lvttAttrInfo
0940: .getLocalVariableTypeTable();
0941: for (int m = 0; m < lvts.length; m++) {
0942: // Remap name
0943: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0944: .getNameIndex());
0945: String remapName = nm
0946: .mapLocalVariable(
0947: this ClassName,
0948: methodName, descriptor,
0949: nameUtf.getString());
0950: if (remapName == null
0951: || remapName.length() < 1) {
0952: constantPool.decRefCount(lvts[m]
0953: .getNameIndex());
0954: constantPool.decRefCount(lvts[m]
0955: .getSignatureIndex());
0956: LocalVariableTypeInfo[] newArray = new LocalVariableTypeInfo[lvts.length - 1];
0957: System.arraycopy(lvts, 0, newArray,
0958: 0, m);
0959: if (newArray.length > m) {
0960: System.arraycopy(lvts, m + 1,
0961: newArray, m,
0962: newArray.length - m);
0963: }
0964: lvts = newArray;
0965: lvttAttrInfo
0966: .setLocalVariableTypeTable(lvts);
0967: m--;
0968: } else {
0969: lvts[m]
0970: .setNameIndex(constantPool
0971: .remapUtf8To(
0972: remapName,
0973: lvts[m]
0974: .getNameIndex()));
0975:
0976: // Remap descriptor
0977: Utf8CpInfo signatureUtf = (Utf8CpInfo) getCpEntry(lvts[m]
0978: .getSignatureIndex());
0979: String remapSig = nm
0980: .mapSignature(signatureUtf
0981: .getString());
0982: lvts[m]
0983: .setSignatureIndex(constantPool
0984: .remapUtf8To(
0985: remapSig,
0986: lvts[m]
0987: .getSignatureIndex()));
0988: }
0989: }
0990: } else if (innerAttrInfo instanceof LineNumberTableAttrInfo) {
0991: LineNumberTableAttrInfo ltai = (LineNumberTableAttrInfo) innerAttrInfo;
0992: if (!nm.mapLineNumberTable(this ClassName,
0993: methodName, descriptor, ltai)) {
0994: AttrInfo[] newAtt = new AttrInfo[codeAttrInfo.u2attributesCount - 1];
0995: System.arraycopy(
0996: codeAttrInfo.attributes, 0,
0997: newAtt, 0, k);
0998: if (newAtt.length > k) {
0999: System.arraycopy(
1000: codeAttrInfo.attributes,
1001: k + 1, newAtt, k,
1002: newAtt.length - k);
1003: }
1004: codeAttrInfo.attributes = newAtt;
1005: codeAttrInfo.u2attributesCount--;
1006: k--;
1007: }
1008: }
1009: }
1010: }
1011: }
1012: }
1013:
1014: // Go through all of class's fields and methods mapping 'name' and 'descriptor' references
1015: for (int i = 0; i < u2fieldsCount; i++) {
1016: // Remap field 'name', unless it is 'Synthetic'
1017: FieldInfo field = fields[i];
1018: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(field
1019: .getNameIndex());
1020: if (!field.isSynthetic()
1021: || nameUtf.getString().startsWith("class$")) {
1022: String remapName = nm.mapField(this ClassName, nameUtf
1023: .getString());
1024: field.setNameIndex(constantPool.remapUtf8To(remapName,
1025: field.getNameIndex()));
1026: }
1027:
1028: for (int j = 0; j < field.u2attributesCount; j++) {
1029: AttrInfo attrInfo = field.attributes[j];
1030: if (attrInfo instanceof RuntimeVisibleAnnotationsAttrInfo) {
1031: remapAnnotations(
1032: (RuntimeVisibleAnnotationsAttrInfo) attrInfo,
1033: nm);
1034: } else if (attrInfo instanceof SignatureAttrInfo) {
1035: remapSignature(nm, (SignatureAttrInfo) attrInfo);
1036: }
1037: }
1038:
1039: // Remap field 'descriptor'
1040: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(field
1041: .getDescriptorIndex());
1042: String remapDesc = nm.mapDescriptor(descUtf.getString());
1043: field.setDescriptorIndex(constantPool.remapUtf8To(
1044: remapDesc, field.getDescriptorIndex()));
1045: }
1046: for (int i = 0; i < u2methodsCount; i++) {
1047: // Remap method 'name', unless it is 'Synthetic'
1048: MethodInfo method = methods[i];
1049: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(method
1050: .getDescriptorIndex());
1051: if (!method.isSynthetic()) {
1052: Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(method
1053: .getNameIndex());
1054: String remapName = nm.mapMethod(this ClassName, nameUtf
1055: .getString(), descUtf.getString());
1056: method.setNameIndex(constantPool.remapUtf8To(remapName,
1057: method.getNameIndex()));
1058: }
1059:
1060: // Remap method 'descriptor'
1061: String remapDesc = nm.mapDescriptor(descUtf.getString());
1062: method.setDescriptorIndex(constantPool.remapUtf8To(
1063: remapDesc, method.getDescriptorIndex()));
1064: }
1065:
1066: // check whether .class constructs of Class.forName calls reside in the code..
1067: if (replaceClassNameStrings && nm instanceof ClassTree)
1068: // &&
1069: // (containsClassMethodReference("java/lang/Class","forName(Ljava/lang/String;)Ljava/lang/Class;")) ||
1070: // (containsClassMethodReference("java/lang/Class","forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;")) ||
1071: // (containsDotClassMethodReference()))
1072: {
1073: this .replaceConstantPoolStrings((ClassTree) nm);
1074: }
1075:
1076: // Remap all field/method names and descriptors in the constant pool (depends on class names)
1077: int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
1078: for (int i = 0; i < currentCpLength; i++) {
1079: CpInfo cpInfo = getCpEntry(i);
1080: if (cpInfo != null) {
1081: // If this is a CONSTANT_Fieldref, CONSTANT_Methodref or CONSTANT_InterfaceMethodref
1082: // get the CONSTANT_NameAndType and remap the name and the components of the
1083: // descriptor string.
1084: if (cpInfo instanceof RefCpInfo) {
1085: // Get the unmodified class name
1086: ClassCpInfo classInfo = (ClassCpInfo) getCpEntry(((RefCpInfo) cpInfo)
1087: .getClassIndex());
1088: Utf8CpInfo classUtf = (Utf8CpInfo) getCpEntry(classInfo
1089: .getNameIndex());
1090: String className = classUtf.getString();
1091:
1092: // Get the current N&T reference and its 'name' and 'descriptor' utf's
1093: int ntIndex = ((RefCpInfo) cpInfo)
1094: .getNameAndTypeIndex();
1095:
1096: NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
1097: Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
1098: .getNameIndex());
1099: Utf8CpInfo descUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo
1100: .getDescriptorIndex());
1101:
1102: // Get the remapped versions of 'name' and 'descriptor'
1103: String remapRef;
1104: if (cpInfo instanceof FieldrefCpInfo) {
1105: remapRef = nm.mapField(className, refUtf
1106: .getString());
1107:
1108: // check if this is a compiler generated field
1109: // supporting the JDK1.2-or-later '.class' construct
1110: if (refUtf.getString().startsWith("class$")) {
1111: if (!replaceClassNameStrings) {
1112: String internalClassName = refUtf
1113: .getString().substring(6);
1114: String realClassName = internalClassName
1115: .replace('$', '.');
1116: internalClassName = internalClassName
1117: .replace('$', '/');
1118: String map = nm
1119: .mapClass(internalClassName);
1120: if (map != null
1121: && !internalClassName
1122: .equals(map)) {
1123: String warning = realClassName
1124: + " shouldn't be obfuscated: it is most likely referenced as "
1125: + realClassName
1126: + ".class from "
1127: + Conversion
1128: .toJavaClass(this ClassName);
1129: Logger.getInstance().warning(
1130: warning);
1131: log.println("<!-- WARNING: "
1132: + warning + " -->");
1133: }
1134: }
1135: }
1136: } else {
1137: remapRef = nm.mapMethod(className, refUtf
1138: .getString(), descUtf.getString());
1139: }
1140: String remapDesc = nm.mapDescriptor(descUtf
1141: .getString());
1142: ((RefCpInfo) cpInfo).setNameAndTypeIndex(remapNT(
1143: refUtf, remapRef, descUtf, remapDesc,
1144: nameTypeInfo, ((RefCpInfo) cpInfo)
1145: .getNameAndTypeIndex()));
1146: }
1147: }
1148: }
1149:
1150: // Finally, remap all class references to Utf
1151: for (int i = 0; i < constantPool.length(); i++) {
1152: CpInfo cpInfo = getCpEntry(i);
1153: if (cpInfo != null) {
1154: // If this is CONSTANT_Class, remap the class-name Utf8 entry
1155: if (cpInfo instanceof ClassCpInfo) {
1156: ClassCpInfo classInfo = (ClassCpInfo) cpInfo;
1157: Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(classInfo
1158: .getNameIndex());
1159: String remapClass = nm.mapClass(utf.getString());
1160: int remapIndex = constantPool.remapUtf8To(
1161: remapClass, classInfo.getNameIndex());
1162: classInfo.setNameIndex(remapIndex);
1163: }
1164: }
1165: }
1166: }
1167:
1168: private void remapAnnotationDefault(
1169: AnnotationDefaultAttrInfo annotationDefault, NameMapper nm) {
1170: remapElementValue(annotationDefault.elementValue, nm);
1171: }
1172:
1173: private void remapAnnotations(
1174: RuntimeVisibleAnnotationsAttrInfo annotation, NameMapper nm) {
1175: final AnnotationInfo[] annotations = annotation
1176: .getAnnotations();
1177: if (annotations != null) {
1178: for (int i = 0; i < annotations.length; i++) {
1179: remapAnnotation(annotations[i], nm);
1180: }
1181: }
1182: }
1183:
1184: private void remapAnnotations(
1185: RuntimeVisibleParameterAnnotationsAttrInfo annotation,
1186: NameMapper nm) {
1187: final ParameterAnnotationInfo[] annotations = annotation
1188: .getParameterAnnotations();
1189: if (annotations != null) {
1190: for (int i = 0; i < annotations.length; i++) {
1191: final ParameterAnnotationInfo info = annotations[i];
1192: final AnnotationInfo[] a = info.getAnnotations();
1193: if (a != null) {
1194: for (int j = 0; j < a.length; j++) {
1195: remapAnnotation(a[j], nm);
1196: }
1197: }
1198: }
1199: }
1200: }
1201:
1202: private void remapAnnotation(AnnotationInfo annotation,
1203: NameMapper nm) {
1204: CpInfo info = getCpEntry(annotation.u2typeIndex);
1205: if (info instanceof Utf8CpInfo) {
1206: Utf8CpInfo utf = (Utf8CpInfo) info;
1207: String s = utf.getString();
1208: if (s.length() > 2 && s.charAt(0) == 'L'
1209: && s.charAt(s.length() - 1) == ';') {
1210: String fqn = s.substring(1, s.length() - 1);
1211: String newFqn = nm.mapClass(fqn);
1212: if (!fqn.equals(newFqn)) {
1213: annotation.u2typeIndex = constantPool.remapUtf8To(
1214: 'L' + newFqn + ';', annotation.u2typeIndex);
1215: }
1216: final ElementValuePairInfo[] evp = annotation
1217: .getElementValuePairs();
1218: if (evp != null) {
1219: for (int i = 0; i < evp.length; i++) {
1220: final ElementValuePairInfo elementValuePair = evp[i];
1221: utf = (Utf8CpInfo) getCpEntry(elementValuePair.u2ElementNameIndex);
1222: String remapName = nm.mapAnnotationField(fqn,
1223: utf.getString());
1224: if (!remapName.equals(utf.getString())) {
1225: elementValuePair.u2ElementNameIndex = constantPool
1226: .remapUtf8To(
1227: remapName,
1228: elementValuePair.u2ElementNameIndex);
1229: }
1230: final ElementValueInfo elementValue = elementValuePair.elementValue;
1231: remapElementValue(elementValue, nm);
1232: }
1233: }
1234: }
1235: }
1236: }
1237:
1238: private void remapElementValue(ElementValueInfo elementValue,
1239: NameMapper nm) {
1240: switch (elementValue.u1Tag) {
1241: case 'B':
1242: case 'C':
1243: case 'D':
1244: case 'F':
1245: case 'I':
1246: case 'J':
1247: case 'S':
1248: case 'Z':
1249: case 's':
1250: // do nothing, this is a constant
1251: break;
1252: case 'e':
1253: // remap the type...
1254: {
1255: Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(elementValue.u2typeNameIndex);
1256: String name = utf.getString();
1257: if (name.length() > 22 & name.charAt(0) == 'L'
1258: && name.charAt(name.length() - 1) == ';') {
1259: name = name.substring(1, name.length() - 1);
1260: String remapName = 'L' + nm.mapClass(name) + ';';
1261: elementValue.u2typeNameIndex = constantPool
1262: .remapUtf8To(remapName,
1263: elementValue.u2typeNameIndex);
1264: }
1265: }
1266: // leave the constant value in u2constNameIndex
1267: break;
1268: case 'c': {
1269: Utf8CpInfo utf = (Utf8CpInfo) getCpEntry(elementValue.u2cpIndex);
1270: String name = utf.getString();
1271: if (name.length() > 22 & name.charAt(0) == 'L'
1272: && name.charAt(name.length() - 1) == ';') {
1273: name = name.substring(1, name.length() - 1);
1274: String remapName = 'L' + nm.mapClass(name) + ';';
1275: elementValue.u2cpIndex = constantPool.remapUtf8To(
1276: remapName, elementValue.u2cpIndex);
1277: }
1278: }
1279: break;
1280: case '@':
1281: remapAnnotation(elementValue.nestedAnnotation, nm);
1282: break;
1283: case '[':
1284: for (int j = 0; j < elementValue.arrayValues.length; j++) {
1285: final ElementValueInfo evi = elementValue.arrayValues[j];
1286: remapElementValue(evi, nm);
1287: }
1288: break;
1289: default:
1290: throw new RuntimeException(
1291: "Unknown type tag in annotation!");
1292: }
1293: }
1294:
1295: private void remapSignature(NameMapper nm,
1296: SignatureAttrInfo signature) {
1297: CpInfo cpInfo = getCpEntry(signature.getSignatureIndex());
1298: if (cpInfo instanceof Utf8CpInfo) {
1299: Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
1300: String sig = utf.getString();
1301: String remapSignature = nm.mapSignature(sig);
1302: if (!sig.equals(remapSignature)) {
1303: int remapIndex = constantPool.remapUtf8To(
1304: remapSignature, signature.getSignatureIndex());
1305: signature.setSignatureIndex(remapIndex);
1306: }
1307: }
1308: }
1309:
1310: private int remapNT(Utf8CpInfo refUtf, String remapRef,
1311: Utf8CpInfo descUtf, String remapDesc,
1312: NameAndTypeCpInfo nameTypeInfo, int nameAndTypeIndex) {
1313: // If a remap is required, make a new N&T (increment ref count on 'name' and
1314: // 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
1315: // remap new N&T's utf's
1316: if (!remapRef.equals(refUtf.getString())
1317: || !remapDesc.equals(descUtf.getString())) {
1318: // Get the new N&T guy
1319: NameAndTypeCpInfo newNameTypeInfo;
1320: if (nameTypeInfo.getRefCount() == 1) {
1321: newNameTypeInfo = nameTypeInfo;
1322: } else {
1323: // Create the new N&T info
1324: newNameTypeInfo = (NameAndTypeCpInfo) nameTypeInfo
1325: .clone();
1326:
1327: // Adjust its reference counts of its utf's
1328: ((CpInfo) getCpEntry(newNameTypeInfo.getNameIndex()))
1329: .incRefCount();
1330: ((CpInfo) getCpEntry(newNameTypeInfo
1331: .getDescriptorIndex())).incRefCount();
1332:
1333: // Append it to the Constant Pool, and
1334: // point the RefCpInfo entry to the new N&T data
1335: nameAndTypeIndex = constantPool
1336: .addEntry(newNameTypeInfo);
1337:
1338: // Adjust reference counts from RefCpInfo
1339: newNameTypeInfo.incRefCount();
1340: nameTypeInfo.decRefCount();
1341: }
1342:
1343: // Remap the 'name' and 'descriptor' utf's in N&T
1344: newNameTypeInfo.setNameIndex(constantPool.remapUtf8To(
1345: remapRef, newNameTypeInfo.getNameIndex()));
1346: newNameTypeInfo.setDescriptorIndex(constantPool
1347: .remapUtf8To(remapDesc, newNameTypeInfo
1348: .getDescriptorIndex()));
1349: }
1350: return nameAndTypeIndex;
1351: }
1352:
1353: /**
1354: * goes through the constantpool, identifies classnamestrings and replaces
1355: * them appropriately if necessary
1356: */
1357: private void replaceConstantPoolStrings(ClassTree ct) {
1358: for (Enumeration enumeration = constantPool.elements(); enumeration
1359: .hasMoreElements();) {
1360: CpInfo cpi = (CpInfo) enumeration.nextElement();
1361: if (cpi instanceof Utf8CpInfo) {
1362: Utf8CpInfo ui = (Utf8CpInfo) cpi;
1363: String s = ui.getString();
1364: boolean jikes = false;
1365: if (s.length() > 5 && s.startsWith("[L")
1366: && s.endsWith(";")) {
1367: s = s.substring(2, s.length() - 1);
1368: jikes = true;
1369: }
1370: if (s.length() > 2
1371: && Character.isJavaIdentifierPart(s.charAt(s
1372: .length() - 1)) && s.indexOf(' ') < 0
1373: && s.indexOf('.') > 0) {
1374: Cl cl = ct.findClassForName(s);
1375: if (cl != null) {
1376: if (!cl.getFullInName().equals(
1377: cl.getFullOutName())) {
1378: if (jikes) {
1379: ui.setString("[L"
1380: + cl.getFullOutName().replace(
1381: '/', '.') + ";");
1382: } else {
1383: ui.setString(cl.getFullOutName()
1384: .replace('/', '.'));
1385: }
1386: }
1387: }
1388: }
1389: }
1390: }
1391: }
1392:
1393: /** Export the representation to a DataOutput stream. */
1394: public void write(DataOutput dout) throws java.io.IOException {
1395: if (dout == null)
1396: throw new NullPointerException(
1397: "No output stream was provided.");
1398: dout.writeInt(u4magic);
1399: dout.writeShort(u2minorVersion);
1400: dout.writeShort(u2majorVersion);
1401: dout
1402: .writeShort(constantPool.length()
1403: + (writeIdString ? 1 : 0));
1404: for (Enumeration enumeration = constantPool.elements(); enumeration
1405: .hasMoreElements();) {
1406: CpInfo cpInfo = (CpInfo) enumeration.nextElement();
1407: if (cpInfo != null) {
1408: cpInfo.write(dout);
1409: }
1410: }
1411: if (writeIdString) {
1412: cpIdString.write(dout);
1413: }
1414: dout.writeShort(u2accessFlags);
1415: dout.writeShort(u2this Class);
1416: dout.writeShort(u2super Class);
1417: dout.writeShort(u2interfacesCount);
1418: for (int i = 0; i < u2interfacesCount; i++) {
1419: dout.writeShort(u2interfaces[i]);
1420: }
1421: dout.writeShort(u2fieldsCount);
1422: for (int i = 0; i < u2fieldsCount; i++) {
1423: fields[i].write(dout);
1424: }
1425: dout.writeShort(u2methodsCount);
1426: for (int i = 0; i < u2methodsCount; i++) {
1427: methods[i].write(dout);
1428: }
1429: dout.writeShort(u2attributesCount);
1430: for (int i = 0; i < u2attributesCount; i++) {
1431: attributes[i].write(dout);
1432: }
1433: }
1434:
1435: /** Dump the content of the class file to the specified file (used for debugging). */
1436: public void dump(PrintWriter pw) {
1437: pw
1438: .println("_____________________________________________________________________");
1439: pw.println("CLASS: " + getName());
1440: pw.println("Magic: " + Integer.toHexString(u4magic));
1441: pw.println("Minor version: "
1442: + Integer.toHexString(u2minorVersion));
1443: pw.println("Major version: "
1444: + Integer.toHexString(u2majorVersion));
1445: pw.println();
1446: pw.println("CP length: "
1447: + Integer.toHexString(constantPool.length()));
1448: for (int i = 0; i < constantPool.length(); i++) {
1449: CpInfo cpInfo = (CpInfo) constantPool.getCpEntry(i);
1450: if (cpInfo != null) {
1451: cpInfo.dump(pw, this , i);
1452: }
1453: }
1454: pw.println("Access: " + Integer.toHexString(u2accessFlags));
1455: pw.println("This class: " + getName());
1456: pw.println("Superclass: " + getSuper());
1457: pw.println("Interfaces count: "
1458: + Integer.toHexString(u2interfacesCount));
1459: for (int i = 0; i < u2interfacesCount; i++) {
1460: CpInfo info = getCpEntry(u2interfaces[i]);
1461: if (info == null) {
1462: pw.println(" Interface " + Integer.toHexString(i)
1463: + ": (null)");
1464: } else {
1465: pw.println(" Interface "
1466: + Integer.toHexString(i)
1467: + ": "
1468: + ((Utf8CpInfo) getCpEntry(((ClassCpInfo) info)
1469: .getNameIndex())).getString());
1470: }
1471: }
1472: pw.println("Fields count: "
1473: + Integer.toHexString(u2fieldsCount));
1474: for (int i = 0; i < u2fieldsCount; i++) {
1475: ClassItemInfo info = fields[i];
1476: if (info == null) {
1477: pw.println(" Field " + Integer.toHexString(i)
1478: + ": (null)");
1479: } else {
1480: pw
1481: .println(" Field "
1482: + Integer.toHexString(i)
1483: + ": "
1484: + ((Utf8CpInfo) getCpEntry(info
1485: .getNameIndex())).getString()
1486: + " "
1487: + ((Utf8CpInfo) getCpEntry(info
1488: .getDescriptorIndex()))
1489: .getString());
1490: }
1491: pw.println(" Attrs count: "
1492: + Integer.toHexString(info.u2attributesCount));
1493: for (int j = 0; j < info.u2attributesCount; j++) {
1494: pw.println(info.attributes[j]);
1495: }
1496: }
1497: pw.println("Methods count: "
1498: + Integer.toHexString(u2methodsCount));
1499: for (int i = 0; i < u2methodsCount; i++) {
1500: ClassItemInfo info = methods[i];
1501: if (info == null) {
1502: pw.println(" Method " + Integer.toHexString(i)
1503: + ": (null)");
1504: } else {
1505: pw
1506: .println(" Method "
1507: + Integer.toHexString(i)
1508: + ": "
1509: + ((Utf8CpInfo) getCpEntry(info
1510: .getNameIndex())).getString()
1511: + " "
1512: + ((Utf8CpInfo) getCpEntry(info
1513: .getDescriptorIndex()))
1514: .getString()
1515: + " "
1516: + Integer.toHexString(info
1517: .getAccessFlags()));
1518: }
1519: pw.println(" Attrs count: "
1520: + Integer.toHexString(info.u2attributesCount));
1521: for (int j = 0; j < info.u2attributesCount; j++) {
1522: if (info.attributes[j] instanceof CodeAttrInfo) {
1523: pw.println(info.attributes[j]);
1524: CodeAttrInfo cai = (CodeAttrInfo) info.attributes[j];
1525: for (int k = 0; k < cai.u2attributesCount; k++) {
1526: pw.println(cai.attributes[k]);
1527: }
1528: } else {
1529: pw.println(info.attributes[j]);
1530: }
1531: }
1532: }
1533: pw.println("Attrs count: "
1534: + Integer.toHexString(u2attributesCount));
1535: for (int i = 0; i < u2attributesCount; i++) {
1536: pw.println(attributes[i]);
1537: }
1538: }
1539:
1540: }
|