001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.js;
017:
018: import com.google.gwt.dev.js.ast.JsName;
019: import com.google.gwt.dev.js.ast.JsProgram;
020: import com.google.gwt.dev.js.ast.JsRootScope;
021: import com.google.gwt.dev.js.ast.JsScope;
022:
023: import java.util.Iterator;
024: import java.util.List;
025:
026: /**
027: * A namer that uses short, unrecognizable idents to minimize generated code
028: * size.
029: */
030: public class JsObfuscateNamer {
031:
032: /**
033: * A lookup table of base-64 chars we use to encode idents.
034: */
035: private static final char[] sBase64Chars = new char[] { 'a', 'b',
036: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
037: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
038: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
039: 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
040: 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
041: '$', '_' };
042:
043: /**
044: * A temp buffer big enough to hold at least 32 bits worth of base-64 chars.
045: */
046: private static final char[] sIdentBuf = new char[6];
047:
048: public static void exec(JsProgram program) {
049: new JsObfuscateNamer(program).execImpl();
050: }
051:
052: private static String makeObfuscatedIdent(int id) {
053: // Use base-32 for the first character of the identifier,
054: // so that we don't use any numbers (which are illegal at
055: // the beginning of an identifier).
056: //
057: int i = 0;
058: sIdentBuf[i++] = sBase64Chars[id & 0x1f];
059: id >>= 5;
060:
061: // Use base-64 for the rest of the identifier.
062: //
063: while (id != 0) {
064: sIdentBuf[i++] = sBase64Chars[id & 0x3f];
065: id >>= 6;
066: }
067:
068: return new String(sIdentBuf, 0, i);
069: }
070:
071: /**
072: * Communicates to a parent scope the maximum id used by any of its children.
073: */
074: private int maxChildId = 0;
075: private final JsProgram program;
076:
077: public JsObfuscateNamer(JsProgram program) {
078: this .program = program;
079: }
080:
081: private void execImpl() {
082: visit(program.getRootScope());
083: }
084:
085: private boolean isLegal(JsScope scope, String newIdent) {
086: if (JsKeywords.isKeyword(newIdent)) {
087: return false;
088: }
089: /*
090: * Never obfuscate a name into an identifier that conflicts with an existing
091: * unobfuscatable name! It's okay if it conflicts with an existing
092: * obfuscatable name, since that name will get obfuscated to something else
093: * anyway.
094: */
095: return (scope.findExistingUnobfuscatableName(newIdent) == null);
096: }
097:
098: private void visit(JsScope scope) {
099: // Save off the maxChildId which is currently being computed for my parent.
100: int mySiblingsMaxId = maxChildId;
101:
102: /*
103: * Visit my children first. Reset maxChildId so that my children will get a
104: * clean slate: I do not communicate to my children.
105: */
106: maxChildId = 0;
107: List<JsScope> children = scope.getChildren();
108: for (Iterator<JsScope> it = children.iterator(); it.hasNext();) {
109: visit(it.next());
110: }
111: // maxChildId is now the max of all of my children's ids
112:
113: JsRootScope rootScope = program.getRootScope();
114: if (scope == rootScope) {
115: return;
116: }
117:
118: // Visit my idents.
119: int curId = maxChildId;
120: for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) {
121: JsName name = it.next();
122: if (!name.isObfuscatable()) {
123: // Unobfuscatable names become themselves.
124: name.setShortIdent(name.getIdent());
125: continue;
126: }
127:
128: String newIdent;
129: while (true) {
130: // Get the next possible obfuscated name
131: newIdent = makeObfuscatedIdent(curId++);
132: if (isLegal(scope, newIdent)) {
133: break;
134: }
135: }
136: name.setShortIdent(newIdent);
137: }
138:
139: maxChildId = Math.max(mySiblingsMaxId, curId);
140: }
141: }
|