You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
7.3 KiB
248 lines
7.3 KiB
4 weeks ago
|
/*
|
||
|
Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
"use strict";
|
||
|
|
||
|
/* eslint-disable no-underscore-dangle */
|
||
|
|
||
|
const Scope = require("./scope");
|
||
|
const assert = require("assert");
|
||
|
|
||
|
const GlobalScope = Scope.GlobalScope;
|
||
|
const CatchScope = Scope.CatchScope;
|
||
|
const WithScope = Scope.WithScope;
|
||
|
const ModuleScope = Scope.ModuleScope;
|
||
|
const ClassScope = Scope.ClassScope;
|
||
|
const SwitchScope = Scope.SwitchScope;
|
||
|
const FunctionScope = Scope.FunctionScope;
|
||
|
const ForScope = Scope.ForScope;
|
||
|
const FunctionExpressionNameScope = Scope.FunctionExpressionNameScope;
|
||
|
const BlockScope = Scope.BlockScope;
|
||
|
|
||
|
/**
|
||
|
* @class ScopeManager
|
||
|
*/
|
||
|
class ScopeManager {
|
||
|
constructor(options) {
|
||
|
this.scopes = [];
|
||
|
this.globalScope = null;
|
||
|
this.__nodeToScope = new WeakMap();
|
||
|
this.__currentScope = null;
|
||
|
this.__options = options;
|
||
|
this.__declaredVariables = new WeakMap();
|
||
|
}
|
||
|
|
||
|
__useDirective() {
|
||
|
return this.__options.directive;
|
||
|
}
|
||
|
|
||
|
__isOptimistic() {
|
||
|
return this.__options.optimistic;
|
||
|
}
|
||
|
|
||
|
__ignoreEval() {
|
||
|
return this.__options.ignoreEval;
|
||
|
}
|
||
|
|
||
|
__isNodejsScope() {
|
||
|
return this.__options.nodejsScope;
|
||
|
}
|
||
|
|
||
|
isModule() {
|
||
|
return this.__options.sourceType === "module";
|
||
|
}
|
||
|
|
||
|
isImpliedStrict() {
|
||
|
return this.__options.impliedStrict;
|
||
|
}
|
||
|
|
||
|
isStrictModeSupported() {
|
||
|
return this.__options.ecmaVersion >= 5;
|
||
|
}
|
||
|
|
||
|
// Returns appropriate scope for this node.
|
||
|
__get(node) {
|
||
|
return this.__nodeToScope.get(node);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get variables that are declared by the node.
|
||
|
*
|
||
|
* "are declared by the node" means the node is same as `Variable.defs[].node` or `Variable.defs[].parent`.
|
||
|
* If the node declares nothing, this method returns an empty array.
|
||
|
* CAUTION: This API is experimental. See https://github.com/estools/escope/pull/69 for more details.
|
||
|
*
|
||
|
* @param {Espree.Node} node - a node to get.
|
||
|
* @returns {Variable[]} variables that declared by the node.
|
||
|
*/
|
||
|
getDeclaredVariables(node) {
|
||
|
return this.__declaredVariables.get(node) || [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* acquire scope from node.
|
||
|
* @method ScopeManager#acquire
|
||
|
* @param {Espree.Node} node - node for the acquired scope.
|
||
|
* @param {boolean=} inner - look up the most inner scope, default value is false.
|
||
|
* @returns {Scope?} Scope from node
|
||
|
*/
|
||
|
acquire(node, inner) {
|
||
|
|
||
|
/**
|
||
|
* predicate
|
||
|
* @param {Scope} testScope - scope to test
|
||
|
* @returns {boolean} predicate
|
||
|
*/
|
||
|
function predicate(testScope) {
|
||
|
if (testScope.type === "function" && testScope.functionExpressionScope) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const scopes = this.__get(node);
|
||
|
|
||
|
if (!scopes || scopes.length === 0) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// Heuristic selection from all scopes.
|
||
|
// If you would like to get all scopes, please use ScopeManager#acquireAll.
|
||
|
if (scopes.length === 1) {
|
||
|
return scopes[0];
|
||
|
}
|
||
|
|
||
|
if (inner) {
|
||
|
for (let i = scopes.length - 1; i >= 0; --i) {
|
||
|
const scope = scopes[i];
|
||
|
|
||
|
if (predicate(scope)) {
|
||
|
return scope;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for (let i = 0, iz = scopes.length; i < iz; ++i) {
|
||
|
const scope = scopes[i];
|
||
|
|
||
|
if (predicate(scope)) {
|
||
|
return scope;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* acquire all scopes from node.
|
||
|
* @method ScopeManager#acquireAll
|
||
|
* @param {Espree.Node} node - node for the acquired scope.
|
||
|
* @returns {Scopes?} Scope array
|
||
|
*/
|
||
|
acquireAll(node) {
|
||
|
return this.__get(node);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* release the node.
|
||
|
* @method ScopeManager#release
|
||
|
* @param {Espree.Node} node - releasing node.
|
||
|
* @param {boolean=} inner - look up the most inner scope, default value is false.
|
||
|
* @returns {Scope?} upper scope for the node.
|
||
|
*/
|
||
|
release(node, inner) {
|
||
|
const scopes = this.__get(node);
|
||
|
|
||
|
if (scopes && scopes.length) {
|
||
|
const scope = scopes[0].upper;
|
||
|
|
||
|
if (!scope) {
|
||
|
return null;
|
||
|
}
|
||
|
return this.acquire(scope.block, inner);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
attach() { } // eslint-disable-line class-methods-use-this
|
||
|
|
||
|
detach() { } // eslint-disable-line class-methods-use-this
|
||
|
|
||
|
__nestScope(scope) {
|
||
|
if (scope instanceof GlobalScope) {
|
||
|
assert(this.__currentScope === null);
|
||
|
this.globalScope = scope;
|
||
|
}
|
||
|
this.__currentScope = scope;
|
||
|
return scope;
|
||
|
}
|
||
|
|
||
|
__nestGlobalScope(node) {
|
||
|
return this.__nestScope(new GlobalScope(this, node));
|
||
|
}
|
||
|
|
||
|
__nestBlockScope(node) {
|
||
|
return this.__nestScope(new BlockScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestFunctionScope(node, isMethodDefinition) {
|
||
|
return this.__nestScope(new FunctionScope(this, this.__currentScope, node, isMethodDefinition));
|
||
|
}
|
||
|
|
||
|
__nestForScope(node) {
|
||
|
return this.__nestScope(new ForScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestCatchScope(node) {
|
||
|
return this.__nestScope(new CatchScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestWithScope(node) {
|
||
|
return this.__nestScope(new WithScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestClassScope(node) {
|
||
|
return this.__nestScope(new ClassScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestSwitchScope(node) {
|
||
|
return this.__nestScope(new SwitchScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestModuleScope(node) {
|
||
|
return this.__nestScope(new ModuleScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__nestFunctionExpressionNameScope(node) {
|
||
|
return this.__nestScope(new FunctionExpressionNameScope(this, this.__currentScope, node));
|
||
|
}
|
||
|
|
||
|
__isES6() {
|
||
|
return this.__options.ecmaVersion >= 6;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ScopeManager;
|
||
|
|
||
|
/* vim: set sw=4 ts=4 et tw=80 : */
|