001: /* ===========================================================================
002: * $RCSfile: KeywordNameMaker.java,v $
003: * ===========================================================================
004: *
005: * RetroGuard -- an obfuscation package for Java classfiles.
006: *
007: * Copyright (c) 1998-2006 Mark Welsh (markw@retrologic.com)
008: *
009: * This program can be redistributed and/or modified under the terms of the
010: * Version 2 of the GNU General Public License as published by the Free
011: * Software Foundation.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: */
019:
020: package COM.rl.obf;
021:
022: import java.io.*;
023: import java.util.*;
024: import COM.rl.util.*;
025:
026: /**
027: * Name generator that uses (almost) the full Java identifier namespace,
028: * and chooses to put some of the keyword names (legal in JVM, illegal in
029: * Java language) out front in sequence.
030: *
031: * @author Mark Welsh
032: */
033: public class KeywordNameMaker extends NameMaker {
034: // Constants -------------------------------------------------------------
035: private static final String DUMMY_ARG_LIST = "dummy";
036:
037: // Fields ----------------------------------------------------------------
038: private int skipped = 0; // Names skipped in the sequence
039: private Vector namesToDate = new Vector();
040: private Hashtable argCount = new Hashtable();
041: private String[] noObfNames = null; // List of names not to be obfuscated
042: private String[] keywordsToUse;
043: private String[] keywordsToExclude;
044: private String[] firstLetter;
045: private String[] nextLetter;
046: private String[] noKeywords = {};
047: private String[] someKeywords = { "a", "if", "do", "for", "int",
048: "new", "try", "byte", "case", "char", "else", "goto",
049: "long",
050: // "null", -- removed due to reported bug in JDK1.4 VM
051: "void" };
052: private String[] excSomeKeywords = { "a", "if", "do", "for", "int",
053: "new", "try", "byte", "case", "char", "else", "goto",
054: "long", "null", // -- removed due to reported bug in JDK1.4 VM
055: "void" };
056: private String[] allKeywords = { "if", "do", "for", "int", "new",
057: "try", "byte", "case", "char", "else", "goto", "long",
058: "null", "this", "void", "true", "false", "break", "catch",
059: "class", "const", "float", "final", "short", "super",
060: "throw", "while", "double", "import", "native", "public",
061: "return", "static", "switch", "throws", "boolean",
062: "default", "extends", "finally", "package", "private",
063: "abstract", "continue", "volatile", "interface",
064: "protected", "transient", "implements", "instanceof",
065: "synchronized" };
066: private String[] firstLetterLower = { "a", "b", "c", "d", "e", "f",
067: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
068: "s", "t", "u", "v", "w", "x", "y", "z" };
069: private String[] nextLetterLower = { "a", "b", "c", "d", "e", "f",
070: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
071: "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3",
072: "4", "5", "6", "7", "8", "9" };
073: private String[] firstLetterAll = { "a", "b", "c", "d", "e", "f",
074: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
075: "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
076: "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
077: "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
078: private String[] nextLetterAll = { "a", "b", "c", "d", "e", "f",
079: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
080: "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
081: "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
082: "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1",
083: "2", "3", "4", "5", "6", "7", "8", "9" };
084:
085: // Class Methods ---------------------------------------------------------
086: /** Main method for testing. */
087: /*
088: public static void main(String[] args) throws Exception
089: {
090: PrintWriter pw = new PrintWriter(
091: new BufferedOutputStream(
092: new FileOutputStream("keywordnamemaker.tst")));
093: try
094: {
095: NameMaker nmk = new KeywordNameMaker();
096: for (int i = 0; i < 1000000; i++)
097: {
098: pw.println(nmk.nextName(null));
099: }
100: }
101: finally
102: {
103: pw.close();
104: }
105: }
106: */
107:
108: // Instance Methods ------------------------------------------------------
109: /** Ctor. */
110: public KeywordNameMaker() {
111: this (null);
112: }
113:
114: /** Ctor - block names not to be obfuscated from the mapping target space. */
115: public KeywordNameMaker(String[] noObfNames) {
116: this (noObfNames, true);
117: }
118:
119: /** Ctor - block names not to be obfuscated from the mapping target space. */
120: public KeywordNameMaker(String[] noObfNames, boolean useKeywords) {
121: this (noObfNames, true, false);
122: }
123:
124: /** Ctor - block names not to be obfuscated from the mapping target space. */
125: public KeywordNameMaker(String[] noObfNames, boolean useKeywords,
126: boolean lowerCaseOnly) {
127: this .noObfNames = noObfNames == null ? new String[0]
128: : noObfNames;
129: if (useKeywords) {
130: keywordsToUse = someKeywords;
131: keywordsToExclude = excSomeKeywords;
132: } else {
133: keywordsToUse = noKeywords;
134: keywordsToExclude = allKeywords;
135: }
136: if (lowerCaseOnly) {
137: firstLetter = firstLetterLower;
138: nextLetter = nextLetterLower;
139: } else {
140: firstLetter = firstLetterAll;
141: nextLetter = nextLetterAll;
142: }
143: }
144:
145: /** Return the next unique name for this namespace. */
146: protected String getNextName(String descriptor) throws Exception {
147: // Check for arg-list in hashtable
148: String argList = DUMMY_ARG_LIST;
149: if (descriptor != null) {
150: argList = getArgList(descriptor);
151: }
152: Integer intCount = (Integer) argCount.get(argList);
153: int theCount = 0;
154: if (intCount == null) {
155: argCount.put(argList, new Integer(theCount));
156: } else {
157: theCount = intCount.intValue() + 1;
158: argCount.remove(argList);
159: argCount.put(argList, new Integer(theCount));
160: }
161: return getName(theCount);
162: }
163:
164: // Extract the arg-list from a descriptor
165: private String getArgList(String descriptor) throws Exception {
166: int pos = descriptor.indexOf(')');
167: return descriptor.substring(1, pos);
168: }
169:
170: // Generate i'th allowed, unique name
171: private String getName(int index) throws Exception {
172: // If we have previously computed this name, just return it
173: String name = null;
174: if (index < namesToDate.size()) {
175: name = (String) namesToDate.elementAt(index);
176: } else {
177: // Generate a new valid name for the sequence
178: for (;;) {
179: name = getNewName(index + skipped);
180: if (!Tools.isInArray(name, noObfNames)
181: && (index + skipped < keywordsToUse.length || !Tools
182: .isInArray(name, keywordsToExclude))) {
183: break;
184: }
185: skipped++;
186: }
187: namesToDate.addElement(name);
188: }
189: return name;
190: }
191:
192: // Generate j'th name in sequence (can repeat keywords)
193: private String getNewName(int index) throws Exception {
194: String name = null;
195:
196: // Check if we are in the 'keyword' part of the namespace
197: if (index < keywordsToUse.length) {
198: name = keywordsToUse[index];
199: } else {
200: // Check if we are in the single letter part of the namespace
201: index -= keywordsToUse.length;
202: if (index < firstLetter.length) {
203: name = firstLetter[index];
204: } else {
205: // We are in the >=2 letter part of namespace
206: index -= firstLetter.length;
207: int nextLetters = 1;
208: int subspaceSize = nextLetter.length;
209: while (index >= firstLetter.length * subspaceSize) {
210: index -= firstLetter.length * subspaceSize;
211: nextLetters++;
212: subspaceSize *= nextLetter.length;
213: }
214:
215: // Pull out the name
216: StringBuffer sb = new StringBuffer(firstLetter[index
217: / subspaceSize]);
218: while (subspaceSize != 1) {
219: index %= subspaceSize;
220: subspaceSize /= nextLetter.length;
221: sb.append(nextLetter[index / subspaceSize]);
222: }
223:
224: // Check for collision with keywords
225: name = sb.toString();
226: }
227: }
228: return name;
229: }
230: }
|