diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8855755377e..f9cca936347 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3242,6 +3242,10 @@ namespace ts { transformFlags |= TransformFlags.AssertESNext; break; + case SyntaxKind.BindExpression: + transformFlags |= TransformFlags.AssertESNext; + break; + case SyntaxKind.SuperKeyword: // This node is ES6 syntax. transformFlags |= TransformFlags.AssertES2015; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 241d51576e0..675de53bd1e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -616,6 +616,8 @@ namespace ts { return emitPropertyAccessExpression(node); case SyntaxKind.ElementAccessExpression: return emitElementAccessExpression(node); + case SyntaxKind.BindExpression: + return emitBindExpression(node); case SyntaxKind.CallExpression: return emitCallExpression(node); case SyntaxKind.NewExpression: @@ -1079,6 +1081,12 @@ namespace ts { write("]"); } + function emitBindExpression(node: BindExpression) { + emitExpression(node.expression); + write("::"); + emitExpression(node.targetExpression); + } + function emitCallExpression(node: CallExpression) { emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 76c4bcd3d6d..273a1f6f823 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1624,6 +1624,18 @@ namespace ts { ); } + export function createFunctionBind(func: Expression, thisArg: Expression, argumentsList: Expression[], location?: TextRange) { + return createCall( + createPropertyAccess(func, "bind"), + /*typeArguments*/ undefined, + [ + thisArg, + ...argumentsList + ], + location + ); + } + export function createFunctionApply(func: Expression, thisArg: Expression, argumentsExpression: Expression, location?: TextRange) { return createCall( createPropertyAccess(func, "apply"), diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7f9388b8481..f3cb1a18535 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4026,6 +4026,14 @@ namespace ts { continue; } + if (parseOptional(SyntaxKind.ColonColonToken)) { + const bindExpression = createNode(SyntaxKind.BindExpression, expression.pos); + bindExpression.expression = expression; + bindExpression.targetExpression = parseMemberExpressionOrHigher(); + expression = finishNode(bindExpression); + continue; + } + return expression; } } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index a20ad00e7ce..0f7a5e48eb7 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -170,6 +170,7 @@ namespace ts { "||": SyntaxKind.BarBarToken, "?": SyntaxKind.QuestionToken, ":": SyntaxKind.ColonToken, + "::": SyntaxKind.ColonColonToken, "=": SyntaxKind.EqualsToken, "+=": SyntaxKind.PlusEqualsToken, "-=": SyntaxKind.MinusEqualsToken, @@ -1273,8 +1274,8 @@ namespace ts { return token = SyntaxKind.WhitespaceTrivia; } case CharacterCodes.exclamation: - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - if (text.charCodeAt(pos + 2) === CharacterCodes.equals) { + if (pos + 1 < end && text.charCodeAt(pos + 1) === CharacterCodes.equals) { + if (pos + 2 < end && text.charCodeAt(pos + 2) === CharacterCodes.equals) { return pos += 3, token = SyntaxKind.ExclamationEqualsEqualsToken; } return pos += 2, token = SyntaxKind.ExclamationEqualsToken; @@ -1288,17 +1289,19 @@ namespace ts { case CharacterCodes.backtick: return token = scanTemplateAndSetTokenValue(); case CharacterCodes.percent: - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + if (pos + 1 < end && text.charCodeAt(pos + 1) === CharacterCodes.equals) { return pos += 2, token = SyntaxKind.PercentEqualsToken; } pos++; return token = SyntaxKind.PercentToken; case CharacterCodes.ampersand: - if (text.charCodeAt(pos + 1) === CharacterCodes.ampersand) { - return pos += 2, token = SyntaxKind.AmpersandAmpersandToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.AmpersandEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.ampersand) { + return pos += 2, token = SyntaxKind.AmpersandAmpersandToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.AmpersandEqualsToken; + } } pos++; return token = SyntaxKind.AmpersandToken; @@ -1309,23 +1312,27 @@ namespace ts { pos++; return token = SyntaxKind.CloseParenToken; case CharacterCodes.asterisk: - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.AsteriskEqualsToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { - if (text.charCodeAt(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.AsteriskEqualsToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { + if (pos + 2 < end && text.charCodeAt(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken; + } + return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; } - return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; } pos++; return token = SyntaxKind.AsteriskToken; case CharacterCodes.plus: - if (text.charCodeAt(pos + 1) === CharacterCodes.plus) { - return pos += 2, token = SyntaxKind.PlusPlusToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.PlusEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.plus) { + return pos += 2, token = SyntaxKind.PlusPlusToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.PlusEqualsToken; + } } pos++; return token = SyntaxKind.PlusToken; @@ -1333,81 +1340,86 @@ namespace ts { pos++; return token = SyntaxKind.CommaToken; case CharacterCodes.minus: - if (text.charCodeAt(pos + 1) === CharacterCodes.minus) { - return pos += 2, token = SyntaxKind.MinusMinusToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.MinusEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.minus) { + return pos += 2, token = SyntaxKind.MinusMinusToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.MinusEqualsToken; + } } pos++; return token = SyntaxKind.MinusToken; case CharacterCodes.dot: - if (isDigit(text.charCodeAt(pos + 1))) { - tokenValue = scanNumber(); - return token = SyntaxKind.NumericLiteral; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.dot && text.charCodeAt(pos + 2) === CharacterCodes.dot) { - return pos += 3, token = SyntaxKind.DotDotDotToken; + if (pos + 1 < end) { + if (isDigit(text.charCodeAt(pos + 1))) { + tokenValue = scanNumber(); + return token = SyntaxKind.NumericLiteral; + } + if (pos + 2 < end && text.charCodeAt(pos + 1) === CharacterCodes.dot && text.charCodeAt(pos + 2) === CharacterCodes.dot) { + return pos += 3, token = SyntaxKind.DotDotDotToken; + } } pos++; return token = SyntaxKind.DotToken; case CharacterCodes.slash: // Single-line comment - if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { - pos += 2; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { + pos += 2; - while (pos < end) { - if (isLineBreak(text.charCodeAt(pos))) { - break; - } - pos++; + while (pos < end) { + if (isLineBreak(text.charCodeAt(pos))) { + break; + } + pos++; - } - - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.SingleLineCommentTrivia; - } - } - // Multi-line comment - if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { - pos += 2; - - let commentClosed = false; - while (pos < end) { - const ch = text.charCodeAt(pos); - - if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { - pos += 2; - commentClosed = true; - break; } - if (isLineBreak(ch)) { - precedingLineBreak = true; + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.SingleLineCommentTrivia; + } + } + // Multi-line comment + if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { + pos += 2; + + let commentClosed = false; + while (pos < end) { + const ch = text.charCodeAt(pos); + + if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { + pos += 2; + commentClosed = true; + break; + } + + if (isLineBreak(ch)) { + precedingLineBreak = true; + } + pos++; + } + + if (!commentClosed) { + error(Diagnostics.Asterisk_Slash_expected); + } + + if (skipTrivia) { + continue; + } + else { + tokenIsUnterminated = !commentClosed; + return token = SyntaxKind.MultiLineCommentTrivia; } - pos++; } - if (!commentClosed) { - error(Diagnostics.Asterisk_Slash_expected); - } - - if (skipTrivia) { - continue; - } - else { - tokenIsUnterminated = !commentClosed; - return token = SyntaxKind.MultiLineCommentTrivia; + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.SlashEqualsToken; } } - - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.SlashEqualsToken; - } - pos++; return token = SyntaxKind.SlashToken; @@ -1462,8 +1474,12 @@ namespace ts { tokenValue = scanNumber(); return token = SyntaxKind.NumericLiteral; case CharacterCodes.colon: + if (pos + 1 < end && text.charCodeAt(pos + 1) === CharacterCodes.colon) { + pos += 2; + return token = SyntaxKind.ColonColonToken; + } pos++; - return token = SyntaxKind.ColonToken; + return token = SyntaxKind.ColonToken; case CharacterCodes.semicolon: pos++; return token = SyntaxKind.SemicolonToken; @@ -1477,20 +1493,21 @@ namespace ts { return token = SyntaxKind.ConflictMarkerTrivia; } } - - if (text.charCodeAt(pos + 1) === CharacterCodes.lessThan) { - if (text.charCodeAt(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.lessThan) { + if (pos + 2 < end && text.charCodeAt(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken; + } + return pos += 2, token = SyntaxKind.LessThanLessThanToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.LessThanEqualsToken; + } + if (languageVariant === LanguageVariant.JSX && + text.charCodeAt(pos + 1) === CharacterCodes.slash && + text.charCodeAt(pos + 2) !== CharacterCodes.asterisk) { + return pos += 2, token = SyntaxKind.LessThanSlashToken; } - return pos += 2, token = SyntaxKind.LessThanLessThanToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.LessThanEqualsToken; - } - if (languageVariant === LanguageVariant.JSX && - text.charCodeAt(pos + 1) === CharacterCodes.slash && - text.charCodeAt(pos + 2) !== CharacterCodes.asterisk) { - return pos += 2, token = SyntaxKind.LessThanSlashToken; } pos++; return token = SyntaxKind.LessThanToken; @@ -1504,15 +1521,16 @@ namespace ts { return token = SyntaxKind.ConflictMarkerTrivia; } } - - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - if (text.charCodeAt(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + if (pos + 2 < end && text.charCodeAt(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken; + } + return pos += 2, token = SyntaxKind.EqualsEqualsToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.greaterThan) { + return pos += 2, token = SyntaxKind.EqualsGreaterThanToken; } - return pos += 2, token = SyntaxKind.EqualsEqualsToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.greaterThan) { - return pos += 2, token = SyntaxKind.EqualsGreaterThanToken; } pos++; return token = SyntaxKind.EqualsToken; @@ -1539,7 +1557,7 @@ namespace ts { pos++; return token = SyntaxKind.CloseBracketToken; case CharacterCodes.caret: - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + if (pos + 1 < end && text.charCodeAt(pos + 1) === CharacterCodes.equals) { return pos += 2, token = SyntaxKind.CaretEqualsToken; } pos++; @@ -1548,14 +1566,16 @@ namespace ts { pos++; return token = SyntaxKind.OpenBraceToken; case CharacterCodes.bar: - if (text.charCodeAt(pos + 1) === CharacterCodes.bar) { - return pos += 2, token = SyntaxKind.BarBarToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.BarEqualsToken; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.greaterThan) { - return pos += 2, token = SyntaxKind.BarGreaterThanToken; + if (pos + 1 < end) { + if (text.charCodeAt(pos + 1) === CharacterCodes.bar) { + return pos += 2, token = SyntaxKind.BarBarToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.BarEqualsToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.greaterThan) { + return pos += 2, token = SyntaxKind.BarGreaterThanToken; + } } pos++; return token = SyntaxKind.BarToken; diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 23404fa93df..89e7f266dd9 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -71,6 +71,8 @@ namespace ts { return visitCallExpression(node as CallExpression); case SyntaxKind.OperatorExpression: return visitOperatorExpression(node as OperatorExpression); + case SyntaxKind.BindExpression: + return visitBindExpression(node as BindExpression); default: return visitEachChild(node, visitor, context); } @@ -488,11 +490,11 @@ namespace ts { positionalParameters, /*type*/ undefined, /*equalsGreaterThanToken*/ createToken(SyntaxKind.EqualsGreaterThanToken), - updateCall(node, expression, /*typeArguments*/ undefined, argumentList), + updateCall(node, expression, /*typeArguments*/ undefined, argumentList || node.arguments), /*location*/ node ); } - return updateCall(node, expression, /*typeArguments*/ undefined, argumentList); + return updateCall(node, expression, /*typeArguments*/ undefined, argumentList || node.arguments); } function visitOperatorExpression(node: OperatorExpression) { @@ -537,6 +539,23 @@ namespace ts { node ); } + + function visitBindExpression(node: BindExpression) { + const thisArg = createTempVariable(context.hoistVariableDeclaration); + return inlineExpressions([ + createAssignment( + thisArg, + visitNode(node.expression, visitor, isExpression), + node.expression + ), + createFunctionBind( + visitNode(node.targetExpression, visitor, isLeftHandSideExpression), + thisArg, + [], + node + ) + ]); + } } const assignHelper: EmitHelper = { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0b2feb4ee26..4bdbbe99783 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -96,6 +96,7 @@ namespace ts { BarBarToken, QuestionToken, ColonToken, + ColonColonToken, AtToken, // Assignments EqualsToken, @@ -233,6 +234,7 @@ namespace ts { ObjectLiteralExpression, PropertyAccessExpression, ElementAccessExpression, + BindExpression, CallExpression, NewExpression, TaggedTemplateExpression, @@ -1417,6 +1419,12 @@ namespace ts { | SuperElementAccessExpression ; + export interface BindExpression extends MemberExpression { + kind: SyntaxKind.BindExpression; + expression: LeftHandSideExpression; + targetExpression: MemberExpression; + } + export interface CallExpression extends LeftHandSideExpression, Declaration { kind: SyntaxKind.CallExpression; expression: LeftHandSideExpression; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5979ef34c0e..af97bb56f2f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3947,11 +3947,10 @@ namespace ts { return node.kind === SyntaxKind.ExpressionWithTypeArguments; } - function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { + function isMemberExpressionKind(kind: SyntaxKind): boolean { return kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression - || kind === SyntaxKind.NewExpression - || kind === SyntaxKind.CallExpression + || kind === SyntaxKind.BindExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.TaggedTemplateExpression @@ -3976,6 +3975,16 @@ namespace ts { || kind === SyntaxKind.RawExpression; } + export function isMemberExpression(node: Node): node is MemberExpression { + return isMemberExpressionKind(skipPartiallyEmittedExpressions(node).kind); + } + + function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { + return kind === SyntaxKind.NewExpression + || kind === SyntaxKind.CallExpression + || isMemberExpressionKind(kind); + } + export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind); }