opt out of tail recursion optimisation if the owner id has been reassigned - fixes #744
This commit is contained in:
parent
56a953df64
commit
db93c52182
@ -64,11 +64,16 @@ TailCallTransformer.prototype.run = function () {
|
|||||||
var scope = this.scope;
|
var scope = this.scope;
|
||||||
var node = this.node;
|
var node = this.node;
|
||||||
|
|
||||||
// only tail recursion can be optimized as for now,
|
// only tail recursion can be optimized as for now, so we can skip anonymous
|
||||||
// so we can skip anonymous functions entirely
|
// functions entirely
|
||||||
var ownerId = this.ownerId;
|
var ownerId = this.ownerId;
|
||||||
if (!ownerId) return;
|
if (!ownerId) return;
|
||||||
|
|
||||||
|
// check if the ownerId has been reassigned, if it has then it's not safe to
|
||||||
|
// perform optimisations
|
||||||
|
var ownerIdInfo = this.scope.getBindingInfo(ownerId.name);
|
||||||
|
if (!ownerIdInfo || ownerIdInfo.reassigned) return;
|
||||||
|
|
||||||
// traverse the function and look for tail recursion
|
// traverse the function and look for tail recursion
|
||||||
scope.traverse(node, firstPass, this);
|
scope.traverse(node, firstPass, this);
|
||||||
|
|
||||||
|
|||||||
@ -229,6 +229,14 @@ Scope.prototype.registerDeclaration = function (node) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Scope.prototype.registerBindingReassignment = function (node) {
|
||||||
|
var ids = t.getBindingIdentifiers(node);
|
||||||
|
for (var name in ids) {
|
||||||
|
var info = this.getBindingInfo(name);
|
||||||
|
if (info) info.reassigned = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Scope.prototype.registerBinding = function (kind, node) {
|
Scope.prototype.registerBinding = function (kind, node) {
|
||||||
if (!kind) throw new ReferenceError("no `kind`");
|
if (!kind) throw new ReferenceError("no `kind`");
|
||||||
|
|
||||||
@ -241,6 +249,7 @@ Scope.prototype.registerBinding = function (kind, node) {
|
|||||||
|
|
||||||
this.bindings[name] = {
|
this.bindings[name] = {
|
||||||
typeAnnotation: this.getTypeAnnotation(name, id, node),
|
typeAnnotation: this.getTypeAnnotation(name, id, node),
|
||||||
|
reassigned: false,
|
||||||
identifier: id,
|
identifier: id,
|
||||||
scope: this,
|
scope: this,
|
||||||
kind: kind
|
kind: kind
|
||||||
@ -302,6 +311,8 @@ var programReferenceVisitor = {
|
|||||||
state.addGlobal(node);
|
state.addGlobal(node);
|
||||||
} else if (t.isLabeledStatement(node)) {
|
} else if (t.isLabeledStatement(node)) {
|
||||||
state.addGlobal(node);
|
state.addGlobal(node);
|
||||||
|
} else if (t.isAssignmentExpression(node)) {
|
||||||
|
scope.registerBindingReassignment(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
13
test/fixtures/transformation/es6-tail-call/ignore-reassigned/actual.js
vendored
Normal file
13
test/fixtures/transformation/es6-tail-call/ignore-reassigned/actual.js
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// we need to deopt `test` if it's reassigned as we can't be certain of it's
|
||||||
|
// state, ie. it could have been rebound or dereferenced
|
||||||
|
|
||||||
|
function test(exit) {
|
||||||
|
if (exit) {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
return test(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
test = test.bind({ x: "yay" });
|
||||||
|
|
||||||
|
console.log(test());
|
||||||
15
test/fixtures/transformation/es6-tail-call/ignore-reassigned/expected.js
vendored
Normal file
15
test/fixtures/transformation/es6-tail-call/ignore-reassigned/expected.js
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// we need to deopt `test` if it's reassigned as we can't be certain of it's
|
||||||
|
// state, ie. it could have been rebound or dereferenced
|
||||||
|
|
||||||
|
function test(exit) {
|
||||||
|
if (exit) {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
return test(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
test = test.bind({ x: "yay" });
|
||||||
|
|
||||||
|
console.log(test());
|
||||||
Loading…
x
Reference in New Issue
Block a user