0001: /* ===========================================================================
0002: * $RCSfile: ClassTree.java,v $
0003: * ===========================================================================
0004: *
0005: * RetroGuard -- an obfuscation package for Java classfiles.
0006: *
0007: * Copyright (c) 1998-2006 Mark Welsh (markw@retrologic.com)
0008: *
0009: * This program can be redistributed and/or modified under the terms of the
0010: * Version 2 of the GNU General Public License as published by the Free
0011: * Software Foundation.
0012: *
0013: * This program is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: * GNU General Public License for more details.
0017: *
0018: */
0019:
0020: package COM.rl.obf;
0021:
0022: import java.io.*;
0023: import java.lang.reflect.*;
0024: import java.util.*;
0025: import COM.rl.obf.classfile.*;
0026:
0027: /**
0028: * Tree structure of package levels, classes, methods and fields used for obfuscation.
0029: *
0030: * @author Mark Welsh
0031: */
0032: public class ClassTree implements NameMapper {
0033: // Constants -------------------------------------------------------------
0034: public static final char PACKAGE_LEVEL = '/';
0035: public static final char CLASS_LEVEL = '$';
0036: public static final char METHOD_FIELD_LEVEL = '/';
0037: private static final String LOG_PRE_UNOBFUSCATED = "# Names reserved from obfuscation:";
0038: private static final String LOG_PRE_OBFUSCATED = "# Obfuscated name mappings (some of these may be unchanged due to polymorphism constraints):";
0039: private static final String LOG_FREQUENCY_TABLE = "# Obfuscated name overloading frequency:";
0040: private static final String LOG_DANGER_HEADER1 = "# WARNING - Reflection methods are called which may unavoidably break in the";
0041: private static final String LOG_DANGER_HEADER2 = "# obfuscated version at runtime. Please review your source code to ensure";
0042: private static final String LOG_DANGER_HEADER3 = "# these methods do not act on classes in the obfuscated Jar file.";
0043:
0044: // Fields ----------------------------------------------------------------
0045: private Vector retainAttrs = new Vector(); // List of attributes to retain
0046: private Pk root = null; // Root package in database (Java default package)
0047:
0048: // Class methods ---------------------------------------------------------
0049: /** Return a fully qualified name broken into package/class segments. */
0050: public static Enumeration getNameEnum(String name) throws Exception {
0051: Vector vec = new Vector();
0052: String nameOrig = name;
0053: while (name != null && !name.equals("")) {
0054: int posP = name.indexOf(PACKAGE_LEVEL);
0055: int posC = name.indexOf(CLASS_LEVEL);
0056: SimpleName simpleName = null;
0057: if (posP == -1 && posC == -1) {
0058: simpleName = new SimpleName(name).setAsClass();
0059: name = "";
0060: }
0061: if (posP == -1 && posC != -1) {
0062: simpleName = new SimpleName(name.substring(0, posC))
0063: .setAsClass();
0064: name = name.substring(posC + 1, name.length());
0065: }
0066: if (posP != -1 && posC == -1) {
0067: simpleName = new SimpleName(name.substring(0, posP))
0068: .setAsPackage();
0069: name = name.substring(posP + 1, name.length());
0070: }
0071: if (posP != -1 && posC != -1) {
0072: if (posP < posC) {
0073: simpleName = new SimpleName(name.substring(0, posP))
0074: .setAsPackage();
0075: name = name.substring(posP + 1, name.length());
0076: } else {
0077: throw new IOException(
0078: "Invalid fully qualified name (a): "
0079: + nameOrig);
0080: }
0081: }
0082: vec.addElement(simpleName);
0083: }
0084: return vec.elements();
0085: }
0086:
0087: // Instance Methods ------------------------------------------------------
0088: /** Ctor. */
0089: public ClassTree() {
0090: root = Pk.createRoot(this );
0091: }
0092:
0093: /** Return the root node. */
0094: public Pk getRoot() {
0095: return root;
0096: }
0097:
0098: /** Update the path of the passed filename, if that path corresponds to a package. */
0099: public String getOutName(String inName) {
0100: try {
0101: TreeItem ti = root;
0102: StringBuffer sb = new StringBuffer();
0103: for (Enumeration nameEnum = getNameEnum(inName); nameEnum
0104: .hasMoreElements();) {
0105: SimpleName simpleName = (SimpleName) nameEnum
0106: .nextElement();
0107: String name = simpleName.getName();
0108: if (simpleName.isAsPackage()) {
0109: if (ti != null) {
0110: ti = ((Pk) ti).getPackage(name);
0111: if (ti != null) {
0112: String repackageName = ((Pk) ti)
0113: .getRepackageName();
0114: if (repackageName != null) {
0115: sb = new StringBuffer(repackageName);
0116: } else {
0117: sb.append(ti.getOutName());
0118: }
0119: } else {
0120: sb.append(name);
0121: }
0122: } else {
0123: sb.append(name);
0124: }
0125: sb.append(PACKAGE_LEVEL);
0126: } else if (simpleName.isAsClass()) {
0127: sb.append(name);
0128: return sb.toString();
0129: } else {
0130: throw new Exception(
0131: "Internal error: illegal package/class name tag");
0132: }
0133: }
0134: } catch (Exception e) {
0135: // Just drop through and return the original name
0136: }
0137: return inName;
0138: }
0139:
0140: /** Add a classfile's package, class, method and field entries to database. */
0141: public void addClassFile(ClassFile cf) throws Exception {
0142: addClassFile(cf, false);
0143: }
0144:
0145: /** Add a classfile's package, class, method and field entries to database. */
0146: public void addClassFile(ClassFile cf, boolean enableTrim)
0147: throws Exception {
0148: // Add the fully qualified class name
0149: TreeItem ti = root;
0150: for (Enumeration nameEnum = getNameEnum(cf.getName()); nameEnum
0151: .hasMoreElements();) {
0152: SimpleName simpleName = (SimpleName) nameEnum.nextElement();
0153: String name = simpleName.getName();
0154: if (simpleName.isAsPackage()) {
0155: ti = ((Pk) ti).addPackage(name);
0156: } else if (simpleName.isAsClass()) {
0157: // If this is an inner class, just add placeholder classes up the tree
0158: if (nameEnum.hasMoreElements()) {
0159: ti = ((PkCl) ti).addPlaceholderClass(name);
0160: } else {
0161: ti = ((PkCl) ti).addClass(name, cf.getSuper(), cf
0162: .getInterfaces(), cf.getModifiers());
0163: }
0164: } else {
0165: throw new Exception(
0166: "Internal error: illegal package/class name tag");
0167: }
0168: }
0169:
0170: // We must have a class before adding methods and fields
0171: if (ti instanceof Cl) {
0172: Cl cl = (Cl) ti;
0173:
0174: // Add the class's methods to the database
0175: for (int i = 0; i < cf.getMethodCount(); i++) {
0176: Md md = cl.addMethod(cf, cf.getMethod(i), enableTrim);
0177: }
0178:
0179: // Add the class's fields to the database
0180: for (int i = 0; i < cf.getFieldCount(); i++) {
0181: Fd fd = cl.addField(cf, cf.getField(i), enableTrim);
0182: }
0183:
0184: // Construct class's reference list
0185: cl.findRefs(cf);
0186:
0187: // Add warnings about class
0188: cl.setWarnings(cf);
0189: } else {
0190: throw new Exception("Inconsistent class file.");
0191: }
0192: }
0193:
0194: /** Mark a class/interface type to suppress warnings from it. */
0195: public void noWarnClass(String name) throws Exception {
0196: // Mark the class (or classes, if this is a wildcarded specifier)
0197: for (Enumeration clEnum = getClEnum(name); clEnum
0198: .hasMoreElements();) {
0199: Cl cl = (Cl) clEnum.nextElement();
0200: cl.setNoWarn();
0201: }
0202: }
0203:
0204: /** Write any non-suppressed warnings to the log. */
0205: public void logWarnings(PrintWriter log) throws Exception {
0206: final Vector hasWarnings = new Vector();
0207: walkTree(new TreeAction() {
0208: public void classAction(Cl cl) throws Exception {
0209: if (cl.hasWarnings())
0210: hasWarnings.addElement(Boolean.valueOf(true));
0211: }
0212: });
0213: if (hasWarnings.size() > 0) {
0214: log.println("#");
0215: log.println(LOG_DANGER_HEADER1);
0216: log.println(LOG_DANGER_HEADER2);
0217: log.println(LOG_DANGER_HEADER3);
0218: final PrintWriter flog = log;
0219: walkTree(new TreeAction() {
0220: public void classAction(Cl cl) throws Exception {
0221: cl.logWarnings(flog);
0222: }
0223: });
0224: }
0225: }
0226:
0227: /** Mark an attribute type for retention. */
0228: public void retainAttribute(String name) throws Exception {
0229: retainAttrs.addElement(name);
0230: }
0231:
0232: /** Mark a class/interface type (and possibly methods and fields defined in class) for retention. */
0233: public void retainClass(String name, boolean retainToPublic,
0234: boolean retainToProtected, boolean retainPubProtOnly,
0235: boolean retainFieldsOnly, boolean retainMethodsOnly,
0236: String extendsName, boolean invert, boolean notrimOnly, // TODO - use notrimOnly
0237: int accessMask, int accessSetting) throws Exception {
0238: // Mark the class (or classes, if this is a wildcarded specifier)
0239: for (Enumeration clEnum = getClEnum(name); clEnum
0240: .hasMoreElements();) {
0241: Cl cl = (Cl) clEnum.nextElement();
0242: if ((extendsName == null || cl
0243: .hasAsSuperOrInterface(extendsName))
0244: && cl.modifiersMatchMask(accessMask, accessSetting)) {
0245: retainHierarchy(cl, invert);
0246: if (retainToPublic || retainToProtected
0247: || retainPubProtOnly) {
0248: // Retain methods if requested
0249: if (!retainFieldsOnly) {
0250: for (Enumeration enm = cl.getMethodEnum(); enm
0251: .hasMoreElements();) {
0252: Md md = (Md) enm.nextElement();
0253: if ((retainToPublic && Modifier.isPublic(md
0254: .getModifiers()))
0255: || (retainToProtected && !Modifier
0256: .isPrivate(md
0257: .getModifiers()))
0258: || (retainPubProtOnly && (Modifier
0259: .isPublic(md.getModifiers()) || Modifier
0260: .isProtected(md
0261: .getModifiers())))) {
0262: if (invert) {
0263: md.setOutName(null);
0264: md.clearFromScript();
0265: } else {
0266: md.setOutName(md.getInName());
0267: md.setFromScript();
0268: }
0269: }
0270: }
0271: }
0272:
0273: // Retain fields if requested
0274: if (!retainMethodsOnly) {
0275: for (Enumeration enm = cl.getFieldEnum(); enm
0276: .hasMoreElements();) {
0277: Fd fd = (Fd) enm.nextElement();
0278: if ((retainToPublic && Modifier.isPublic(fd
0279: .getModifiers()))
0280: || (retainToProtected && !Modifier
0281: .isPrivate(fd
0282: .getModifiers()))
0283: || (retainPubProtOnly && (Modifier
0284: .isPublic(fd.getModifiers()) || Modifier
0285: .isProtected(fd
0286: .getModifiers())))) {
0287: if (invert) {
0288: fd.setOutName(null);
0289: fd.clearFromScript();
0290: } else {
0291: fd.setOutName(fd.getInName());
0292: fd.setFromScript();
0293: }
0294: }
0295: }
0296: }
0297: }
0298: }
0299: }
0300: }
0301:
0302: /** Mark a method type for retention. */
0303: public void retainMethod(String name, String descriptor,
0304: boolean retainAndClass, String extendsName, boolean invert,
0305: boolean notrimOnly, // TODO - use notrimOnly
0306: int accessMask, int accessSetting) throws Exception {
0307: for (Enumeration enm = getMdEnum(name, descriptor); enm
0308: .hasMoreElements();) {
0309: Md md = (Md) enm.nextElement();
0310: Cl this Cl = (Cl) md.getParent();
0311: if ((extendsName == null || this Cl
0312: .hasAsSuperOrInterface(extendsName))
0313: && md.modifiersMatchMask(accessMask, accessSetting)) {
0314: if (invert) {
0315: md.setOutName(null);
0316: md.clearFromScript();
0317: } else {
0318: md.setOutName(md.getInName());
0319: md.setFromScript();
0320: }
0321: if (retainAndClass) {
0322: retainHierarchy(this Cl, invert);
0323: }
0324: }
0325: }
0326: }
0327:
0328: /** Mark a field type for retention. */
0329: public void retainField(String name, String descriptor,
0330: boolean retainAndClass, String extendsName, boolean invert,
0331: boolean notrimOnly, // TODO - use notrimOnly
0332: int accessMask, int accessSetting) throws Exception {
0333: for (Enumeration enm = getFdEnum(name, descriptor); enm
0334: .hasMoreElements();) {
0335: Fd fd = (Fd) enm.nextElement();
0336: Cl this Cl = (Cl) fd.getParent();
0337: if ((extendsName == null || this Cl
0338: .hasAsSuperOrInterface(extendsName))
0339: && fd.modifiersMatchMask(accessMask, accessSetting)) {
0340: if (invert) {
0341: fd.setOutName(null);
0342: fd.clearFromScript();
0343: } else {
0344: fd.setOutName(fd.getInName());
0345: fd.setFromScript();
0346: }
0347: if (retainAndClass) {
0348: retainHierarchy(this Cl, invert);
0349: }
0350: }
0351: }
0352: }
0353:
0354: /** Mark a package for retention, and specify its new name. */
0355: public void retainPackageMap(String name, String obfName)
0356: throws Exception {
0357: retainItemMap(getPk(name), obfName);
0358: }
0359:
0360: /** Mark a package for repackaging under this new name. */
0361: public void retainRepackageMap(String name, String obfName)
0362: throws Exception {
0363: Pk pk = getPk(name);
0364: if (!pk.isFixed()) {
0365: pk.setRepackageName(obfName);
0366: pk.setOutName(pk.getInName());
0367: }
0368: }
0369:
0370: /** Mark a class/interface type for retention, and specify its new name. */
0371: public void retainClassMap(String name, String obfName)
0372: throws Exception {
0373: retainItemMap(getCl(name), obfName);
0374: }
0375:
0376: /** Mark a method type for retention, and specify its new name. */
0377: public void retainMethodMap(String name, String descriptor,
0378: String obfName) throws Exception {
0379: retainItemMap(getMd(name, descriptor), obfName);
0380: }
0381:
0382: /** Mark a field type for retention, and specify its new name. */
0383: public void retainFieldMap(String name, String obfName)
0384: throws Exception {
0385: retainItemMap(getFd(name), obfName);
0386: }
0387:
0388: // Mark an item for retention, and specify its new name.
0389: private void retainItemMap(TreeItem item, String obfName)
0390: throws Exception {
0391: if (!item.isFixed()) {
0392: item.setOutName(obfName);
0393: item.setFromScriptMap();
0394: }
0395: }
0396:
0397: /** Traverse the class tree, generating obfuscated names within each namespace. */
0398: public void generateNames(boolean enableRepackage) throws Exception {
0399: // Repackage first, if requested
0400: // (need TreeItem.isFixed set properly, so must be done first)
0401: if (enableRepackage) {
0402: // Exclude package names already fixed at the root level
0403: String[] noObfNames = getRootPackageNames();
0404: final NameMaker nm = new KeywordNameMaker(noObfNames,
0405: false, true);
0406: // Generate single-level package names, unique across jar
0407: walkTree(new TreeAction() {
0408: public void packageAction(Pk pk) throws Exception {
0409: pk.repackageName(nm);
0410: }
0411: });
0412: }
0413: // Now rename everything in the traditional way (no repackaging)
0414: walkTree(new TreeAction() {
0415: public void packageAction(Pk pk) throws Exception {
0416: pk.generateNames();
0417: }
0418:
0419: public void classAction(Cl cl) throws Exception {
0420: cl.generateNames();
0421: }
0422: });
0423: }
0424:
0425: // Return list of currently fixed root package names
0426: private String[] getRootPackageNames() throws Exception {
0427: Vector vec = new Vector();
0428: for (Enumeration enm = root.getPackageEnum(); enm
0429: .hasMoreElements();) {
0430: Pk pk = (Pk) enm.nextElement();
0431: if (pk.isFixed()) {
0432: vec.addElement(pk.getOutName());
0433: }
0434: }
0435: String[] noObfNames = new String[vec.size()];
0436: for (int i = 0; i < noObfNames.length; i++) {
0437: noObfNames[i] = (String) vec.elementAt(i);
0438: }
0439: return noObfNames;
0440: }
0441:
0442: /** Resolve the polymorphic dependencies of each class. */
0443: public void resolveClasses() throws Exception {
0444: walkTree(new TreeAction() {
0445: public void classAction(Cl cl) throws Exception {
0446: cl.resetResolve();
0447: }
0448: });
0449: walkTree(new TreeAction() {
0450: public void classAction(Cl cl) throws Exception {
0451: cl.setupNameListDowns();
0452: }
0453: });
0454: Cl.nameSpace = 0;
0455: walkTree(new TreeAction() {
0456: public void classAction(Cl cl) throws Exception {
0457: cl.resolveOptimally();
0458: }
0459: });
0460: }
0461:
0462: /** Return a list of attributes marked to keep. */
0463: public String[] getAttrsToKeep() throws Exception {
0464: String[] attrs = new String[retainAttrs.size()];
0465: for (int i = 0; i < attrs.length; i++) {
0466: attrs[i] = (String) retainAttrs.elementAt(i);
0467: }
0468: return attrs;
0469: }
0470:
0471: /** Get classes in tree from the fully qualified name
0472: (can be wildcarded). */
0473: public Enumeration getClEnum(String fullName) throws Exception {
0474: final Vector vec = new Vector();
0475: // Wildcard? then return list of all matching classes (including inner)
0476: if (fullName.indexOf('*') != -1) {
0477: // Old !a/b/* wildcard syntax, for backward compatibility
0478: // (acts as if every * becomes a ** in new-style match)
0479: if (fullName.indexOf('!') == 0) {
0480: final String fName = fullName.substring(1);
0481: walkTree(new TreeAction() {
0482: public void classAction(Cl cl) throws Exception {
0483: if (cl.isOldStyleMatch(fName)) {
0484: vec.addElement(cl);
0485: }
0486: }
0487: });
0488: }
0489: // New a/b/** wildcard syntax
0490: else {
0491: final String fName = fullName;
0492: walkTree(new TreeAction() {
0493: public void classAction(Cl cl) throws Exception {
0494: if (cl.isWildcardMatch(fName)) {
0495: vec.addElement(cl);
0496: }
0497: }
0498: });
0499: }
0500: } else {
0501: // Single class
0502: Cl cl = getCl(fullName);
0503: if (cl != null) {
0504: vec.addElement(cl);
0505: }
0506: }
0507: return vec.elements();
0508: }
0509:
0510: /** Get methods in tree from the fully qualified, and possibly
0511: wildcarded, name. */
0512: public Enumeration getMdEnum(String fullName, String descriptor)
0513: throws Exception {
0514: final Vector vec = new Vector();
0515: final String fDesc = descriptor;
0516: // Wildcard? then return list of all matching methods
0517: if (fullName.indexOf('*') != -1
0518: || descriptor.indexOf('*') != -1) {
0519: // Old !a/b/* wildcard syntax, for backward compatibility
0520: // (acts as if every * becomes a ** in new-style match)
0521: if (fullName.indexOf('!') == 0) {
0522: final String fName = fullName.substring(1);
0523: walkTree(new TreeAction() {
0524: public void methodAction(Md md) throws Exception {
0525: if (md.isOldStyleMatch(fName, fDesc)) {
0526: vec.addElement(md);
0527: }
0528: }
0529: });
0530: }
0531: // New a/b/** wildcard syntax
0532: else {
0533: final String fName = fullName;
0534: walkTree(new TreeAction() {
0535: public void methodAction(Md md) throws Exception {
0536: if (md.isWildcardMatch(fName, fDesc)) {
0537: vec.addElement(md);
0538: }
0539: }
0540: });
0541: }
0542: } else {
0543: Md md = getMd(fullName, descriptor);
0544: if (md != null) {
0545: vec.addElement(md);
0546: }
0547: }
0548: return vec.elements();
0549: }
0550:
0551: /** Get fields in tree from the fully qualified, and possibly
0552: wildcarded, name. */
0553: public Enumeration getFdEnum(String fullName, String descriptor)
0554: throws Exception {
0555: final Vector vec = new Vector();
0556: // Wildcard? then return list of all matching methods
0557: if (fullName.indexOf('*') != -1) {
0558: // Old !a/b/* wildcard syntax, for backward compatibility
0559: // (acts as if every * becomes a ** in new-style match)
0560: if (fullName.indexOf('!') == 0) {
0561: final String fName = fullName.substring(1);
0562: walkTree(new TreeAction() {
0563: public void fieldAction(Fd fd) throws Exception {
0564: if (fd.isOldStyleMatch(fName)) {
0565: vec.addElement(fd);
0566: }
0567: }
0568: });
0569: }
0570: // New a/b/** wildcard syntax
0571: else {
0572: final String fName = fullName;
0573: final String fDesc = descriptor;
0574: walkTree(new TreeAction() {
0575: public void fieldAction(Fd fd) throws Exception {
0576: if (fd.isWildcardMatch(fName, fDesc)) {
0577: vec.addElement(fd);
0578: }
0579: }
0580: });
0581: }
0582: } else {
0583: Fd fd = getFd(fullName);
0584: if (fd != null) {
0585: vec.addElement(fd);
0586: }
0587: }
0588: return vec.elements();
0589: }
0590:
0591: /** Get class in tree from the fully qualified name, returning null if name not found. */
0592: public Cl getCl(String fullName) throws Exception {
0593: TreeItem ti = root;
0594: for (Enumeration nameEnum = getNameEnum(fullName); nameEnum
0595: .hasMoreElements();) {
0596: SimpleName simpleName = (SimpleName) nameEnum.nextElement();
0597: String name = simpleName.getName();
0598: if (simpleName.isAsPackage()) {
0599: ti = ((Pk) ti).getPackage(name);
0600: } else if (simpleName.isAsClass()) {
0601: ti = ((PkCl) ti).getClass(name);
0602: } else {
0603: throw new Exception(
0604: "Internal error: illegal package/class name tag");
0605: }
0606:
0607: // If the name is not in the database, return null
0608: if (ti == null) {
0609: return null;
0610: }
0611: }
0612:
0613: // It is an error if we do not end up with a class or interface
0614: if (!(ti instanceof Cl)) {
0615: // 15Jul2005 - this exception is being over-sensitive with fullName of null (should never get here) so safely return null instead
0616: //throw new Exception("Inconsistent class or interface name: " + fullName);
0617: return null;
0618: }
0619: return (Cl) ti;
0620: }
0621:
0622: /** Get package in tree from the fully qualified name, returning null if name not found. */
0623: public Pk getPk(String fullName) throws Exception {
0624: TreeItem ti = root;
0625: for (Enumeration nameEnum = getNameEnum(fullName); nameEnum
0626: .hasMoreElements();) {
0627: SimpleName simpleName = (SimpleName) nameEnum.nextElement();
0628: String name = simpleName.getName();
0629: ti = ((Pk) ti).getPackage(name);
0630:
0631: // If the name is not in the database, return null
0632: if (ti == null) {
0633: return null;
0634: }
0635: // It is an error if we do not end up with a package
0636: if (!(ti instanceof Pk)) {
0637: throw new Exception("Inconsistent package.");
0638: }
0639: }
0640: return (Pk) ti;
0641: }
0642:
0643: /** Get method in tree from the fully qualified name. */
0644: public Md getMd(String fullName, String descriptor)
0645: throws Exception {
0646: // Split into class and method names
0647: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0648: Cl cl = getCl(fullName.substring(0, pos));
0649: return cl.getMethod(fullName.substring(pos + 1), descriptor);
0650: }
0651:
0652: /** Get field in tree from the fully qualified name. */
0653: public Fd getFd(String fullName) throws Exception {
0654: // Split into class and field names
0655: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0656: Cl cl = getCl(fullName.substring(0, pos));
0657: return cl.getField(fullName.substring(pos + 1));
0658: }
0659:
0660: /** Mapping for fully qualified class name.
0661: * @see NameMapper#mapClass */
0662: public String mapClass(String className) throws Exception {
0663: // Check for array -- requires special handling
0664: if (className.length() > 0 && className.charAt(0) == '[') {
0665: StringBuffer newName = new StringBuffer();
0666: int i = 0;
0667: while (i < className.length()) {
0668: char ch = className.charAt(i++);
0669: switch (ch) {
0670: case '[':
0671: case ';':
0672: newName.append(ch);
0673: break;
0674:
0675: case 'L':
0676: newName.append(ch);
0677: int pos = className.indexOf(';', i);
0678: if (pos < 0) {
0679: throw new Exception(
0680: "Invalid class name encountered: "
0681: + className);
0682: }
0683: newName
0684: .append(mapClass(className
0685: .substring(i, pos)));
0686: i = pos;
0687: break;
0688:
0689: default:
0690: return className;
0691: }
0692: }
0693: return newName.toString();
0694: } else {
0695: Cl cl = getCl(className);
0696: return cl != null ? cl.getFullOutName() : className;
0697: }
0698: }
0699:
0700: /** Mapping for method name, of fully qualified class.
0701: * @see NameMapper#mapMethod */
0702: public String mapMethod(String className, String methodName,
0703: String descriptor) throws Exception {
0704: String outName = methodName;
0705: if (!methodName.equals("<init>")) {
0706: Stack s = new Stack();
0707: Cl nextCl = getCl(className);
0708: if (nextCl != null) {
0709: s.push(nextCl);
0710: }
0711: while (!s.empty()) {
0712: Cl cl = (Cl) s.pop();
0713: Md md = cl.getMethod(methodName, descriptor);
0714: if (md != null) {
0715: outName = md.getOutName();
0716: break;
0717: } else {
0718: nextCl = cl.getSuperCl();
0719: if (nextCl != null) {
0720: s.push(nextCl);
0721: }
0722: Enumeration enm = cl.getSuperInterfaces();
0723: while (enm.hasMoreElements()) {
0724: nextCl = (Cl) enm.nextElement();
0725: if (nextCl != null) {
0726: s.push(nextCl);
0727: }
0728: }
0729: }
0730: }
0731: }
0732: return outName;
0733: }
0734:
0735: /** Mapping for field name, of fully qualified class.
0736: * @see NameMapper#mapField */
0737: public String mapField(String className, String fieldName)
0738: throws Exception {
0739: String outName = fieldName;
0740: if (!fieldName.equals("<init>")) {
0741: Stack s = new Stack();
0742: Cl nextCl = getCl(className);
0743: if (nextCl != null) {
0744: s.push(nextCl);
0745: }
0746: while (!s.empty()) {
0747: Cl cl = (Cl) s.pop();
0748: Fd fd = cl.getField(fieldName);
0749: if (fd != null) {
0750: outName = fd.getOutName();
0751: break;
0752: } else {
0753: nextCl = cl.getSuperCl();
0754: if (nextCl != null) {
0755: s.push(nextCl);
0756: }
0757: Enumeration enm = cl.getSuperInterfaces();
0758: while (enm.hasMoreElements()) {
0759: nextCl = (Cl) enm.nextElement();
0760: if (nextCl != null) {
0761: s.push(nextCl);
0762: }
0763: }
0764: }
0765: }
0766: }
0767: return outName;
0768: }
0769:
0770: /** Mapping for generic type signature.
0771: * @see NameMapper#mapSignature */
0772: public String mapSignature(String signature) throws Exception {
0773: // NOTE - not currently parsed and mapped; reserve identifiers
0774: // appearing in type signatures for reflective methods to work.
0775: return signature;
0776: }
0777:
0778: /** Mapping for descriptor of field or method.
0779: * @see NameMapper#mapDescriptor */
0780: public String mapDescriptor(String descriptor) throws Exception {
0781: // Pass everything through unchanged, except for the String between
0782: // 'L' and ';' -- this is passed through mapClass(String)
0783: StringBuffer newDesc = new StringBuffer();
0784: int i = 0;
0785: while (i < descriptor.length()) {
0786: char ch = descriptor.charAt(i++);
0787: switch (ch) {
0788: case '[':
0789: case 'B':
0790: case 'C':
0791: case 'D':
0792: case 'F':
0793: case 'I':
0794: case 'J':
0795: case 'S':
0796: case 'Z':
0797: case 'V':
0798: case '(':
0799: case ')':
0800: case ';':
0801: newDesc.append(ch);
0802: break;
0803:
0804: case 'L':
0805: newDesc.append(ch);
0806: int pos = descriptor.indexOf(';', i);
0807: if (pos < 0) {
0808: throw new Exception(
0809: "Invalid descriptor string encountered.");
0810: }
0811: newDesc.append(mapClass(descriptor.substring(i, pos)));
0812: i = pos;
0813: break;
0814:
0815: default:
0816: throw new Exception(
0817: "Invalid descriptor string encountered.");
0818: }
0819: }
0820: return newDesc.toString();
0821: }
0822:
0823: /** Dump the content of the class tree to the specified file (used for logging). */
0824: public void dump(final PrintWriter log) throws Exception {
0825: log.println("#");
0826: log.println(LOG_FREQUENCY_TABLE);
0827: dumpFrequencyCount(log);
0828: log.println("#");
0829: log.println(LOG_PRE_UNOBFUSCATED);
0830: log.println("#");
0831: walkTree(new TreeAction() {
0832: public void classAction(Cl cl) {
0833: if (cl.isFromScript()) {
0834: log.println(".class " + cl.getFullInName());
0835: if (cl.isTrimmed()) {
0836: log
0837: .println("# ERROR: incorrectly trimmed class "
0838: + cl.getFullInName());
0839: }
0840: }
0841: }
0842:
0843: public void methodAction(Md md) {
0844: if (md.isFromScript()) {
0845: log.println(".method " + md.getFullInName() + " "
0846: + md.getDescriptor());
0847: if (md.isTrimmed()) {
0848: log
0849: .println("# ERROR: incorrectly trimmed method "
0850: + md.getFullInName()
0851: + " "
0852: + md.getDescriptor());
0853: }
0854: }
0855: }
0856:
0857: public void fieldAction(Fd fd) {
0858: if (fd.isFromScript()) {
0859: log.println(".field " + fd.getFullInName() + " "
0860: + fd.getDescriptor());
0861: if (fd.isTrimmed()) {
0862: log
0863: .println("# ERROR: incorrectly trimmed field "
0864: + fd.getFullInName()
0865: + " "
0866: + fd.getDescriptor());
0867: }
0868: }
0869: }
0870:
0871: public void packageAction(Pk pk) {
0872: // No action
0873: }
0874: });
0875: log.println("#");
0876: log.println("#");
0877: log.println(LOG_PRE_OBFUSCATED);
0878: log.println("#");
0879: walkTree(new TreeAction() {
0880: public void classAction(Cl cl) {
0881: if (!cl.isFromScript()) {
0882: if (cl.isTrimmed()) {
0883: log.println("# trimmed class "
0884: + cl.getFullInName());
0885: } else {
0886: log.println(".class_map " + cl.getFullInName()
0887: + " " + cl.getOutName());
0888: }
0889: }
0890: }
0891:
0892: public void methodAction(Md md) {
0893: if (!md.isFromScript()) {
0894: if (!md.getParent().isTrimmed()) {
0895: if (md.isTrimmed()) {
0896: log.println("# trimmed method "
0897: + md.getFullInName() + " "
0898: + md.getDescriptor());
0899: } else {
0900: log.println(".method_map "
0901: + md.getFullInName() + " "
0902: + md.getDescriptor() + " "
0903: + md.getOutName());
0904: }
0905: }
0906: }
0907: }
0908:
0909: public void fieldAction(Fd fd) {
0910: if (!fd.isFromScript()) {
0911: if (!fd.getParent().isTrimmed()) {
0912: if (fd.isTrimmed()) {
0913: log.println("# trimmed field "
0914: + fd.getFullInName() + " "
0915: + fd.getDescriptor());
0916: } else {
0917: log.println(".field_map "
0918: + fd.getFullInName() + " "
0919: + fd.getOutName());
0920: }
0921: }
0922: }
0923: }
0924:
0925: public void packageAction(Pk pk) {
0926: if (!pk.isFromScript()
0927: && pk.getFullInName().length() > 0) {
0928: if (pk.getRepackageName() != null) {
0929: log.println(".repackage_map "
0930: + pk.getFullInName() + " "
0931: + pk.getRepackageName());
0932: } else {
0933: log.println(".package_map "
0934: + pk.getFullInName() + " "
0935: + pk.getOutName());
0936: }
0937: }
0938: }
0939: });
0940: }
0941:
0942: private void dumpFrequencyCount(PrintWriter log) {
0943: // Compute total use count
0944: int totalUseCount = 0;
0945: for (Enumeration useCountEnum = NameMaker.getUseCounts(); useCountEnum
0946: .hasMoreElements();) {
0947: totalUseCount += ((Integer) useCountEnum.nextElement())
0948: .intValue() + 1;
0949: }
0950:
0951: // Log the individual use counts for names
0952: if (totalUseCount != 0) {
0953: int sumPercent = 0;
0954: int sumUseCount = 0;
0955: Vector sort = new Vector();
0956: for (Enumeration nameEnum = NameMaker.getNames(), useCountEnum = NameMaker
0957: .getUseCounts(); nameEnum.hasMoreElements();) {
0958: String name = (String) nameEnum.nextElement();
0959: int useCount = ((Integer) useCountEnum.nextElement())
0960: .intValue() + 1;
0961: int percent = 100 * useCount / totalUseCount;
0962: if (percent != 0) {
0963: sort.addElement(new SortElement(name, useCount));
0964: sumUseCount += useCount;
0965: sumPercent += percent;
0966: }
0967: }
0968: while (sort.size() != 0) {
0969: SortElement se = null;
0970: int largestUseCount = 0;
0971: for (Enumeration enm = sort.elements(); enm
0972: .hasMoreElements();) {
0973: SortElement this Se = (SortElement) enm
0974: .nextElement();
0975: if (this Se.useCount >= largestUseCount) {
0976: se = this Se;
0977: largestUseCount = se.useCount;
0978: }
0979: }
0980: sort.removeElement(se);
0981: log.println("# '"
0982: + se.name
0983: + "' \tused "
0984: + se.useCount
0985: + " times\t("
0986: + Integer.toString(100 * se.useCount
0987: / totalUseCount) + "%)");
0988: }
0989:
0990: // Log the remainder percentage
0991: log
0992: .println("# Other names (each used in <1% of mappings) used a total of "
0993: + Integer.toString(totalUseCount
0994: - sumUseCount)
0995: + " times ("
0996: + Integer.toString(100 - sumPercent) + "%)");
0997: log.println("#");
0998: }
0999: }
1000:
1001: class SortElement {
1002: int useCount;
1003: String name;
1004:
1005: SortElement(String name, int useCount) {
1006: this .useCount = useCount;
1007: this .name = name;
1008: }
1009: }
1010:
1011: // Private Methods -------------------------------------------------------
1012: // Mark TreeItem and all parents for retention.
1013: private void retainHierarchy(TreeItem ti, boolean invert)
1014: throws Exception {
1015: if (invert) {
1016: // error to force package level obfuscation
1017: if (!(ti instanceof Pk)) {
1018: ti.setOutName(null);
1019: ti.clearFromScript();
1020: }
1021: } else {
1022: if (!ti.isFixed()) {
1023: ti.setOutName(ti.getInName());
1024: ti.setFromScript();
1025: }
1026: }
1027: if (ti.parent != null) {
1028: retainHierarchy(ti.parent, invert);
1029: }
1030: }
1031:
1032: /** Walk the whole tree taking action once only on each package level, class, method and field. */
1033: public void walkTree(TreeAction ta) throws Exception {
1034: walkTree(ta, root);
1035: }
1036:
1037: // Walk the tree which has TreeItem as its root taking action once only on each
1038: // package level, class, method and field.
1039: private void walkTree(TreeAction ta, TreeItem ti) throws Exception {
1040: if (ti instanceof Pk) {
1041: Enumeration packageEnum = ((Pk) ti).getPackageEnum();
1042: ta.packageAction((Pk) ti);
1043: while (packageEnum.hasMoreElements()) {
1044: walkTree(ta, (TreeItem) packageEnum.nextElement());
1045: }
1046: }
1047: if (ti instanceof PkCl) {
1048: Enumeration classEnum = ((PkCl) ti).getClassEnum();
1049: while (classEnum.hasMoreElements()) {
1050: walkTree(ta, (TreeItem) classEnum.nextElement());
1051: }
1052: }
1053: if (ti instanceof Cl) {
1054: Enumeration fieldEnum = ((Cl) ti).getFieldEnum();
1055: Enumeration methodEnum = ((Cl) ti).getMethodEnum();
1056: ta.classAction((Cl) ti);
1057: while (fieldEnum.hasMoreElements()) {
1058: ta.fieldAction((Fd) fieldEnum.nextElement());
1059: }
1060: while (methodEnum.hasMoreElements()) {
1061: ta.methodAction((Md) methodEnum.nextElement());
1062: }
1063: }
1064: }
1065: }
|