Merge pull request #1518 from babel/es7.functionBind
Add experimental support for ES7 function bind.
This commit is contained in:
@@ -214,7 +214,12 @@ pp.parseExprSubscripts = function(refShorthandDefaultPos) {
|
||||
}
|
||||
|
||||
pp.parseSubscripts = function(base, start, noCalls) {
|
||||
if (this.eat(tt.dot)) {
|
||||
if (!noCalls && this.eat(tt.doubleColon)) {
|
||||
let node = this.startNodeAt(start)
|
||||
node.object = base
|
||||
node.callee = this.parseNoCallExpr()
|
||||
return this.parseSubscripts(this.finishNode(node, "BindExpression"), start, noCalls)
|
||||
} else if (this.eat(tt.dot)) {
|
||||
let node = this.startNodeAt(start)
|
||||
node.object = base
|
||||
node.property = this.parseIdent(true)
|
||||
@@ -240,6 +245,13 @@ pp.parseSubscripts = function(base, start, noCalls) {
|
||||
} return base
|
||||
}
|
||||
|
||||
// Parse a no-call expression (like argument of `new` or `::` operators).
|
||||
|
||||
pp.parseNoCallExpr = function() {
|
||||
let start = this.markPosition()
|
||||
return this.parseSubscripts(this.parseExprAtom(), start, true)
|
||||
}
|
||||
|
||||
// Parse an atomic expression — either a single token that is an
|
||||
// expression, an expression started by a keyword like `function` or
|
||||
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
|
||||
@@ -363,6 +375,15 @@ pp.parseExprAtom = function(refShorthandDefaultPos) {
|
||||
case tt.backQuote:
|
||||
return this.parseTemplate()
|
||||
|
||||
case tt.doubleColon:
|
||||
node = this.startNode()
|
||||
this.next()
|
||||
node.object = null
|
||||
let callee = node.callee = this.parseNoCallExpr()
|
||||
if (callee.type !== "MemberExpression")
|
||||
this.raise(callee.start, "Binding should be performed on object property.")
|
||||
return this.finishNode(node, "BindExpression")
|
||||
|
||||
default:
|
||||
this.unexpected()
|
||||
}
|
||||
@@ -472,8 +493,7 @@ pp.parseNew = function() {
|
||||
this.raise(node.property.start, "The only valid meta property for new is new.target")
|
||||
return this.finishNode(node, "MetaProperty")
|
||||
}
|
||||
let start = this.markPosition()
|
||||
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true)
|
||||
node.callee = this.parseNoCallExpr()
|
||||
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(
|
||||
tt.parenR,
|
||||
this.options.features["es7.trailingFunctionCommas"]
|
||||
|
||||
@@ -320,7 +320,13 @@ pp.getTokenFromCode = function(code) {
|
||||
case 93: ++this.pos; return this.finishToken(tt.bracketR)
|
||||
case 123: ++this.pos; return this.finishToken(tt.braceL)
|
||||
case 125: ++this.pos; return this.finishToken(tt.braceR)
|
||||
case 58: ++this.pos; return this.finishToken(tt.colon)
|
||||
|
||||
case 58:
|
||||
if (this.options.features["es7.functionBind"] && this.input.charCodeAt(this.pos + 1) === 58)
|
||||
return this.finishOp(tt.doubleColon, 2)
|
||||
++this.pos
|
||||
return this.finishToken(tt.colon)
|
||||
|
||||
case 63: ++this.pos; return this.finishToken(tt.question)
|
||||
case 64: ++this.pos; return this.finishToken(tt.at)
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export const types = {
|
||||
comma: new TokenType(",", beforeExpr),
|
||||
semi: new TokenType(";", beforeExpr),
|
||||
colon: new TokenType(":", beforeExpr),
|
||||
doubleColon: new TokenType("::", beforeExpr),
|
||||
dot: new TokenType("."),
|
||||
question: new TokenType("?", beforeExpr),
|
||||
arrow: new TokenType("=>", beforeExpr),
|
||||
|
||||
@@ -133,6 +133,12 @@ export function AssignmentExpression(node, print) {
|
||||
print(node.right);
|
||||
}
|
||||
|
||||
export function BindExpression(node, print) {
|
||||
print(node.object);
|
||||
this.push("::");
|
||||
print(node.callee);
|
||||
}
|
||||
|
||||
export {
|
||||
AssignmentExpression as BinaryExpression,
|
||||
AssignmentExpression as LogicalExpression,
|
||||
|
||||
@@ -72,4 +72,10 @@ def("ExportAllDeclaration")
|
||||
.field("exported", def("Identifier"))
|
||||
.field("source", def("Literal"));
|
||||
|
||||
def("BindExpression")
|
||||
.bases("Expression")
|
||||
.build("object", "callee")
|
||||
.field("object", or(def("Expression"), null))
|
||||
.field("callee", def("Expression"));
|
||||
|
||||
types.finalize();
|
||||
|
||||
41
src/babel/transformation/transformers/es7/function-bind.js
Normal file
41
src/babel/transformation/transformers/es7/function-bind.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// https://github.com/zenparsing/es-function-bind
|
||||
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var metadata = {
|
||||
optional: true,
|
||||
stage: 0
|
||||
};
|
||||
|
||||
function getTempId(scope) {
|
||||
var id = scope.path.getData("functionBind");
|
||||
if (id) return id;
|
||||
id = scope.generateTemp("context");
|
||||
return scope.path.setData("functionBind", id);
|
||||
}
|
||||
|
||||
function inferBindContext(bind, scope) {
|
||||
var tempId = getTempId(scope);
|
||||
if (bind.object) {
|
||||
bind.callee = t.sequenceExpression([
|
||||
t.assignmentExpression("=", tempId, bind.object),
|
||||
bind.callee
|
||||
]);
|
||||
} else {
|
||||
bind.callee.object = t.assignmentExpression("=", tempId, bind.callee.object);
|
||||
}
|
||||
return tempId;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
export function BindExpression(node, parent, scope, file) {
|
||||
var context = inferBindContext(node, scope);
|
||||
return t.callExpression(t.memberExpression(node.callee, t.identifier("bind")), [context]);
|
||||
}
|
||||
@@ -51,6 +51,7 @@ export default {
|
||||
"spec.protoToAssign": require("./spec/proto-to-assign"),
|
||||
"es7.doExpressions": require("./es7/do-expressions"),
|
||||
"es6.spec.symbols": require("./es6/spec.symbols"),
|
||||
"es7.functionBind": require("./es7/function-bind"),
|
||||
"spec.undefinedToVoid": require("./spec/undefined-to-void"),
|
||||
jscript: require("./other/jscript"),
|
||||
flow: require("./other/flow"),
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
"right": null
|
||||
},
|
||||
|
||||
"BindExpression": {
|
||||
"object": null,
|
||||
"callee": null
|
||||
},
|
||||
|
||||
"BlockStatement": {
|
||||
"body": null
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"AssignmentPattern": ["left", "right"],
|
||||
"AwaitExpression": ["argument"],
|
||||
"BinaryExpression": ["left", "right"],
|
||||
"BindExpression": ["object", "callee"],
|
||||
"BlockStatement": ["body"],
|
||||
"BreakStatement": ["label"],
|
||||
"CallExpression": ["callee", "arguments"],
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
::foo.bar.foo;
|
||||
::foo.bar["foo"];
|
||||
|
||||
ctx::foo.bar.foo;
|
||||
ctx::foo.bar["foo"];
|
||||
@@ -0,0 +1,5 @@
|
||||
::foo.bar.foo;
|
||||
::foo.bar["foo"];
|
||||
|
||||
ctx::foo.bar.foo;
|
||||
ctx::foo.bar["foo"];
|
||||
@@ -0,0 +1,3 @@
|
||||
var f = ctx::ns.obj.func;
|
||||
var g = ::ns.obj.func;
|
||||
var h = new X::y;
|
||||
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var _context;
|
||||
|
||||
var f = (_context = ctx, ns.obj.func).bind(_context);
|
||||
var g = (_context = ns.obj).func.bind(_context);
|
||||
var h = (_context = new X(), y).bind(_context);
|
||||
@@ -0,0 +1,4 @@
|
||||
ctx::ns.obj.func();
|
||||
::ns.obj.func();
|
||||
|
||||
ns.obj2::ns.obj1.func();
|
||||
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
var _context;
|
||||
|
||||
(_context = ctx, ns.obj.func).call(_context);
|
||||
(_context = ns.obj).func.call(_context);
|
||||
|
||||
(_context = ns.obj2, ns.obj1.func).call(_context);
|
||||
@@ -0,0 +1,6 @@
|
||||
import { map, takeWhile, forEach } from "iterlib";
|
||||
|
||||
getPlayers()
|
||||
::map(x => x.character())
|
||||
::takeWhile(x => x.strength > 100)
|
||||
::forEach(x => console.log(x));
|
||||
@@ -0,0 +1,27 @@
|
||||
var operations = [];
|
||||
|
||||
var lib = {};
|
||||
|
||||
for (let key of ['f', 'g', 'h']) {
|
||||
let func = () => operations.push(`lib.${key}()`);
|
||||
Object.defineProperty(lib, key, {
|
||||
get() {
|
||||
operations.push(`get lib.${key}`);
|
||||
return func;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
({prop:'value'})
|
||||
::lib.f()
|
||||
::lib.g()
|
||||
::lib.h();
|
||||
|
||||
assert.deepEqual(operations, [
|
||||
'get lib.f',
|
||||
'lib.f()',
|
||||
'get lib.g',
|
||||
'lib.g()',
|
||||
'get lib.h',
|
||||
'lib.h()'
|
||||
]);
|
||||
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
var _context;
|
||||
|
||||
var _iterlib = require("iterlib");
|
||||
|
||||
(_context = (_context = (_context = getPlayers(), _iterlib.map).call(_context, function (x) {
|
||||
return x.character();
|
||||
}), _iterlib.takeWhile).call(_context, function (x) {
|
||||
return x.strength > 100;
|
||||
}), _iterlib.forEach).call(_context, function (x) {
|
||||
return console.log(x);
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"optional": "es7.functionBind"
|
||||
}
|
||||
@@ -34,7 +34,8 @@ _.each(helper.get("generation"), function (testSuite) {
|
||||
features: {
|
||||
"es7.comprehensions": true,
|
||||
"es7.asyncFunctions": true,
|
||||
"es7.exportExtensions": true
|
||||
"es7.exportExtensions": true,
|
||||
"es7.functionBind": true
|
||||
}
|
||||
});
|
||||
var actualCode = generate(actualAst, task.options, actual.code).code;
|
||||
|
||||
Reference in New Issue
Block a user