From b713c53fc5b23c8df3a947af8f91f58e93b78751 Mon Sep 17 00:00:00 2001 From: Yusuke Suzuki Date: Wed, 11 Mar 2015 10:31:59 +0900 Subject: [PATCH] Support AssignmentPattern This is used for default values. After that, we need to drop default parameter support. CAUTION: There's no fully supported TDZ scope yet. --- src/referencer.js | 96 ++++++++++++----------- test/es6-destructuring-assignments.coffee | 76 ++++++++++++++++++ 2 files changed, 125 insertions(+), 47 deletions(-) diff --git a/src/referencer.js b/src/referencer.js index 4da84d5..f51a40f 100644 --- a/src/referencer.js +++ b/src/referencer.js @@ -21,64 +21,69 @@ (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 estraverse from 'estraverse'; +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'; -const Syntax = estraverse.Syntax; +class PatternVisitor extends esrecurse.Visitor { + constructor(rootPattern, referencer, callback) { + super(this); + this.referencer = referencer; + this.callback = callback; + } -function traverseIdentifierInPattern(rootPattern, callback) { - estraverse.traverse(rootPattern, { - enter(pattern, parent) { - var i, iz, element, property; + perform(pattern) { + if (pattern.type === Syntax.Identifier) { + this.callback(pattern, true); + return; + } + this.visit(pattern); + } - switch (pattern.type) { - case Syntax.Identifier: - // Toplevel identifier. - if (parent === null) { - callback(pattern, true); - } - break; + Identifier(pattern) { + this.callback(pattern, false); + } - case Syntax.SpreadElement: - if (pattern.argument.type === Syntax.Identifier) { - callback(pattern.argument, false); - } - break; - - case Syntax.ObjectPattern: - for (i = 0, iz = pattern.properties.length; i < iz; ++i) { - property = pattern.properties[i]; - if (property.shorthand) { - callback(property.key, false); - continue; - } - if (property.value.type === Syntax.Identifier) { - callback(property.value, false); - continue; - } - } - break; - - case Syntax.ArrayPattern: - for (i = 0, iz = pattern.elements.length; i < iz; ++i) { - element = pattern.elements[i]; - if (element && element.type === Syntax.Identifier) { - callback(element, false); - } - } - break; + 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; + return nodeType === Syntax.Identifier || nodeType === Syntax.ObjectPattern || nodeType === Syntax.ArrayPattern || nodeType === Syntax.SpreadElement || nodeType === Syntax.RestElement || nodeType === Syntax.AssignmentPattern; } // Importing ImportDeclaration. @@ -178,7 +183,7 @@ export default class Referencer extends esrecurse.Visitor { } visitPattern(node, callback) { - traverseIdentifierInPattern(node, callback); + traverseIdentifierInPattern(node, this, callback); } visitFunction(node) { @@ -332,9 +337,6 @@ export default class Referencer extends esrecurse.Visitor { decl = node.declarations[index]; init = decl.init; - // FIXME: Don't consider initializer with complex patterns. - // Such as, - // var [a, b, c = 20] = array; this.visitPattern(decl.id, (pattern, toplevel) => { variableTargetScope.__define(pattern, new Definition( diff --git a/test/es6-destructuring-assignments.coffee b/test/es6-destructuring-assignments.coffee index 8013b91..486bf3d 100644 --- a/test/es6-destructuring-assignments.coffee +++ b/test/es6-destructuring-assignments.coffee @@ -23,6 +23,7 @@ expect = require('chai').expect harmony = require '../third_party/esprima' +espree = require '../third_party/espree' escope = require '..' describe 'ES6 destructuring assignments', -> @@ -703,4 +704,79 @@ describe 'ES6 destructuring assignments', -> expect(scope.variables[index].name).to.be.equal name expect(scope.references).to.have.length 0 + it 'default values and patterns in var', -> + ast = espree """ + (function () { + var [a, b, c, d = 20 ] = array; + }()); + """ + + scopeManager = escope.analyze ast, ecmaVersion: 6 + expect(scopeManager.scopes).to.have.length 2 + + scope = scopeManager.scopes[0] + globalScope = scope + expect(scope.type).to.be.equal 'global' + expect(scope.variables).to.have.length 0 + expect(scope.references).to.have.length 0 + + scope = scopeManager.scopes[1] + expect(scope.type).to.be.equal 'function' + expect(scope.variables).to.have.length 5 + for name, index in [ + 'arguments' + 'a' + 'b' + 'c' + 'd' + ] + expect(scope.variables[index].name).to.be.equal name + expect(scope.references).to.have.length 5 + for name, index in [ + 'a' + 'b' + 'c' + 'd' + 'array' + ] + expect(scope.references[index].identifier.name).to.be.equal name + + it 'default values containing references and patterns in var', -> + ast = espree """ + (function () { + var [a, b, c, d = e ] = array; + }()); + """ + + scopeManager = escope.analyze ast, ecmaVersion: 6 + expect(scopeManager.scopes).to.have.length 2 + + scope = scopeManager.scopes[0] + globalScope = scope + expect(scope.type).to.be.equal 'global' + expect(scope.variables).to.have.length 0 + expect(scope.references).to.have.length 0 + + scope = scopeManager.scopes[1] + expect(scope.type).to.be.equal 'function' + expect(scope.variables).to.have.length 5 + for name, index in [ + 'arguments' + 'a' + 'b' + 'c' + 'd' + ] + expect(scope.variables[index].name).to.be.equal name + expect(scope.references).to.have.length 6 + for name, index in [ + 'a' + 'b' + 'c' + 'd' + 'e' + 'array' + ] + expect(scope.references[index].identifier.name).to.be.equal name + # vim: set sw=4 ts=4 et tw=80 :