optimise named functions depending on whether they contain an assignment/reference - #861
This commit is contained in:
@@ -15,11 +15,32 @@ var visitor = {
|
||||
if (localDeclar !== state.outerDeclar) return;
|
||||
|
||||
state.selfReference = true;
|
||||
this.stop();
|
||||
state.references.push(this);
|
||||
|
||||
if (t.isPattern(parent) || t.isAssignmentExpression(parent) || t.isUpdateExpression(parent)) {
|
||||
state.selfAssignment = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var wrap = function (method, id, scope) {
|
||||
var wrapIncludesSelfReference = function (state, method, id, scope) {
|
||||
var outerId = scope.generateUidIdentifier("getOuter");
|
||||
var outerIdCall = t.callExpression(outerId, []);
|
||||
|
||||
for (var i = 0; i < state.references.length; i++) {
|
||||
state.references[i].replaceNode(outerIdCall);
|
||||
}
|
||||
|
||||
method.id = id;
|
||||
|
||||
return util.template("named-function", {
|
||||
GET_OUTER_ID: outerId,
|
||||
FUNCTION_ID: id,
|
||||
FUNCTION: method
|
||||
});
|
||||
};
|
||||
|
||||
var wrapIncludesSelfAssignment = function (state, method, id, scope) {
|
||||
var templateName = "property-method-assignment-wrapper";
|
||||
if (method.generator) templateName += "-generator";
|
||||
return util.template(templateName, {
|
||||
@@ -30,23 +51,68 @@ var wrap = function (method, id, scope) {
|
||||
});
|
||||
};
|
||||
|
||||
var wrap = function (state, method, id, scope) {
|
||||
if (state.selfReference) {
|
||||
if (state.selfAssignment) {
|
||||
return wrapIncludesSelfAssignment(state, method, id, scope);
|
||||
} else {
|
||||
return wrapIncludesSelfReference(state, method, id, scope);
|
||||
}
|
||||
} else {
|
||||
method.id = id;
|
||||
return method;
|
||||
}
|
||||
};
|
||||
|
||||
var visit = function (node, name, scope) {
|
||||
var state = {
|
||||
name: name,
|
||||
selfReference: false,
|
||||
outerDeclar: scope.getBindingIdentifier(name),
|
||||
selfAssignment: false,
|
||||
selfReference: false,
|
||||
outerDeclar: scope.getBindingIdentifier(name),
|
||||
references: [],
|
||||
name: name,
|
||||
};
|
||||
|
||||
if (doesntHaveLocal(name, scope)) {
|
||||
// check to see if we have a local binding of the id we're setting inside of
|
||||
// the function, this is important as there are caveats associated
|
||||
|
||||
var bindingInfo = scope.getOwnBindingInfo(name);
|
||||
|
||||
if (bindingInfo) {
|
||||
if (bindingInfo.kind === "param") {
|
||||
// safari will blow up in strict mode with code like:
|
||||
//
|
||||
// var t = function t(t) {};
|
||||
//
|
||||
// with the error:
|
||||
//
|
||||
// Cannot declare a parameter named 't' as it shadows the name of a
|
||||
// strict mode function.
|
||||
//
|
||||
// this isn't to the spec and they've invented this behaviour which is
|
||||
// **extremely** annoying so we avoid setting the name if it has a param
|
||||
// with the same id
|
||||
state.selfAssignment = true;
|
||||
state.selfReference = true;
|
||||
} else {
|
||||
// otherwise it's defined somewhere in scope like:
|
||||
//
|
||||
// var t = function () {
|
||||
// var t = 2;
|
||||
// };
|
||||
//
|
||||
// so we can safely just set the id and move along as it shadows the
|
||||
// bound function id
|
||||
}
|
||||
} else {
|
||||
scope.traverse(node, visitor, state);
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
var doesntHaveLocal = function (name, scope) {
|
||||
var bindingInfo = scope.getOwnBindingInfo(name);
|
||||
return !bindingInfo || bindingInfo.kind !== "param";
|
||||
var hasParamOfSameName = function (name, scope) {
|
||||
return bindingInfo && bindingInfo.kind === "param";
|
||||
};
|
||||
|
||||
exports.property = function (node, file, scope) {
|
||||
@@ -58,12 +124,7 @@ exports.property = function (node, file, scope) {
|
||||
|
||||
var method = node.value;
|
||||
var state = visit(method, name, scope);
|
||||
|
||||
if (state.selfReference) {
|
||||
node.value = wrap(method, id, scope);
|
||||
} else {
|
||||
method.id = id;
|
||||
}
|
||||
node.value = wrap(state, method, id, scope);
|
||||
};
|
||||
|
||||
exports.bare = function (node, parent, scope) {
|
||||
@@ -86,13 +147,6 @@ exports.bare = function (node, parent, scope) {
|
||||
var name = t.toIdentifier(id.name);
|
||||
id = t.identifier(name);
|
||||
|
||||
//
|
||||
|
||||
var state = visit(node, name, scope);
|
||||
|
||||
if (state.selfReference) {
|
||||
return wrap(node, id, scope);
|
||||
} else {
|
||||
node.id = id;
|
||||
}
|
||||
return wrap(state, node, id, scope);
|
||||
};
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
(function () {
|
||||
function GET_OUTER_ID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
return FUNCTION;
|
||||
})()
|
||||
7
lib/babel/transformation/templates/named-function.js
Normal file
7
lib/babel/transformation/templates/named-function.js
Normal file
@@ -0,0 +1,7 @@
|
||||
(function () {
|
||||
function GET_OUTER_ID() {
|
||||
return FUNCTION_ID;
|
||||
}
|
||||
|
||||
return FUNCTION;
|
||||
})()
|
||||
@@ -1,7 +1,7 @@
|
||||
var t = function (t = "foo", f = 5) {
|
||||
return t + " bar " + f;
|
||||
var t = function (e = "foo", f = 5) {
|
||||
return e + " bar " + f;
|
||||
};
|
||||
|
||||
var a = function (t, f = 5) {
|
||||
return t + " bar " + f;
|
||||
var a = function (e, f = 5) {
|
||||
return e + " bar " + f;
|
||||
};
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
var t = function t() {
|
||||
var t = arguments[0] === undefined ? "foo" : arguments[0];
|
||||
var e = arguments[0] === undefined ? "foo" : arguments[0];
|
||||
var f = arguments[1] === undefined ? 5 : arguments[1];
|
||||
|
||||
return t + " bar " + f;
|
||||
return e + " bar " + f;
|
||||
};
|
||||
|
||||
var a = function a(t) {
|
||||
var a = function a(e) {
|
||||
var f = arguments[1] === undefined ? 5 : arguments[1];
|
||||
|
||||
return t + " bar " + f;
|
||||
return e + " bar " + f;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
var t = function (t = "foo") {
|
||||
return t + " bar";
|
||||
var t = function (f = "foo") {
|
||||
return f + " bar";
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var t = function t() {
|
||||
var t = arguments[0] === undefined ? "foo" : arguments[0];
|
||||
var f = arguments[0] === undefined ? "foo" : arguments[0];
|
||||
|
||||
return t + " bar";
|
||||
return f + " bar";
|
||||
};
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
var bar = {
|
||||
foo() {
|
||||
console.log(foo);
|
||||
}
|
||||
};
|
||||
|
||||
var bar = {
|
||||
foo() {
|
||||
var foo = 41;
|
||||
console.log(foo);
|
||||
}
|
||||
};
|
||||
|
||||
var foobar = 123;
|
||||
var foobar2 = {
|
||||
foobar() {
|
||||
console.log(foobar);
|
||||
}
|
||||
};
|
||||
@@ -1,51 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var bar = {
|
||||
foo: (function (_foo) {
|
||||
var _fooWrapper = function foo() {
|
||||
return _foo.apply(this, arguments);
|
||||
};
|
||||
|
||||
_fooWrapper.toString = function () {
|
||||
return _foo.toString();
|
||||
};
|
||||
|
||||
return _fooWrapper;
|
||||
})(function () {
|
||||
console.log(foo);
|
||||
})
|
||||
};
|
||||
|
||||
var bar = {
|
||||
foo: (function (_foo) {
|
||||
var _fooWrapper = function foo() {
|
||||
return _foo.apply(this, arguments);
|
||||
};
|
||||
|
||||
_fooWrapper.toString = function () {
|
||||
return _foo.toString();
|
||||
};
|
||||
|
||||
return _fooWrapper;
|
||||
})(function () {
|
||||
var foo = 41;
|
||||
console.log(foo);
|
||||
})
|
||||
};
|
||||
|
||||
var foobar = 123;
|
||||
var foobar2 = {
|
||||
foobar: (function (_foobar) {
|
||||
var _foobarWrapper = function foobar() {
|
||||
return _foobar.apply(this, arguments);
|
||||
};
|
||||
|
||||
_foobarWrapper.toString = function () {
|
||||
return _foobar.toString();
|
||||
};
|
||||
|
||||
return _foobarWrapper;
|
||||
})(function () {
|
||||
console.log(foobar);
|
||||
})
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"blacklist": ["es6.tailCall"]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
var obj = {
|
||||
f: function () {
|
||||
(function f() {
|
||||
console.log(f);
|
||||
})();
|
||||
},
|
||||
|
||||
g: function () {
|
||||
console.log(g);
|
||||
},
|
||||
|
||||
h: function () {
|
||||
console.log(h);
|
||||
},
|
||||
|
||||
m: function () {
|
||||
doSmth();
|
||||
}
|
||||
};
|
||||
|
||||
var f = function () {
|
||||
console.log(f, g);
|
||||
};
|
||||
|
||||
var g = function () {
|
||||
doSmth();
|
||||
};
|
||||
48
test/fixtures/transformation/spec-function-name/all/actual.js
vendored
Normal file
48
test/fixtures/transformation/spec-function-name/all/actual.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
var obj = {
|
||||
// localy declared variable
|
||||
f: function () {
|
||||
(function f() {
|
||||
console.log(f);
|
||||
})();
|
||||
},
|
||||
|
||||
// self reference
|
||||
h: function () {
|
||||
console.log(h);
|
||||
},
|
||||
|
||||
// no reference
|
||||
m: function () {
|
||||
doSmth();
|
||||
}
|
||||
};
|
||||
|
||||
// locally declared variable
|
||||
var f = function () {
|
||||
var f = 2;
|
||||
};
|
||||
|
||||
// self reference
|
||||
var f = function () {
|
||||
console.log(f, g);
|
||||
};
|
||||
|
||||
// no reference
|
||||
var g = function () {
|
||||
doSmth();
|
||||
};
|
||||
|
||||
// param with the same name as id
|
||||
var h = function (h) {
|
||||
|
||||
};
|
||||
|
||||
// assignment to self
|
||||
var i = function () {
|
||||
i = 5;
|
||||
};
|
||||
|
||||
// assignment to self
|
||||
var j = function () {
|
||||
({ j }) = 5;
|
||||
};
|
||||
81
test/fixtures/transformation/spec-function-name/all/expected.js
vendored
Normal file
81
test/fixtures/transformation/spec-function-name/all/expected.js
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
|
||||
var obj = {
|
||||
// localy declared variable
|
||||
f: function f() {
|
||||
(function f() {
|
||||
console.log(f);
|
||||
})();
|
||||
},
|
||||
|
||||
// self reference
|
||||
h: (function () {
|
||||
function _getOuter() {
|
||||
return h;
|
||||
}
|
||||
|
||||
return function h() {
|
||||
console.log(_getOuter());
|
||||
};
|
||||
})(),
|
||||
|
||||
// no reference
|
||||
m: function m() {
|
||||
doSmth();
|
||||
}
|
||||
};
|
||||
|
||||
// locally declared variable
|
||||
var f = function f() {
|
||||
var f = 2;
|
||||
};
|
||||
|
||||
// self reference
|
||||
var f = (function () {
|
||||
function _getOuter() {
|
||||
return f;
|
||||
}
|
||||
|
||||
return function f() {
|
||||
console.log(_getOuter(), g);
|
||||
};
|
||||
})();
|
||||
|
||||
// no reference
|
||||
var g = function g() {
|
||||
doSmth();
|
||||
};
|
||||
|
||||
// param with the same name as id
|
||||
var h = (function (_h) {
|
||||
var _hWrapper = function h() {
|
||||
return _h.apply(this, arguments);
|
||||
};
|
||||
|
||||
_hWrapper.toString = function () {
|
||||
return _h.toString();
|
||||
};
|
||||
|
||||
return _hWrapper;
|
||||
})(function (h) {});
|
||||
|
||||
// assignment to self
|
||||
var i = (function (_i) {
|
||||
var _iWrapper = function i() {
|
||||
return _i.apply(this, arguments);
|
||||
};
|
||||
|
||||
_iWrapper.toString = function () {
|
||||
return _i.toString();
|
||||
};
|
||||
|
||||
return _iWrapper;
|
||||
})(function () {
|
||||
i = 5;
|
||||
});
|
||||
|
||||
// assignment to self
|
||||
var j = function j() {
|
||||
var _ref = 5;
|
||||
j = _ref.j;
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var _selfGlobal = typeof global === "undefined" ? self : global;
|
||||
var obj = {
|
||||
f: function f() {
|
||||
(function f() {
|
||||
console.log(f);
|
||||
})();
|
||||
},
|
||||
|
||||
g: (function () {
|
||||
function _getOuter() {
|
||||
return g;
|
||||
}
|
||||
|
||||
return function g() {
|
||||
console.log(_getOuter());
|
||||
};
|
||||
})(),
|
||||
|
||||
h: function h() {
|
||||
console.log(_selfGlobal.h);
|
||||
},
|
||||
|
||||
m: function m() {
|
||||
doSmth();
|
||||
}
|
||||
};
|
||||
|
||||
var f = (function () {
|
||||
function _getOuter() {
|
||||
return f;
|
||||
}
|
||||
|
||||
return function f() {
|
||||
console.log(_getOuter(), g);
|
||||
};
|
||||
})();
|
||||
|
||||
var g = function g() {
|
||||
doSmth();
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"whitelist": ["spec.functionName"]
|
||||
}
|
||||
Reference in New Issue
Block a user