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;
|
||||
})()
|
||||
Reference in New Issue
Block a user