summaryrefslogtreecommitdiff
path: root/tools/eslint/node_modules/escope/src/referencer.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint/node_modules/escope/src/referencer.js')
-rw-r--r--tools/eslint/node_modules/escope/src/referencer.js593
1 files changed, 593 insertions, 0 deletions
diff --git a/tools/eslint/node_modules/escope/src/referencer.js b/tools/eslint/node_modules/escope/src/referencer.js
new file mode 100644
index 0000000000..a337be0d45
--- /dev/null
+++ b/tools/eslint/node_modules/escope/src/referencer.js
@@ -0,0 +1,593 @@
+/*
+ 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.
+*/
+import { Syntax } from 'estraverse';
+import esrecurse from 'esrecurse';
+import Reference from './reference';
+import Variable from './variable';
+import { ParameterDefinition, Definition } from './definition';
+import assert from 'assert';
+
+class PatternVisitor extends esrecurse.Visitor {
+ constructor(rootPattern, referencer, callback) {
+ super();
+ this.referencer = referencer;
+ this.callback = callback;
+ }
+
+ perform(pattern) {
+ if (pattern.type === Syntax.Identifier) {
+ this.callback(pattern, true);
+ return;
+ }
+ this.visit(pattern);
+ }
+
+ Identifier(pattern) {
+ this.callback(pattern, false);
+ }
+
+ ObjectPattern(pattern) {
+ var i, iz, property;
+ for (i = 0, iz = pattern.properties.length; i < iz; ++i) {
+ property = pattern.properties[i];
+ if (property.shorthand) {
+ this.visit(property.key);
+ continue;
+ }
+ this.visit(property.value);
+ }
+ }
+
+ ArrayPattern(pattern) {
+ var i, iz, element;
+ for (i = 0, iz = pattern.elements.length; i < iz; ++i) {
+ element = pattern.elements[i];
+ if (element) {
+ this.visit(element);
+ }
+ }
+ }
+
+ AssignmentPattern(pattern) {
+ this.visit(pattern.left);
+ // FIXME: Condier TDZ scope.
+ this.referencer.visit(pattern.right);
+ }
+}
+
+function traverseIdentifierInPattern(rootPattern, referencer, callback) {
+ var visitor = new PatternVisitor(rootPattern, referencer, callback);
+ visitor.perform(rootPattern);
+}
+
+function isPattern(node) {
+ var nodeType = node.type;
+ return nodeType === Syntax.Identifier || nodeType === Syntax.ObjectPattern || nodeType === Syntax.ArrayPattern || nodeType === Syntax.SpreadElement || nodeType === Syntax.RestElement || nodeType === Syntax.AssignmentPattern;
+}
+
+// Importing ImportDeclaration.
+// http://people.mozilla.org/~jorendorff/es6-draft.html#sec-moduledeclarationinstantiation
+// https://github.com/estree/estree/blob/master/es6.md#importdeclaration
+// FIXME: Now, we don't create module environment, because the context is
+// implementation dependent.
+
+class Importer extends esrecurse.Visitor {
+ constructor(declaration, referencer) {
+ super();
+ this.declaration = declaration;
+ this.referencer = referencer;
+ }
+
+ visitImport(id, specifier) {
+ this.referencer.visitPattern(id, (pattern) => {
+ this.referencer.currentScope().__define(pattern,
+ new Definition(
+ Variable.ImportBinding,
+ pattern,
+ specifier,
+ this.declaration,
+ null,
+ null
+ ));
+ });
+ }
+
+ ImportNamespaceSpecifier(node) {
+ let local = (node.local || node.id);
+ if (local) {
+ this.visitImport(local, node);
+ }
+ }
+
+ ImportDefaultSpecifier(node) {
+ let local = (node.local || node.id);
+ this.visitImport(local, node);
+ }
+
+ ImportSpecifier(node) {
+ let local = (node.local || node.id);
+ if (node.name) {
+ this.visitImport(node.name, node);
+ } else {
+ this.visitImport(local, node);
+ }
+ }
+}
+
+// Referencing variables and creating bindings.
+export default class Referencer extends esrecurse.Visitor {
+ constructor(scopeManager) {
+ super();
+ this.scopeManager = scopeManager;
+ this.parent = null;
+ this.isInnerMethodDefinition = false;
+ }
+
+ currentScope() {
+ return this.scopeManager.__currentScope;
+ }
+
+ close(node) {
+ while (this.currentScope() && node === this.currentScope().block) {
+ this.scopeManager.__currentScope = this.currentScope().__close(this.scopeManager);
+ }
+ }
+
+ pushInnerMethodDefinition(isInnerMethodDefinition) {
+ var previous = this.isInnerMethodDefinition;
+ this.isInnerMethodDefinition = isInnerMethodDefinition;
+ return previous;
+ }
+
+ popInnerMethodDefinition(isInnerMethodDefinition) {
+ this.isInnerMethodDefinition = isInnerMethodDefinition;
+ }
+
+ materializeTDZScope(node, iterationNode) {
+ // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-forin-div-ofexpressionevaluation-abstract-operation
+ // TDZ scope hides the declaration's names.
+ this.scopeManager.__nestTDZScope(node, iterationNode);
+ this.visitVariableDeclaration(this.currentScope(), Variable.TDZ, iterationNode.left, 0);
+ }
+
+ materializeIterationScope(node) {
+ // Generate iteration scope for upper ForIn/ForOf Statements.
+ var letOrConstDecl;
+ this.scopeManager.__nestForScope(node);
+ letOrConstDecl = node.left;
+ this.visitVariableDeclaration(this.currentScope(), Variable.Variable, letOrConstDecl, 0);
+ this.visitPattern(letOrConstDecl.declarations[0].id, (pattern) => {
+ this.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true);
+ });
+ }
+
+ visitPattern(node, callback) {
+ traverseIdentifierInPattern(node, this, callback);
+ }
+
+ visitFunction(node) {
+ var i, iz;
+ // FunctionDeclaration name is defined in upper scope
+ // NOTE: Not referring variableScope. It is intended.
+ // Since
+ // in ES5, FunctionDeclaration should be in FunctionBody.
+ // in ES6, FunctionDeclaration should be block scoped.
+ if (node.type === Syntax.FunctionDeclaration) {
+ // id is defined in upper scope
+ this.currentScope().__define(node.id,
+ new Definition(
+ Variable.FunctionName,
+ node.id,
+ node,
+ null,
+ null,
+ null
+ ));
+ }
+
+ // FunctionExpression with name creates its special scope;
+ // FunctionExpressionNameScope.
+ if (node.type === Syntax.FunctionExpression && node.id) {
+ this.scopeManager.__nestFunctionExpressionNameScope(node);
+ }
+
+ // Consider this function is in the MethodDefinition.
+ this.scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
+
+ for (i = 0, iz = node.params.length; i < iz; ++i) {
+ this.visitPattern(node.params[i], (pattern) => {
+ this.currentScope().__define(pattern,
+ new ParameterDefinition(
+ pattern,
+ node,
+ i,
+ false
+ ));
+ });
+ }
+
+ // if there's a rest argument, add that
+ if (node.rest) {
+ this.visitPattern({
+ type: 'RestElement',
+ argument: node.rest
+ }, (pattern) => {
+ this.currentScope().__define(pattern,
+ new ParameterDefinition(
+ pattern,
+ node,
+ node.params.length,
+ true
+ ));
+ });
+ }
+
+ // Skip BlockStatement to prevent creating BlockStatement scope.
+ if (node.body.type === Syntax.BlockStatement) {
+ this.visitChildren(node.body);
+ } else {
+ this.visit(node.body);
+ }
+
+ this.close(node);
+ }
+
+ visitClass(node) {
+ if (node.type === Syntax.ClassDeclaration) {
+ this.currentScope().__define(node.id,
+ new Definition(
+ Variable.ClassName,
+ node.id,
+ node,
+ null,
+ null,
+ null
+ ));
+ }
+
+ // FIXME: Maybe consider TDZ.
+ this.visit(node.superClass);
+
+ this.scopeManager.__nestClassScope(node);
+
+ if (node.id) {
+ this.currentScope().__define(node.id,
+ new Definition(
+ Variable.ClassName,
+ node.id,
+ node
+ ));
+ }
+ this.visit(node.body);
+
+ this.close(node);
+ }
+
+ visitProperty(node) {
+ var previous, isMethodDefinition;
+ if (node.computed) {
+ this.visit(node.key);
+ }
+
+ isMethodDefinition = node.type === Syntax.MethodDefinition || node.method;
+ if (isMethodDefinition) {
+ previous = this.pushInnerMethodDefinition(true);
+ }
+ this.visit(node.value);
+ if (isMethodDefinition) {
+ this.popInnerMethodDefinition(previous);
+ }
+ }
+
+ visitForIn(node) {
+ if (node.left.type === Syntax.VariableDeclaration && node.left.kind !== 'var') {
+ this.materializeTDZScope(node.right, node);
+ this.visit(node.right);
+ this.close(node.right);
+
+ this.materializeIterationScope(node);
+ this.visit(node.body);
+ this.close(node);
+ } else {
+ if (node.left.type === Syntax.VariableDeclaration) {
+ this.visit(node.left);
+ this.visitPattern(node.left.declarations[0].id, (pattern) => {
+ this.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true);
+ });
+ } else {
+ if (!isPattern(node.left)) {
+ this.visit(node.left);
+ }
+ this.visitPattern(node.left, (pattern) => {
+ var maybeImplicitGlobal = null;
+ if (!this.currentScope().isStrict) {
+ maybeImplicitGlobal = {
+ pattern: pattern,
+ node: node
+ };
+ }
+ this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, true);
+ });
+ }
+ this.visit(node.right);
+ this.visit(node.body);
+ }
+ }
+
+ visitVariableDeclaration(variableTargetScope, type, node, index) {
+ var decl, init;
+
+ decl = node.declarations[index];
+ init = decl.init;
+ this.visitPattern(decl.id, (pattern, toplevel) => {
+ variableTargetScope.__define(pattern,
+ new Definition(
+ type,
+ pattern,
+ decl,
+ node,
+ index,
+ node.kind
+ ));
+
+ if (init) {
+ this.currentScope().__referencing(pattern, Reference.WRITE, init, null, !toplevel);
+ }
+ });
+ }
+
+ AssignmentExpression(node) {
+ if (isPattern(node.left)) {
+ if (node.operator === '=') {
+ this.visitPattern(node.left, (pattern, toplevel) => {
+ var maybeImplicitGlobal = null;
+ if (!this.currentScope().isStrict) {
+ maybeImplicitGlobal = {
+ pattern: pattern,
+ node: node
+ };
+ }
+ this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, !toplevel);
+ });
+ } else {
+ this.currentScope().__referencing(node.left, Reference.RW, node.right);
+ }
+ } else {
+ this.visit(node.left);
+ }
+ this.visit(node.right);
+ }
+
+ CatchClause(node) {
+ this.scopeManager.__nestCatchScope(node);
+
+ this.visitPattern(node.param, (pattern) => {
+ this.currentScope().__define(pattern,
+ new Definition(
+ Variable.CatchClause,
+ node.param,
+ node,
+ null,
+ null,
+ null
+ ));
+ });
+ this.visit(node.body);
+
+ this.close(node);
+ }
+
+ Program(node) {
+ this.scopeManager.__nestGlobalScope(node);
+
+ if (this.scopeManager.__isNodejsScope()) {
+ // Force strictness of GlobalScope to false when using node.js scope.
+ this.currentScope().isStrict = false;
+ this.scopeManager.__nestFunctionScope(node, false);
+ }
+
+ if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
+ this.scopeManager.__nestModuleScope(node);
+ }
+
+ this.visitChildren(node);
+ this.close(node);
+ }
+
+ Identifier(node) {
+ this.currentScope().__referencing(node);
+ }
+
+ UpdateExpression(node) {
+ if (isPattern(node.argument)) {
+ this.currentScope().__referencing(node.argument, Reference.RW, null);
+ } else {
+ this.visitChildren(node);
+ }
+ }
+
+ MemberExpression(node) {
+ this.visit(node.object);
+ if (node.computed) {
+ this.visit(node.property);
+ }
+ }
+
+ Property(node) {
+ this.visitProperty(node);
+ }
+
+ MethodDefinition(node) {
+ this.visitProperty(node);
+ }
+
+ BreakStatement() {}
+
+ ContinueStatement() {}
+
+ LabeledStatement(node) {
+ this.visit(node.body);
+ }
+
+ ForStatement(node) {
+ // Create ForStatement declaration.
+ // NOTE: In ES6, ForStatement dynamically generates
+ // per iteration environment. However, escope is
+ // a static analyzer, we only generate one scope for ForStatement.
+ if (node.init && node.init.type === Syntax.VariableDeclaration && node.init.kind !== 'var') {
+ this.scopeManager.__nestForScope(node);
+ }
+
+ this.visitChildren(node);
+
+ this.close(node);
+ }
+
+ ClassExpression(node) {
+ this.visitClass(node);
+ }
+
+ ClassDeclaration(node) {
+ this.visitClass(node);
+ }
+
+ CallExpression(node) {
+ // Check this is direct call to eval
+ if (!this.scopeManager.__ignoreEval() && node.callee.type === Syntax.Identifier && node.callee.name === 'eval') {
+ // NOTE: This should be `variableScope`. Since direct eval call always creates Lexical environment and
+ // let / const should be enclosed into it. Only VariableDeclaration affects on the caller's environment.
+ this.currentScope().variableScope.__detectEval();
+ }
+ this.visitChildren(node);
+ }
+
+ BlockStatement(node) {
+ if (this.scopeManager.__isES6()) {
+ this.scopeManager.__nestBlockScope(node);
+ }
+
+ this.visitChildren(node);
+
+ this.close(node);
+ }
+
+ ThisExpression() {
+ this.currentScope().variableScope.__detectThis();
+ }
+
+ WithStatement(node) {
+ this.visit(node.object);
+ // Then nest scope for WithStatement.
+ this.scopeManager.__nestWithScope(node);
+
+ this.visit(node.body);
+
+ this.close(node);
+ }
+
+ VariableDeclaration(node) {
+ var variableTargetScope, i, iz, decl;
+ variableTargetScope = (node.kind === 'var') ? this.currentScope().variableScope : this.currentScope();
+ for (i = 0, iz = node.declarations.length; i < iz; ++i) {
+ decl = node.declarations[i];
+ this.visitVariableDeclaration(variableTargetScope, Variable.Variable, node, i);
+ if (decl.init) {
+ this.visit(decl.init);
+ }
+ }
+ }
+
+ // sec 13.11.8
+ SwitchStatement(node) {
+ var i, iz;
+
+ this.visit(node.discriminant);
+
+ if (this.scopeManager.__isES6()) {
+ this.scopeManager.__nestSwitchScope(node);
+ }
+
+ for (i = 0, iz = node.cases.length; i < iz; ++i) {
+ this.visit(node.cases[i]);
+ }
+
+ this.close(node);
+ }
+
+ FunctionDeclaration(node) {
+ this.visitFunction(node);
+ }
+
+ FunctionExpression(node) {
+ this.visitFunction(node);
+ }
+
+ ForOfStatement(node) {
+ this.visitForIn(node);
+ }
+
+ ForInStatement(node) {
+ this.visitForIn(node);
+ }
+
+ ArrowFunctionExpression(node) {
+ this.visitFunction(node);
+ }
+
+ ImportDeclaration(node) {
+ var importer;
+
+ assert(this.scopeManager.__isES6() && this.scopeManager.isModule(), 'ImportDeclaration should appear when the mode is ES6 and in the module context.');
+
+ importer = new Importer(node, this);
+ importer.visit(node);
+ }
+
+ visitExportDeclaration(node) {
+ if (node.source) {
+ return;
+ }
+ if (node.declaration) {
+ this.visit(node.declaration);
+ return;
+ }
+
+ this.visitChildren(node);
+ }
+
+ ExportDeclaration(node) {
+ this.visitExportDeclaration(node);
+ }
+
+ ExportNamedDeclaration(node) {
+ this.visitExportDeclaration(node);
+ }
+
+ ExportSpecifier(node) {
+ let local = (node.id || node.local);
+ this.visit(local);
+ }
+}
+
+/* vim: set sw=4 ts=4 et tw=80 : */