diff --git a/src/babel/transformation/transformers/es7/function-bind.js b/src/babel/transformation/transformers/es7/function-bind.js index 08e53b6d5a..ae9f63d558 100644 --- a/src/babel/transformation/transformers/es7/function-bind.js +++ b/src/babel/transformation/transformers/es7/function-bind.js @@ -10,11 +10,23 @@ export var metadata = { function getTempId(scope) { var id = scope.path.getData("functionBind"); if (id) return id; + id = scope.generateTemp("context"); return scope.path.setData("functionBind", id); } +function getStaticContext(bind, scope) { + if (bind.object) { + return scope.isStatic(bind.object) && bind.object; + } else { + return scope.isStatic(bind.callee.object) && bind.callee.object; + } +} + function inferBindContext(bind, scope) { + var staticContext = getStaticContext(bind, scope); + if (staticContext) return staticContext; + var tempId = getTempId(scope); if (bind.object) { bind.callee = t.sequenceExpression([ @@ -30,6 +42,7 @@ function inferBindContext(bind, scope) { export function CallExpression(node, parent, scope, file) { var bind = node.callee; if (!t.isBindExpression(bind)) return; + var context = inferBindContext(bind, scope); node.callee = t.memberExpression(bind.callee, t.identifier("call")); node.arguments.unshift(context); diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index 138ebfd8b4..e77d9efb86 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -263,18 +263,34 @@ export default class Scope { return this.generateUidIdentifier(id); } + /** + * Determine whether evaluating the specific input `node` is a consequenceless reference. ie. + * evaluating it wont result in potentially arbitrary code from being ran. The following are + * whitelisted and determined not cause side effects: + * + * - `this` expressions + * - `super` expressions + * - Bound identifiers + */ + + isStatic(node: Object): boolean { + if (t.isThisExpression(node) || t.isSuper(node)) { + return true; + } + + if (t.isIdentifier(node) && this.hasBinding(node.name)) { + return true; + } + + return false; + } + /** * Description */ generateMemoisedReference(node: Object, dontPush?: boolean): ?Object { - if (t.isThisExpression(node) || t.isSuper(node)) { - return null; - } - - if (t.isIdentifier(node) && this.hasBinding(node.name)) { - return null; - } + if (this.isStatic(node)) return null; var id = this.generateUidBasedOnNode(node); if (!dontPush) this.push({ id }); diff --git a/test/core/fixtures/transformation/es7.function-bind/static-contexts/actual.js b/test/core/fixtures/transformation/es7.function-bind/static-contexts/actual.js new file mode 100644 index 0000000000..43bb7c7074 --- /dev/null +++ b/test/core/fixtures/transformation/es7.function-bind/static-contexts/actual.js @@ -0,0 +1,5 @@ +var bar = function () {}; +foo::bar; + +var foo = {}; +::foo.bar; diff --git a/test/core/fixtures/transformation/es7.function-bind/static-contexts/expected.js b/test/core/fixtures/transformation/es7.function-bind/static-contexts/expected.js new file mode 100644 index 0000000000..4f95b6ffa9 --- /dev/null +++ b/test/core/fixtures/transformation/es7.function-bind/static-contexts/expected.js @@ -0,0 +1,7 @@ +"use strict"; + +var bar = function bar() {}; +bar.bind(foo); + +var foo = {}; +foo.bar.bind(foo);