| /* |
| * 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.cfg.ConfigurationProperties; |
| 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.Set; |
| |
| /** |
| * A namer that keeps the short ("pretty") identifier wherever possible. |
| */ |
| public class JsPrettyNamer extends JsNamer { |
| |
| public static void exec(JsProgram program, ConfigurationProperties config) throws IllegalNameException { |
| new JsPrettyNamer(program, config).execImpl(); |
| } |
| |
| public JsPrettyNamer(JsProgram program, ConfigurationProperties config) { |
| super(program, config); |
| } |
| |
| @Override |
| protected void reset() { |
| } |
| |
| @Override |
| protected void visit(JsScope scope) { |
| changeNames(scope); |
| } |
| |
| /** |
| * Does a minimal fixup on the short names in the given scope and all its descendants. |
| * Unobfuscatable names map to their full name and any names that conflict with a previous |
| * idenitifier or any child scope's identifier are renamed. Otherwise the short name is |
| * left as-is. |
| * @return all names used in the given scope and any of its descendants (after renaming). |
| */ |
| private Set<String> changeNames(JsScope scope) { |
| |
| // First, change the names in all the child scopes and remember that they're taken. |
| Set<String> taken = new HashSet<String>(); |
| for (JsScope child : scope.getChildren()) { |
| taken.addAll(changeNames(child)); |
| } |
| |
| // The next integer to try as an identifier suffix. |
| HashMap<String, Integer> suffixCounters = new HashMap<String, Integer>(); |
| |
| for (JsName name : scope.getAllNames()) { |
| if (!referenced.contains(name)) { |
| // Don't allocate idents for non-referenced names. |
| continue; |
| } |
| rename(name, scope, taken, suffixCounters); |
| taken.add(name.getShortIdent()); |
| } |
| |
| return taken; |
| } |
| |
| /** |
| * Changes the short identifier of one JsName. |
| * @param taken the set of short identifiers that are already used (read-only) |
| * @param suffixCounters contains the next suffix to use for each prefix (updated). |
| */ |
| private void rename(JsName name, JsScope scope, Set<String> taken, |
| HashMap<String, Integer> suffixCounters) { |
| |
| if (!name.isObfuscatable()) { |
| // We must use the full name. |
| name.setShortIdent(name.getIdent()); |
| return; |
| } |
| |
| String prefix = name.getShortIdent(); |
| if (prefix.contains("-")) { |
| // Fixes package-info.java classes. |
| prefix = prefix.replace("-", "_"); |
| name.setShortIdent(prefix); |
| } |
| if (!isAvailable(prefix, scope, taken)) { |
| |
| // 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 suffixOrNull = suffixCounters.get(prefix); |
| int suffix = (suffixOrNull == null) ? 0 : suffixOrNull; |
| |
| String candidate; |
| do { |
| candidate = prefix + "_" + suffix++; |
| } while (!isAvailable(candidate, scope, taken)); |
| |
| suffixCounters.put(prefix, suffix); |
| name.setShortIdent(candidate); |
| } |
| // otherwise the short name is already good |
| } |
| |
| /** |
| * Returns true if a candidate identifier is available in a scope. |
| * @param taken the set of names that we've already used. |
| */ |
| private boolean isAvailable(String candidate, JsScope scope, Set<String> taken) { |
| if (!reserved.isAvailable(candidate)) { |
| return false; |
| } |
| |
| if (taken.contains(candidate)) { |
| 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(candidate) == null); |
| } |
| } |