From e87ef80bc5cbdc31a7a83663eba7b13f154e0e08 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 20 Feb 2015 11:34:00 +1100 Subject: [PATCH] add back named methods for classes and add param binding check to spec.functionName transformer --- .../transformation/helpers/name-method.js | 53 +++++++++++++++++++ .../transformers/es6/classes.js | 3 ++ .../transformers/spec/function-name.js | 38 +++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 lib/babel/transformation/helpers/name-method.js diff --git a/lib/babel/transformation/helpers/name-method.js b/lib/babel/transformation/helpers/name-method.js new file mode 100644 index 0000000000..05cbcf1764 --- /dev/null +++ b/lib/babel/transformation/helpers/name-method.js @@ -0,0 +1,53 @@ +"use strict"; + +var util = require("../../util"); +var t = require("../../types"); + +var visitor = { + enter: function (node, parent, scope, state) { + // check if this node is an identifier that matches the same as our function id + if (!t.isIdentifier(node, { name: state.id })) return; + + // check if this node is the one referenced + if (!t.isReferenced(node, parent)) return; + + // check that we don't have a local variable declared as that removes the need + // for the wrapper + var localDeclar = scope.getBindingIdentifier(state.id); + if (localDeclar !== state.outerDeclar) return; + + state.selfReference = true; + this.stop(); + } +}; + +exports.property = function (node, file, scope) { + var key = t.toComputedKey(node, node.key); + if (!t.isLiteral(key)) return node; // we can't set a function id with this + + var id = t.toIdentifier(key.value); + key = t.identifier(id); + + var state = { + id: id, + selfReference: false, + outerDeclar: scope.getBindingIdentifier(id), + }; + + scope.traverse(node, visitor, state); + + var method = node.value; + + if (state.selfReference) { + var templateName = "property-method-assignment-wrapper"; + if (method.generator) templateName += "-generator"; + node.value = util.template(templateName, { + FUNCTION: method, + FUNCTION_ID: key, + FUNCTION_KEY: scope.generateUidIdentifier(id), + WRAPPER_KEY: scope.generateUidIdentifier(id + "Wrapper") + }); + } else { + method.id = key; + } +}; diff --git a/lib/babel/transformation/transformers/es6/classes.js b/lib/babel/transformation/transformers/es6/classes.js index 2dfcf42b56..6d1705bffa 100644 --- a/lib/babel/transformation/transformers/es6/classes.js +++ b/lib/babel/transformation/transformers/es6/classes.js @@ -1,6 +1,7 @@ "use strict"; var ReplaceSupers = require("../../helpers/replace-supers"); +var nameMethod = require("../../helpers/name-method"); var defineMap = require("../../helpers/define-map"); var messages = require("../../../messages"); var util = require("../../../util"); @@ -216,6 +217,8 @@ ClassTransformer.prototype.pushMethod = function (node) { var kind = node.kind; if (kind === "") { + nameMethod.property(node, this.file, this.scope); + if (this.isLoose) { // use assignments instead of define properties for loose classes diff --git a/lib/babel/transformation/transformers/spec/function-name.js b/lib/babel/transformation/transformers/spec/function-name.js index 40d57407da..8e3d3a0e93 100644 --- a/lib/babel/transformation/transformers/spec/function-name.js +++ b/lib/babel/transformation/transformers/spec/function-name.js @@ -27,6 +27,44 @@ exports.FunctionExpression = function (node, parent, scope, file) { return; } + if (!t.isIdentifier(id)) return; + + // 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(id.name); + + if (bindingInfo) { + if (bindingInfo.type === "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 + } 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 + node.id = id; + } + + return; + } + + // + var binding = scope.getBindingIdentifier(id.name); var outerId, selfGlobalId;