| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.dev.js; |
| |
| import com.google.gwt.dev.js.ast.JsName; |
| import com.google.gwt.dev.js.ast.JsProgram; |
| import com.google.gwt.dev.js.ast.JsScope; |
| |
| import java.util.Iterator; |
| |
| /** |
| * A namer that uses short, unrecognizable idents to minimize generated code |
| * size. |
| */ |
| public class JsObfuscateNamer extends JsNamer { |
| |
| /** |
| * A lookup table of base-64 chars we use to encode idents. |
| */ |
| private static final char[] sBase64Chars = new char[]{ |
| 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
| 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', |
| 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', |
| 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '$', '_', '0', '1', |
| '2', '3', '4', '5', '6', '7', '8', '9'}; |
| |
| public static void exec(JsProgram program) { |
| new JsObfuscateNamer(program).execImpl(); |
| } |
| |
| /** |
| * Communicates to a parent scope the maximum id used by any of its children. |
| */ |
| private int maxChildId = 0; |
| |
| /** |
| * A temp buffer big enough to hold at least 32 bits worth of base-64 chars. |
| */ |
| private final char[] sIdentBuf = new char[6]; |
| |
| public JsObfuscateNamer(JsProgram program) { |
| super(program); |
| } |
| |
| @Override |
| protected void reset() { |
| maxChildId = 0; |
| } |
| |
| @Override |
| protected void visit(JsScope scope) { |
| // Save off the maxChildId which is currently being computed for my parent. |
| int mySiblingsMaxId = maxChildId; |
| |
| /* |
| * Visit my children first. Reset maxChildId so that my children will get a |
| * clean slate: I do not communicate to my children. |
| */ |
| maxChildId = 0; |
| for (JsScope child : scope.getChildren()) { |
| visit(child); |
| } |
| // maxChildId is now the max of all of my children's ids |
| |
| // Visit my idents. |
| int curId = maxChildId; |
| for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) { |
| JsName name = it.next(); |
| if (!referenced.contains(name)) { |
| // Don't allocate idents for non-referenced names. |
| continue; |
| } |
| |
| if (!name.isObfuscatable()) { |
| // Unobfuscatable names become themselves. |
| name.setShortIdent(name.getIdent()); |
| continue; |
| } |
| |
| String newIdent; |
| while (true) { |
| // Get the next possible obfuscated name |
| newIdent = makeObfuscatedIdent(curId++); |
| if (isLegal(scope, newIdent)) { |
| break; |
| } |
| } |
| name.setShortIdent(newIdent); |
| } |
| |
| maxChildId = Math.max(mySiblingsMaxId, curId); |
| } |
| |
| private boolean isLegal(JsScope scope, String newIdent) { |
| if (JsKeywords.isKeyword(newIdent)) { |
| return false; |
| } |
| /* |
| * Never obfuscate a name into an identifier that conflicts with an existing |
| * unobfuscatable name! It's okay if it conflicts with an existing |
| * obfuscatable name, since that name will get obfuscated to something else |
| * anyway. |
| */ |
| return (scope.findExistingUnobfuscatableName(newIdent) == null); |
| } |
| |
| private String makeObfuscatedIdent(int id) { |
| // Use base-54 for the first character of the identifier, |
| // so that we don't use any numbers (which are illegal at |
| // the beginning of an identifier). |
| // |
| int i = 0; |
| sIdentBuf[i++] = sBase64Chars[id % 54]; |
| id /= 54; |
| |
| // Use base-64 for the rest of the identifier. |
| // |
| while (id != 0) { |
| sIdentBuf[i++] = sBase64Chars[id & 0x3f]; |
| id >>= 6; |
| } |
| |
| return new String(sIdentBuf, 0, i); |
| } |
| } |