Skip to content

Commit 8521632

Browse files
committed
Adds a prototype implementation of the bind ('::') operator
1 parent aaf0f78 commit 8521632

14 files changed

+636
-31
lines changed

src/compiler/checker.ts

+40-2
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ namespace ts {
158158
let emitParam = false;
159159
let emitAwaiter = false;
160160
let emitGenerator = false;
161+
let emitBind = false;
161162

162163
let resolutionTargets: Object[] = [];
163164
let resolutionResults: boolean[] = [];
@@ -6178,14 +6179,14 @@ namespace ts {
61786179
if (needToCaptureLexicalThis) {
61796180
captureLexicalThis(node, container);
61806181
}
6181-
6182+
61826183
if (isClassLike(container.parent)) {
61836184
let symbol = getSymbolOfNode(container.parent);
61846185
return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol);
61856186
}
61866187
return anyType;
61876188
}
6188-
6189+
61896190
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
61906191
for (let n = node; n && n !== constructorDecl; n = n.parent) {
61916192
if (n.kind === SyntaxKind.Parameter) {
@@ -8765,6 +8766,33 @@ namespace ts {
87658766
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
87668767
return getReturnTypeOfSignature(getResolvedSignature(node));
87678768
}
8769+
8770+
function checkBindExpression(node: BindExpression): Type {
8771+
emitBind = true;
8772+
8773+
if (!node.baseExpression
8774+
&& node.targetExpression.kind !== SyntaxKind.PropertyAccessExpression
8775+
&& node.targetExpression.kind !== SyntaxKind.ElementAccessExpression) {
8776+
error(node, Diagnostics.The_target_of_a_bind_expression_without_a_left_hand_side_must_be_a_property_or_element_access);
8777+
return unknownType;
8778+
}
8779+
8780+
let baseType = node.baseExpression ? checkExpression(node.baseExpression) : undefined;
8781+
let targetType = checkExpression(node.targetExpression);
8782+
let apparentType = getApparentType(targetType);
8783+
if (apparentType === unknownType) {
8784+
return unknownType;
8785+
}
8786+
8787+
let callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
8788+
let constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
8789+
if (!isTypeAny(targetType) && callSignatures.length === 0 && constructSignatures.length === 0) {
8790+
error(node, Diagnostics.Cannot_bind_an_expression_whose_type_lacks_a_call_or_construct_signature);
8791+
return unknownType;
8792+
}
8793+
8794+
return targetType;
8795+
}
87688796

87698797
function checkAssertion(node: AssertionExpression) {
87708798
let exprType = checkExpression(node.expression);
@@ -9813,6 +9841,8 @@ namespace ts {
98139841
case SyntaxKind.CallExpression:
98149842
case SyntaxKind.NewExpression:
98159843
return checkCallExpression(<CallExpression>node);
9844+
case SyntaxKind.BindExpression:
9845+
return checkBindExpression(<BindExpression>node);
98169846
case SyntaxKind.TaggedTemplateExpression:
98179847
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
98189848
case SyntaxKind.ParenthesizedExpression:
@@ -13235,6 +13265,7 @@ namespace ts {
1323513265
case SyntaxKind.ConditionalExpression:
1323613266
case SyntaxKind.SpreadElementExpression:
1323713267
case SyntaxKind.YieldExpression:
13268+
case SyntaxKind.BindExpression:
1323813269
case SyntaxKind.Block:
1323913270
case SyntaxKind.ModuleBlock:
1324013271
case SyntaxKind.VariableStatement:
@@ -13298,6 +13329,9 @@ namespace ts {
1329813329
emitExtends = false;
1329913330
emitDecorate = false;
1330013331
emitParam = false;
13332+
emitAwaiter = false;
13333+
emitGenerator = false;
13334+
emitBind = false;
1330113335
potentialThisCollisions.length = 0;
1330213336

1330313337
forEach(node.statements, checkSourceElement);
@@ -13331,6 +13365,10 @@ namespace ts {
1333113365
if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) {
1333213366
links.flags |= NodeCheckFlags.EmitGenerator;
1333313367
}
13368+
13369+
if (emitBind) {
13370+
links.flags |= NodeCheckFlags.EmitBind;
13371+
}
1333413372

1333513373
links.flags |= NodeCheckFlags.TypeChecked;
1333613374
}

src/compiler/diagnosticInformationMap.generated.ts

+2
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ namespace ts {
414414
The_arguments_object_cannot_be_referenced_in_an_async_arrow_function_Consider_using_a_standard_async_function_expression: { code: 2522, category: DiagnosticCategory.Error, key: "The 'arguments' object cannot be referenced in an async arrow function. Consider using a standard async function expression." },
415415
yield_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2523, category: DiagnosticCategory.Error, key: "'yield' expressions cannot be used in a parameter initializer." },
416416
await_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2524, category: DiagnosticCategory.Error, key: "'await' expressions cannot be used in a parameter initializer." },
417+
Cannot_bind_an_expression_whose_type_lacks_a_call_or_construct_signature: { code: 2525, category: DiagnosticCategory.Error, key: "Cannot bind an expression whose type lacks a call or construct signature." },
418+
The_target_of_a_bind_expression_without_a_left_hand_side_must_be_a_property_or_element_access: { code: 2526, category: DiagnosticCategory.Error, key: "The target of a bind expression without a left-hand side must be a property or element access." },
417419
JSX_element_attributes_type_0_must_be_an_object_type: { code: 2600, category: DiagnosticCategory.Error, key: "JSX element attributes type '{0}' must be an object type." },
418420
The_return_type_of_a_JSX_element_constructor_must_return_an_object_type: { code: 2601, category: DiagnosticCategory.Error, key: "The return type of a JSX element constructor must return an object type." },
419421
JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist: { code: 2602, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist." },

src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,14 @@
16451645
"category": "Error",
16461646
"code": 2524
16471647
},
1648+
"Cannot bind an expression whose type lacks a call or construct signature.": {
1649+
"category": "Error",
1650+
"code": 2525
1651+
},
1652+
"The target of a bind expression without a left-hand side must be a property or element access.": {
1653+
"category": "Error",
1654+
"code": 2526
1655+
},
16481656
"JSX element attributes type '{0}' must be an object type.": {
16491657
"category": "Error",
16501658
"code": 2600

src/compiler/emitter.ts

+134-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ var __extends = (this && this.__extends) || function (d, b) {
2323
function __() { this.constructor = d; }
2424
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
2525
};`;
26+
27+
// emit output for the __bind helper function
28+
const bindHelper = `
29+
var __bind = (this && this.__bind) || function (b, t) {
30+
var d;
31+
return d = function() { return b.apply(t, arguments); }, d.prototype = b.prototype, d;
32+
};`;
2633

2734
// emit output for the __decorate helper function
2835
const decorateHelper = `
@@ -147,6 +154,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
147154
let decorateEmitted = false;
148155
let paramEmitted = false;
149156
let awaiterEmitted = false;
157+
let bindEmitted = false;
150158
let tempFlags = 0;
151159
let tempVariables: Identifier[];
152160
let tempParameters: Identifier[];
@@ -1442,6 +1450,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
14421450
case SyntaxKind.WhileStatement:
14431451
case SyntaxKind.WithStatement:
14441452
case SyntaxKind.YieldExpression:
1453+
case SyntaxKind.BindExpression:
14451454
return true;
14461455
case SyntaxKind.BindingElement:
14471456
case SyntaxKind.EnumMember:
@@ -2156,22 +2165,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
21562165
}
21572166

21582167
function emitCallExpression(node: CallExpression) {
2159-
if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) {
2168+
let expression = node.expression;
2169+
if (expression.kind === SyntaxKind.BindExpression) {
2170+
if ((<BindExpression>expression).baseExpression !== undefined) {
2171+
emitBindExpression(<BindExpression>expression, node.arguments);
2172+
return;
2173+
}
2174+
else {
2175+
expression = (<BindExpression>expression).targetExpression;
2176+
}
2177+
}
2178+
else if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) {
21602179
emitCallWithSpread(node);
21612180
return;
21622181
}
21632182
let superCall = false;
2164-
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2165-
emitSuper(node.expression);
2183+
if (expression.kind === SyntaxKind.SuperKeyword) {
2184+
emitSuper(expression);
21662185
superCall = true;
21672186
}
21682187
else {
2169-
emit(node.expression);
2170-
superCall = node.expression.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.expression).expression.kind === SyntaxKind.SuperKeyword;
2188+
emit(expression);
2189+
superCall = expression.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>expression).expression.kind === SyntaxKind.SuperKeyword;
21712190
}
21722191
if (superCall && languageVersion < ScriptTarget.ES6) {
21732192
write(".call(");
2174-
emitThis(node.expression);
2193+
emitThis(expression);
21752194
if (node.arguments.length) {
21762195
write(", ");
21772196
emitCommaList(node.arguments);
@@ -2236,6 +2255,108 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
22362255
emitDownlevelTaggedTemplate(node);
22372256
}
22382257
}
2258+
2259+
function createCallCall(expression: Expression, thisArgument: Expression, _arguments: NodeArray<Expression>) {
2260+
let callIdentifier = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
2261+
callIdentifier.text = "call";
2262+
2263+
let callExpr = createCallExpression(
2264+
createPropertyAccessExpression(expression, callIdentifier),
2265+
[thisArgument, ..._arguments]);
2266+
2267+
return callExpr;
2268+
}
2269+
2270+
function createBindCall(expression: Expression, thisArgument: Expression) {
2271+
if (languageVersion < ScriptTarget.ES5) {
2272+
let bindIdentifier = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
2273+
bindIdentifier.text = "__bind";
2274+
2275+
let callExpr = createCallExpression(
2276+
bindIdentifier,
2277+
[parenthesizeForAccess(expression), thisArgument]);
2278+
2279+
return callExpr;
2280+
}
2281+
else {
2282+
let bindIdentifier = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
2283+
bindIdentifier.text = "bind";
2284+
2285+
let callExpr = createCallExpression(
2286+
createPropertyAccessExpression(expression, bindIdentifier),
2287+
[thisArgument]);
2288+
2289+
return callExpr;
2290+
}
2291+
}
2292+
2293+
function createCallExpression(expression: Expression, _arguments: Expression[]) {
2294+
let callExpr = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
2295+
callExpr.expression = parenthesizeForAccess(expression);
2296+
callExpr.arguments = <NodeArray<Expression>>createSynthesizedNodeArray();
2297+
for (let argument of _arguments) {
2298+
callExpr.arguments.push(argument);
2299+
}
2300+
return callExpr;
2301+
}
2302+
2303+
function createAssignmentExpression(left: Expression, right: Expression): BinaryExpression {
2304+
let assignExpression = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression);
2305+
assignExpression.left = left;
2306+
assignExpression.operatorToken = createSynthesizedNode(SyntaxKind.EqualsToken);
2307+
assignExpression.right = right;
2308+
return assignExpression;
2309+
}
2310+
2311+
function createCommaExpression(left: Expression, right: Expression): BinaryExpression {
2312+
let commaExpression = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression);
2313+
commaExpression.left = left;
2314+
commaExpression.operatorToken = createSynthesizedNode(SyntaxKind.CommaToken);
2315+
commaExpression.right = right;
2316+
return commaExpression;
2317+
}
2318+
2319+
function emitBindExpression(node: BindExpression, _arguments?: NodeArray<Expression>): void {
2320+
let { baseExpression, targetExpression } = node;
2321+
let boundExpression: Expression;
2322+
let thisArgument: Expression;
2323+
if (baseExpression) {
2324+
let tempVariable = createAndRecordTempVariable(TempFlags.Auto);
2325+
boundExpression = createCommaExpression(
2326+
createAssignmentExpression(tempVariable, baseExpression),
2327+
targetExpression
2328+
);
2329+
thisArgument = tempVariable;
2330+
}
2331+
else if (targetExpression.kind === SyntaxKind.PropertyAccessExpression) {
2332+
let tempVariable = createAndRecordTempVariable(TempFlags.Auto);
2333+
let { expression, name } = <PropertyAccessExpression>targetExpression;
2334+
boundExpression = createPropertyAccessExpression(
2335+
createAssignmentExpression(tempVariable, expression),
2336+
name
2337+
);
2338+
thisArgument = tempVariable;
2339+
}
2340+
else if (targetExpression.kind === SyntaxKind.ElementAccessExpression) {
2341+
let tempVariable = createAndRecordTempVariable(TempFlags.Auto);
2342+
let { expression, argumentExpression } = <ElementAccessExpression>targetExpression;
2343+
boundExpression = createElementAccessExpression(
2344+
createAssignmentExpression(tempVariable, expression),
2345+
argumentExpression
2346+
);
2347+
thisArgument = tempVariable;
2348+
}
2349+
else {
2350+
emit(targetExpression);
2351+
return;
2352+
}
2353+
2354+
let callExpression = _arguments
2355+
? createCallCall(boundExpression, thisArgument, _arguments)
2356+
: createBindCall(boundExpression, thisArgument);
2357+
2358+
emit(callExpression);
2359+
}
22392360

22402361
function emitParenExpression(node: ParenthesizedExpression) {
22412362
// If the node is synthesized, it means the emitter put the parentheses there,
@@ -6232,6 +6353,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
62326353
writeLines(extendsHelper);
62336354
extendsEmitted = true;
62346355
}
6356+
6357+
if ((languageVersion < ScriptTarget.ES5) && (!bindEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitBind)) {
6358+
writeLines(bindHelper);
6359+
bindEmitted = true;
6360+
}
62356361

62366362
if (!decorateEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitDecorate) {
62376363
writeLines(decorateHelper);
@@ -6416,6 +6542,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
64166542
return emitNewExpression(<NewExpression>node);
64176543
case SyntaxKind.TaggedTemplateExpression:
64186544
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
6545+
case SyntaxKind.BindExpression:
6546+
return emitBindExpression(<BindExpression>node);
64196547
case SyntaxKind.TypeAssertionExpression:
64206548
return emit((<TypeAssertion>node).expression);
64216549
case SyntaxKind.AsExpression:

0 commit comments

Comments
 (0)