optimise named functions depending on whether they contain an assignment/reference - #861

This commit is contained in:
Sebastian McKenzie
2015-02-22 21:35:08 +11:00
parent ab6bb35a4f
commit f2d60aab9e
15 changed files with 225 additions and 187 deletions

View File

@@ -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);
};

View File

@@ -1,7 +0,0 @@
(function () {
function GET_OUTER_ID() {
return ID;
}
return FUNCTION;
})()

View File

@@ -0,0 +1,7 @@
(function () {
function GET_OUTER_ID() {
return FUNCTION_ID;
}
return FUNCTION;
})()

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -1,3 +1,3 @@
var t = function (t = "foo") {
return t + " bar";
var t = function (f = "foo") {
return f + " bar";
};

View File

@@ -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";
};

View File

@@ -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);
}
};

View File

@@ -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);
})
};

View File

@@ -1,3 +0,0 @@
{
"blacklist": ["es6.tailCall"]
}

View File

@@ -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();
};

View 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;
};

View 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;
};

View File

@@ -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();
};

View File

@@ -1,3 +0,0 @@
{
"whitelist": ["spec.functionName"]
}