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

com.google.javascript.jscomp.SyntacticScopeCreator Maven / Gradle / Ivy

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2006 The Closure Compiler Authors.
 *
 * 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.javascript.jscomp;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.common.base.Preconditions;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;

/**
 * 

The syntactic scope creator scans the parse tree to create a Scope object * containing all the variable declarations in that scope.

* *

This implementation is not thread-safe.

* */ public class SyntacticScopeCreator implements ScopeCreator { private final AbstractCompiler compiler; private AbstractScope scope; private InputId inputId; private final boolean isTyped; private SyntacticScopeCreator(AbstractCompiler compiler, boolean isTyped) { this.compiler = compiler; this.isTyped = isTyped; } /** * @deprecated Use Es6SyntacticScopeCreator instead. */ @Deprecated public static SyntacticScopeCreator makeUntyped(AbstractCompiler compiler) { return new SyntacticScopeCreator(compiler, false); } static SyntacticScopeCreator makeTyped(AbstractCompiler compiler) { return new SyntacticScopeCreator(compiler, true); } @Override @SuppressWarnings("unchecked") // The cast to T is OK because we cannot mix typed and untyped scopes in the same chain. public AbstractScope createScope(Node n, AbstractScope parent) { inputId = null; if (parent == null) { scope = isTyped ? TypedScope.createGlobalScope(n) : Scope.createGlobalScope(n); } else { scope = isTyped ? new TypedScope((TypedScope) parent, n) : Scope.createChildScope((Scope) parent, n); } scanRoot(n); inputId = null; AbstractScope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { checkState(a.isName()); declareVar(a); } // Body scanVars(body); } else { // It's either a module or the global block Preconditions.checkState(n.isModuleBody() || scope.getParent() == null, "Expected %s to be a module body, or %s to be the global scope.", n, scope); scanVars(n); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n) { switch (n.getToken()) { case VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; } return; case FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case CATCH: Preconditions.checkState(n.hasTwoChildren(), n); // The first child is the catch var and the second child // is the code block. final Node var = n.getFirstChild(); Preconditions.checkState(var.isName(), var); final Node block = var.getNext(); declareVar(var); scanVars(block); return; // only one child to scan case SCRIPT: inputId = n.getInputId(); checkNotNull(inputId); break; default: break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child); child = next; } } } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { checkState(n.isName(), n); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (!scope.isDeclared(name, false) && !(scope.isLocal() && name.equals(Var.ARGUMENTS))) { if (isTyped) { ((TypedScope) scope).declare(name, n, null, input); } else { ((Scope) scope).declare(name, n, input); } } } @Override public boolean hasBlockScope() { return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy