| /* |
| * Copyright 2007 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.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| /** |
| * A namer that uses short, readable idents to maximize reability. |
| */ |
| public class JsPrettyNamer extends JsNamer { |
| |
| public static void exec(JsProgram program) { |
| new JsPrettyNamer(program).execImpl(); |
| } |
| |
| /** |
| * Communicates to a parent scope all the idents used by all child scopes. |
| */ |
| private Set<String> childIdents = null; |
| |
| public JsPrettyNamer(JsProgram program) { |
| super(program); |
| } |
| |
| @Override |
| protected void reset() { |
| childIdents = new HashSet<String>(); |
| } |
| |
| @Override |
| protected void visit(JsScope scope) { |
| // Save off the childIdents which is currently being computed for my parent. |
| Set<String> myChildIdents = childIdents; |
| |
| /* |
| * Visit my children first. Reset childIdents so that my children will get a |
| * clean slate: I do not communicate to my children. |
| */ |
| childIdents = new HashSet<String>(); |
| for (JsScope child : scope.getChildren()) { |
| visit(child); |
| } |
| // Child idents now contains all idents my children are using. |
| |
| // The next integer to try as an identifier suffix. |
| HashMap<String, Integer> startIdent = new HashMap<String, Integer>(); |
| |
| // Visit all my idents. |
| 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 = name.getShortIdent(); |
| if (!isLegal(scope, childIdents, newIdent)) { |
| String checkIdent; |
| |
| // Start searching using a suffix hint stored in the scope. |
| // We still do a search in case there is a collision with |
| // a user-provided identifier |
| Integer s = startIdent.get(newIdent); |
| int suffix = (s == null) ? 0 : s.intValue(); |
| do { |
| checkIdent = newIdent + "_" + suffix++; |
| } while (!isLegal(scope, childIdents, checkIdent)); |
| startIdent.put(newIdent, suffix); |
| name.setShortIdent(checkIdent); |
| } else { |
| // nothing to do; the short name is already good |
| } |
| childIdents.add(name.getShortIdent()); |
| } |
| myChildIdents.addAll(childIdents); |
| childIdents = myChildIdents; |
| } |
| |
| private boolean isLegal(JsScope scope, Set<String> childIdents, |
| String newIdent) { |
| if (JsKeywords.isKeyword(newIdent)) { |
| return false; |
| } |
| if (childIdents.contains(newIdent)) { |
| // one of my children already claimed this ident |
| 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; that name will get obfuscated out of the way. |
| */ |
| return (scope.findExistingUnobfuscatableName(newIdent) == null); |
| } |
| } |