diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 8abc0cf98d..f6b067e211 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -85,6 +85,7 @@ export default class File { "get", "set", "class-call-check", + "new-arrow-check", "object-destructuring-empty", "temporal-undefined", "temporal-assert-defined", diff --git a/src/babel/transformation/templates/helper-new-arrow-check.js b/src/babel/transformation/templates/helper-new-arrow-check.js new file mode 100644 index 0000000000..a6e9a64308 --- /dev/null +++ b/src/babel/transformation/templates/helper-new-arrow-check.js @@ -0,0 +1,5 @@ +(function (instance, arrowFn) { + if (instance instanceof arrowFn) { + throw new TypeError("Cannot instantiate an arrow function"); + } +}); diff --git a/src/babel/transformation/transformers/es6/arrow-functions.js b/src/babel/transformation/transformers/es6/arrow-functions.js index 39d93f829a..fdbc655231 100644 --- a/src/babel/transformation/transformers/es6/arrow-functions.js +++ b/src/babel/transformation/transformers/es6/arrow-functions.js @@ -5,5 +5,5 @@ export function ArrowFunctionExpression(node) { node.expression = false; node.type = "FunctionExpression"; - node.shadow = true; + node.shadow = node.shadow || true; } diff --git a/src/babel/transformation/transformers/es6/spec.arrow-functions.js b/src/babel/transformation/transformers/es6/spec.arrow-functions.js new file mode 100644 index 0000000000..5a5b07d595 --- /dev/null +++ b/src/babel/transformation/transformers/es6/spec.arrow-functions.js @@ -0,0 +1,26 @@ +import * as t from "../../../types"; + +export var metadata = { + optional: true +}; + +export function ArrowFunctionExpression(node, parent, scope, file) { + if (node.shadow) return; + node.shadow = { this: false }; + + var {id} = node; + var expr = node; + + if (!id) { + id = scope.parent.generateDeclaredUidIdentifier("arrow"); + expr = t.assignmentExpression("=", id, expr); + } + + // make sure that arrow function won't be instantiated + t.ensureBlock(node).body.unshift(t.expressionStatement(t.callExpression(file.addHelper("new-arrow-check"), [ + t.thisExpression(), + id + ]))); + + return t.callExpression(t.memberExpression(expr, t.identifier("bind")), [t.thisExpression()]); +} diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 903bd084b1..6bff7a45c6 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -26,6 +26,7 @@ export default { "es7.decorators": require("./es7/decorators"), "validation.undeclaredVariableCheck": require("./validation/undeclared-variable-check"), "validation.react": require("./validation/react"), + "es6.spec.arrowFunctions": require("./es6/spec.arrow-functions"), "es6.arrowFunctions": require("./es6/arrow-functions"), "spec.blockScopedFunctions": require("./spec/block-scoped-functions"), "optimisation.react.constantElements": require("./optimisation/react.constant-elements"), diff --git a/src/babel/transformation/transformers/internal/shadow-functions.js b/src/babel/transformation/transformers/internal/shadow-functions.js index c01469eb84..17b3810e14 100644 --- a/src/babel/transformation/transformers/internal/shadow-functions.js +++ b/src/babel/transformation/transformers/internal/shadow-functions.js @@ -6,7 +6,7 @@ export var metadata = { function remap(path, key, create) { // ensure that we're shadowed - if (!path.inShadow()) return; + if (!path.inShadow(key)) return; var fnPath = path.findParent((path) => !path.is("shadow") && (path.isFunction() || path.isProgram())); @@ -22,8 +22,10 @@ function remap(path, key, create) { return id; } -export function ThisExpression() { - return remap(this, "this", () => t.thisExpression()); +export function ThisExpression(node) { + if (!node._shadowedFunctionLiteral) { + return remap(this, "this", () => t.thisExpression()); + } } export function ReferencedIdentifier(node) { diff --git a/src/babel/traversal/path/ancestry.js b/src/babel/traversal/path/ancestry.js index 5077a5ca66..61893d71c2 100644 --- a/src/babel/traversal/path/ancestry.js +++ b/src/babel/traversal/path/ancestry.js @@ -62,11 +62,12 @@ export function inType(types) { * Description */ -export function inShadow() { +export function inShadow(key) { var path = this; while (path) { if (path.isFunction()) { - if (path.node.shadow) { + var {shadow} = path.node; + if (shadow && (shadow === true || shadow[key] !== false)) { return path; } else { return null; diff --git a/test/core/fixtures/transformation/es6.arrow-functions/spec/actual.js b/test/core/fixtures/transformation/es6.arrow-functions/spec/actual.js new file mode 100644 index 0000000000..777f5373be --- /dev/null +++ b/test/core/fixtures/transformation/es6.arrow-functions/spec/actual.js @@ -0,0 +1,5 @@ +arr.map(x => x * x); +var f = (x, y) => x * y; +(function () { + return () => this; +})(); diff --git a/test/core/fixtures/transformation/es6.arrow-functions/spec/expected.js b/test/core/fixtures/transformation/es6.arrow-functions/spec/expected.js new file mode 100644 index 0000000000..3b11cfbb65 --- /dev/null +++ b/test/core/fixtures/transformation/es6.arrow-functions/spec/expected.js @@ -0,0 +1,25 @@ +"use strict"; + +var _arrow; + +function _newArrowCheck(instance, arrowFn) { if (instance instanceof arrowFn) { throw new TypeError("Cannot instantiate an arrow function"); } } + +arr.map((_arrow = function (x) { + _newArrowCheck(this, _arrow); + + return x * x; +}).bind(this)); +var f = (function f(x, y) { + _newArrowCheck(this, f); + + return x * y; +}).bind(this); +(function () { + var _arrow2; + + return (_arrow2 = function () { + _newArrowCheck(this, _arrow2); + + return this; + }).bind(this); +})(); diff --git a/test/core/fixtures/transformation/es6.arrow-functions/spec/options.json b/test/core/fixtures/transformation/es6.arrow-functions/spec/options.json new file mode 100644 index 0000000000..18c0abf7f1 --- /dev/null +++ b/test/core/fixtures/transformation/es6.arrow-functions/spec/options.json @@ -0,0 +1,3 @@ +{ + "optional": ["es6.spec.arrowFunctions"] +}