Skip to content

Commit

Permalink
feat: get correct scope ref from ThisExpression, fix #2
Browse files Browse the repository at this point in the history
  • Loading branch information
motiz88 committed Sep 11, 2016
1 parent a38554f commit daf8df5
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 17 deletions.
16 changes: 13 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import type { NodePath, Scope } from 'babel-traverse';

class InnerScopeVisitor {
referencedScopes: Scope[];
seenThisExpression: boolean;

ReferencedIdentifier = (path: NodePath) => {
const binding = path.scope.getBinding(path.node.name);
if (binding) {
// istanbul ignore next: could be initialized elsewhere
if (!this.referencedScopes) {
this.referencedScopes = [];
}
Expand All @@ -17,7 +17,18 @@ class InnerScopeVisitor {
}

ThisExpression = (path: NodePath) => {
this.seenThisExpression = true;
let {scope} = path;
while (scope && (scope = scope.getFunctionParent())) { // eslint-disable-line no-cond-assign
if (!scope.path.isArrowFunctionExpression()) {
// istanbul ignore next: could be initialized elsewhere
if (!this.referencedScopes) {
this.referencedScopes = [];
}
this.referencedScopes.push(scope);
return;
}
scope = scope.parent;
}
}
}

Expand Down Expand Up @@ -61,7 +72,6 @@ export default function ({types: t, template}: {types: BabelTypes, template: Bab
// or the global scope.
const innerScope = new InnerScopeVisitor();
path.traverse(innerScope);
if (innerScope.seenThisExpression && t.isArrowFunctionExpression(path.node)) return;
const referencedScopes = uniqueScopes(innerScope.referencedScopes || []);
const targetScope = deepestScopeOf(
path,
Expand Down
15 changes: 9 additions & 6 deletions test/fixtures/handle-references-to-this/actual.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ class A {
}

(function A () {
// FIXME: We are too conservative regarding "this" in arrow functions.
// The arrow func here is not hoisted due to the embedded ThisExpression,
// but it's actually safe because A's "this" is not referenced.
// This is made even more apparent by the fact that B itself is (correctly)
// hoisted, taking the ThisExpression along with it.
return () => function B () {return this};
// NOTE: hoisted
return () =>
// NOTE: hoisted
function B () {
return this;
};
})();

// NOTE: not hoisted
() => this;
21 changes: 13 additions & 8 deletions test/fixtures/handle-references-to-this/expected.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var _this = this;

(function () {
// NOTE: not hoisted
return () => this;
Expand All @@ -10,15 +12,18 @@ class A {
}
}

var _B = function B() {
var
// NOTE: hoisted
_B = function B() {
return this;
};

var _hoistedAnonymousFunc2 = () => _B;

(function A() {
// FIXME: We are too conservative regarding "this" in arrow functions.
// The arrow func here is not hoisted due to the embedded ThisExpression,
// but it's actually safe because A's "this" is not referenced.
// This is made even more apparent by the fact that B itself is (correctly)
// hoisted, taking the ThisExpression along with it.
return () => _B;
})();
// NOTE: hoisted
return _hoistedAnonymousFunc2;
})();

// NOTE: not hoisted
() => _this;

0 comments on commit daf8df5

Please sign in to comment.