All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.js.ast.JsScope Maven / Gradle / Ivy

/*
 * 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.ast;

import com.google.gwt.dev.js.JsProtectedNames;
import com.google.gwt.dev.util.StringInterner;

import java.io.Serializable;
import java.util.List;

/**
 * A scope is a factory for creating and allocating {@link com.google.gwt.dev.js.ast.JsName}s. A
 * JavaScript AST is built in terms of abstract name objects without worrying about obfuscation,
 * keyword/identifier blacklisting, and so on.
 *
 * 

* * Scopes are associated with {@link com.google.gwt.dev.js.ast.JsFunction}s, but the two are not * equivalent. Functions have scopes, but a scope does not necessarily have an associated * Function. Examples of this include the {@link com.google.gwt.dev.js.ast.JsRootScope} and * synthetic scopes that might be created by a client. * *

* * Scopes can have parents to provide constraints when allocating actual identifiers for names. * Specifically, names in child scopes are chosen such that they do not conflict with names in their * parent scopes. The ultimate parent is usually the global scope (see * {@link com.google.gwt.dev.js.ast.JsProgram#getScope()}), but parentless scopes are useful * for managing names that are always accessed with a qualifier and could therefore never be * confused with the global scope hierarchy. */ public abstract class JsScope implements Serializable { /** * Prevents the client from programmatically creating an illegal ident. */ private static String maybeMangleKeyword(String ident) { if (JsProtectedNames.isKeyword(ident)) { ident = ident + "_$"; } return StringInterner.get().intern(ident); } private final String description; protected JsScope(String description) { this.description = StringInterner.get().intern(description); } /** * Gets a name object associated with the specified ident in this scope, creating it if necessary. * * @param ident An identifier that is unique within this scope. */ public final JsName declareName(String ident) { ident = maybeMangleKeyword(ident); JsName name = findExistingNameNoRecurse(ident); if (name != null) { return name; } return doCreateName(ident, ident); } /** * Gets a name object associated with the specified ident in this scope, creating it if necessary. * * @param ident An identifier that is unique within this scope. * @param shortIdent A "pretty" name that does not have to be unique. * @throws IllegalArgumentException if ident already exists in this scope but the requested short * name does not match the existing short name. */ public final JsName declareName(String ident, String shortIdent) { ident = maybeMangleKeyword(ident); shortIdent = maybeMangleKeyword(shortIdent); JsName name = findExistingNameNoRecurse(ident); if (name != null) { if (!name.getShortIdent().equals(shortIdent)) { throw new IllegalArgumentException("Requested short name " + shortIdent + " conflicts with preexisting short name " + name.getShortIdent() + " for identifier " + ident); } return name; } return doCreateName(ident, shortIdent); } /** * Attempts to find the name object for the specified ident, searching in this scope, and if not * found, in the parent scopes. * * @return null if the identifier has no associated name */ public final JsName findExistingName(String ident) { ident = maybeMangleKeyword(ident); JsName name = findExistingNameNoRecurse(ident); if (name == null && getParent() != null) { return getParent().findExistingName(ident); } return name; } /** * Attempts to find an unobfuscatable name object for the specified ident, searching in this * scope, and if not found, in the parent scopes. * * @return null if the identifier has no associated name */ public final JsName findExistingUnobfuscatableName(String ident) { ident = maybeMangleKeyword(ident); JsName name = findExistingNameNoRecurse(ident); if (name != null && name.isObfuscatable()) { name = null; } if (name == null && getParent() != null) { return getParent().findExistingUnobfuscatableName(ident); } return name; } /** * Returns an iterable for all the names defined by this scope. */ public abstract Iterable getAllNames(); /** * Returns a list of this scope's child scopes. */ public abstract List getChildren(); /** * Returns the parent scope of this scope, or null if this is the root scope. */ public abstract JsScope getParent(); @Override public final String toString() { if (getParent() != null) { return description + "->" + getParent(); } else { return description; } } protected abstract void addChild(JsScope child); /** * Creates a new name in this scope. */ protected abstract JsName doCreateName(String ident, String shortIdent); /** * Attempts to find the name object for the specified ident, searching in this scope only. * * @return null if the identifier has no associated name */ protected abstract JsName findExistingNameNoRecurse(String ident); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy