diff --git a/lib/babel/transformation/helpers/name-method.js b/lib/babel/transformation/helpers/name-method.js index 5b02942ef0..3304c74092 100644 --- a/lib/babel/transformation/helpers/name-method.js +++ b/lib/babel/transformation/helpers/name-method.js @@ -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); }; diff --git a/lib/babel/transformation/templates/named-func.js b/lib/babel/transformation/templates/named-func.js deleted file mode 100644 index 85ada1748f..0000000000 --- a/lib/babel/transformation/templates/named-func.js +++ /dev/null @@ -1,7 +0,0 @@ -(function () { - function GET_OUTER_ID() { - return ID; - } - - return FUNCTION; -})() \ No newline at end of file diff --git a/lib/babel/transformation/templates/named-function.js b/lib/babel/transformation/templates/named-function.js new file mode 100644 index 0000000000..4368fcfe58 --- /dev/null +++ b/lib/babel/transformation/templates/named-function.js @@ -0,0 +1,7 @@ +(function () { + function GET_OUTER_ID() { + return FUNCTION_ID; + } + + return FUNCTION; +})() diff --git a/test/fixtures/transformation/es6-parameters.default/multiple/actual.js b/test/fixtures/transformation/es6-parameters.default/multiple/actual.js index 98694446ff..c5c20ad411 100644 --- a/test/fixtures/transformation/es6-parameters.default/multiple/actual.js +++ b/test/fixtures/transformation/es6-parameters.default/multiple/actual.js @@ -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; }; diff --git a/test/fixtures/transformation/es6-parameters.default/multiple/expected.js b/test/fixtures/transformation/es6-parameters.default/multiple/expected.js index b894cb32cd..60b1aa031e 100644 --- a/test/fixtures/transformation/es6-parameters.default/multiple/expected.js +++ b/test/fixtures/transformation/es6-parameters.default/multiple/expected.js @@ -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; }; diff --git a/test/fixtures/transformation/es6-parameters.default/single/actual.js b/test/fixtures/transformation/es6-parameters.default/single/actual.js index 7d89e2e035..03030ea9af 100644 --- a/test/fixtures/transformation/es6-parameters.default/single/actual.js +++ b/test/fixtures/transformation/es6-parameters.default/single/actual.js @@ -1,3 +1,3 @@ -var t = function (t = "foo") { - return t + " bar"; +var t = function (f = "foo") { + return f + " bar"; }; diff --git a/test/fixtures/transformation/es6-parameters.default/single/expected.js b/test/fixtures/transformation/es6-parameters.default/single/expected.js index fb12b54cf5..0657743b0d 100644 --- a/test/fixtures/transformation/es6-parameters.default/single/expected.js +++ b/test/fixtures/transformation/es6-parameters.default/single/expected.js @@ -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"; }; diff --git a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/actual.js b/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/actual.js deleted file mode 100644 index bc8219323e..0000000000 --- a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/actual.js +++ /dev/null @@ -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); - } -}; diff --git a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/expected.js b/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/expected.js deleted file mode 100644 index 626881c7c8..0000000000 --- a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/expected.js +++ /dev/null @@ -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); - }) -}; diff --git a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/options.json b/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/options.json deleted file mode 100644 index e9accc85db..0000000000 --- a/test/fixtures/transformation/es6-properties.shorthand/method-self-reference/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "blacklist": ["es6.tailCall"] -} \ No newline at end of file diff --git a/test/fixtures/transformation/spec-function-name/actual.js b/test/fixtures/transformation/spec-function-name/actual.js deleted file mode 100644 index ee9d714601..0000000000 --- a/test/fixtures/transformation/spec-function-name/actual.js +++ /dev/null @@ -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(); -}; diff --git a/test/fixtures/transformation/spec-function-name/all/actual.js b/test/fixtures/transformation/spec-function-name/all/actual.js new file mode 100644 index 0000000000..540cdd2db6 --- /dev/null +++ b/test/fixtures/transformation/spec-function-name/all/actual.js @@ -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; +}; diff --git a/test/fixtures/transformation/spec-function-name/all/expected.js b/test/fixtures/transformation/spec-function-name/all/expected.js new file mode 100644 index 0000000000..f13ce1a6b9 --- /dev/null +++ b/test/fixtures/transformation/spec-function-name/all/expected.js @@ -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; +}; diff --git a/test/fixtures/transformation/spec-function-name/expected.js b/test/fixtures/transformation/spec-function-name/expected.js deleted file mode 100644 index 3faa582c9e..0000000000 --- a/test/fixtures/transformation/spec-function-name/expected.js +++ /dev/null @@ -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(); -}; diff --git a/test/fixtures/transformation/spec-function-name/options.json b/test/fixtures/transformation/spec-function-name/options.json deleted file mode 100644 index e9c4354c1b..0000000000 --- a/test/fixtures/transformation/spec-function-name/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "whitelist": ["spec.functionName"] -}