Skip to content

Commit

Permalink
Add support for ArrowFunctionExpression
Browse files Browse the repository at this point in the history
ref #33
  • Loading branch information
Constellation committed Nov 16, 2014
1 parent 19cb9f7 commit 1733eb0
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 12 deletions.
19 changes: 16 additions & 3 deletions escope.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
Copyright (C) 2012-2013 Yusuke Suzuki <[email protected]>
Copyright (C) 2012-2014 Yusuke Suzuki <[email protected]>
Copyright (C) 2013 Alex Seville <[email protected]>
Copyright (C) 2014 Thiago de Arruda <[email protected]>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down Expand Up @@ -342,6 +343,11 @@
return true;
}

// ArrowFunctionExpression's scope is always strict scope.
if (block.type === Syntax.ArrowFunctionExpression) {
return true;
}

if (scope.type === 'function') {
body = block.body;
} else if (scope.type === 'global') {
Expand All @@ -350,6 +356,7 @@
return false;
}

// Search 'use strict' directive.
if (options.directive) {
for (i = 0, iz = body.body.length; i < iz; ++i) {
stmt = body.body[i];
Expand Down Expand Up @@ -775,7 +782,10 @@
if (this.attached) {
return node[Scope.mangledName] || null;
}
if (this.__isScopeRequired(node)) {

// It may return false result. (for example, if node is the FunctionBody, it may return true under ES6 mode.)
// But it will be filtered the subsequent exploring.
if (this.__isScopeRequired(node, null)) {
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
scope = this.scopes[i];
if (!scope.functionExpressionScope) {
Expand Down Expand Up @@ -828,7 +838,7 @@

ScopeManager.prototype.__isScopeRequired = function (node, parent) {
function isFunctionScopeRequired(node) {
return node.type === Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration;
return node.type === Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration || node.type === Syntax.ArrowFunctionExpression;
}

function isVariableScopeRequired(node) {
Expand All @@ -847,6 +857,9 @@
}
return true;
}
if (node.type === Syntax.ArrowFunctionExpression) {
return true;
}
}
return isScopeRequired(node);
};
Expand Down
56 changes: 56 additions & 0 deletions test/es6-arrow-function-expression.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014 Yusuke Suzuki <[email protected]>
#
# 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.

expect = require('chai').expect
harmony = require '../third_party/esprima'
escope = require '..'

describe 'ES6 arrow function expression', ->
it 'materialize scope for arrow function expression', ->
ast = harmony.parse """
var arrow = () => {
let i = 0;
var j = 20;
console.log(i);
}
"""

scopeManager = escope.analyze ast, ecmaVersion: 6
expect(scopeManager.scopes).to.have.length 2

scope = scopeManager.scopes[0]
expect(scope.type).to.be.equal 'global'
expect(scope.block.type).to.be.equal 'Program'
expect(scope.isStrict).to.be.false
expect(scope.variables).to.have.length 1

scope = scopeManager.scopes[1]
expect(scope.type).to.be.equal 'function'
expect(scope.block.type).to.be.equal 'ArrowFunctionExpression'
expect(scope.isStrict).to.be.true
expect(scope.variables).to.have.length 3
expect(scope.variables[0].name).to.be.equal 'arguments'

This comment has been minimized.

Copy link
@Constellation

Constellation Nov 16, 2014

Author Member

Currently, we always materialize "arguments" variable. But maybe, it is not correct in rev28.
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-functiondeclarationinstantiation

expect(scope.variables[1].name).to.be.equal 'i'
expect(scope.variables[2].name).to.be.equal 'j'

# vim: set sw=4 ts=4 et tw=80 :
18 changes: 9 additions & 9 deletions test/es6-block-scope.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ escope = require '..'
describe 'ES6 block scope', ->
it 'let is materialized in ES6 block scope#1', ->
ast = harmony.parse """
{
let i = 20;
i;
}
{
let i = 20;
i;
}
"""

scopeManager = escope.analyze ast, ecmaVersion: 6
Expand All @@ -51,11 +51,11 @@ describe 'ES6 block scope', ->

it 'let is materialized in ES6 block scope#2', ->
ast = harmony.parse """
{
let i = 20;
var i = 20;
i;
}
{
let i = 20;
var i = 20;
i;
}
"""

scopeManager = escope.analyze ast, ecmaVersion: 6
Expand Down

0 comments on commit 1733eb0

Please sign in to comment.