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

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

There is a newer version: 2.10.0
Show newest version
/*
 * 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.JsKeywords;
import com.google.gwt.dev.util.StringInterner;

import java.io.Serializable;
import java.util.Iterator;
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#getGlobalScope()}), 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 (JsKeywords.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 iterator for all the names defined by this scope. */ public abstract Iterator 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