Skip to content

Commit 6a1ccd8

Browse files
committed
Adds support for new.target
1 parent 25c7caa commit 6a1ccd8

23 files changed

+1352
-128
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3133,6 +3133,7 @@ namespace ts {
31333133
case SyntaxKind.TaggedTemplateExpression:
31343134
case SyntaxKind.ShorthandPropertyAssignment:
31353135
case SyntaxKind.StaticKeyword:
3136+
case SyntaxKind.MetaProperty:
31363137
// These nodes are ES6 syntax.
31373138
transformFlags |= TransformFlags.AssertES2015;
31383139
break;

src/compiler/checker.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ namespace ts {
241241
const visitedFlowNodes: FlowNode[] = [];
242242
const visitedFlowTypes: FlowType[] = [];
243243
const potentialThisCollisions: Node[] = [];
244+
const potentialNewTargetCollisions: Node[] = [];
244245
const awaitedTypeStack: number[] = [];
245246

246247
const diagnostics = createDiagnosticCollection();
@@ -10196,6 +10197,7 @@ namespace ts {
1019610197

1019710198
checkCollisionWithCapturedSuperVariable(node, node);
1019810199
checkCollisionWithCapturedThisVariable(node, node);
10200+
checkCollisionWithCapturedNewTargetVariable(node, node);
1019910201
checkNestedBlockScopedBinding(node, symbol);
1020010202

1020110203
const type = getTypeOfSymbol(localOrExportSymbol);
@@ -13720,6 +13722,24 @@ namespace ts {
1372013722
return getNonNullableType(checkExpression(node.expression));
1372113723
}
1372213724

13725+
function checkMetaProperty(node: MetaProperty) {
13726+
checkGrammarMetaProperty(node);
13727+
Debug.assert(node.keywordToken === SyntaxKind.NewKeyword && node.name.text === "target", "Unrecognized meta-property.");
13728+
const container = getNewTargetContainer(node);
13729+
if (!container) {
13730+
error(node, Diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target");
13731+
return unknownType;
13732+
}
13733+
else if (container.kind === SyntaxKind.Constructor) {
13734+
const symbol = getSymbolOfNode(container.parent);
13735+
return getTypeOfSymbol(symbol);
13736+
}
13737+
else {
13738+
const symbol = getSymbolOfNode(container);
13739+
return getTypeOfSymbol(symbol);
13740+
}
13741+
}
13742+
1372313743
function getTypeOfParameter(symbol: Symbol) {
1372413744
const type = getTypeOfSymbol(symbol);
1372513745
if (strictNullChecks) {
@@ -14128,6 +14148,7 @@ namespace ts {
1412814148
if (produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration) {
1412914149
checkCollisionWithCapturedSuperVariable(node, (<FunctionExpression>node).name);
1413014150
checkCollisionWithCapturedThisVariable(node, (<FunctionExpression>node).name);
14151+
checkCollisionWithCapturedNewTargetVariable(node, (<FunctionExpression>node).name);
1413114152
}
1413214153

1413314154
return type;
@@ -15152,6 +15173,8 @@ namespace ts {
1515215173
return checkAssertion(<AssertionExpression>node);
1515315174
case SyntaxKind.NonNullExpression:
1515415175
return checkNonNullAssertion(<NonNullExpression>node);
15176+
case SyntaxKind.MetaProperty:
15177+
return checkMetaProperty(<MetaProperty>node);
1515515178
case SyntaxKind.DeleteExpression:
1515615179
return checkDeleteExpression(<DeleteExpression>node);
1515715180
case SyntaxKind.VoidExpression:
@@ -16552,6 +16575,7 @@ namespace ts {
1655216575

1655316576
checkCollisionWithCapturedSuperVariable(node, node.name);
1655416577
checkCollisionWithCapturedThisVariable(node, node.name);
16578+
checkCollisionWithCapturedNewTargetVariable(node, node.name);
1655516579
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
1655616580
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1655716581
}
@@ -16835,6 +16859,12 @@ namespace ts {
1683516859
}
1683616860
}
1683716861

16862+
function checkCollisionWithCapturedNewTargetVariable(node: Node, name: Identifier): void {
16863+
if (needCollisionCheckForIdentifier(node, name, "_newTarget")) {
16864+
potentialNewTargetCollisions.push(node);
16865+
}
16866+
}
16867+
1683816868
// this function will run after checking the source file so 'CaptureThis' is correct for all nodes
1683916869
function checkIfThisIsCapturedInEnclosingScope(node: Node): void {
1684016870
let current = node;
@@ -16853,6 +16883,23 @@ namespace ts {
1685316883
}
1685416884
}
1685516885

16886+
function checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void {
16887+
let current = node;
16888+
while (current) {
16889+
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureNewTarget) {
16890+
const isDeclaration = node.kind !== SyntaxKind.Identifier;
16891+
if (isDeclaration) {
16892+
error((<Declaration>node).name, Diagnostics.Duplicate_identifier_newTarget_Compiler_uses_variable_declaration_newTarget_to_capture_new_target_meta_property_reference);
16893+
}
16894+
else {
16895+
error(node, Diagnostics.Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference);
16896+
}
16897+
return;
16898+
}
16899+
current = current.parent;
16900+
}
16901+
}
16902+
1685616903
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
1685716904
if (!needCollisionCheckForIdentifier(node, name, "_super")) {
1685816905
return;
@@ -17148,6 +17195,7 @@ namespace ts {
1714817195
}
1714917196
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
1715017197
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
17198+
checkCollisionWithCapturedNewTargetVariable(node, <Identifier>node.name);
1715117199
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
1715217200
checkCollisionWithGlobalPromiseInGeneratedCode(node, <Identifier>node.name);
1715317201
}
@@ -17984,6 +18032,7 @@ namespace ts {
1798418032
if (node.name) {
1798518033
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
1798618034
checkCollisionWithCapturedThisVariable(node, node.name);
18035+
checkCollisionWithCapturedNewTargetVariable(node, node.name);
1798718036
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
1798818037
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1798918038
}
@@ -18518,6 +18567,7 @@ namespace ts {
1851818567

1851918568
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
1852018569
checkCollisionWithCapturedThisVariable(node, node.name);
18570+
checkCollisionWithCapturedNewTargetVariable(node, node.name);
1852118571
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
1852218572
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
1852318573
checkExportsOnMergedDeclarations(node);
@@ -19223,6 +19273,7 @@ namespace ts {
1922319273
checkGrammarSourceFile(node);
1922419274

1922519275
potentialThisCollisions.length = 0;
19276+
potentialNewTargetCollisions.length = 0;
1922619277

1922719278
deferredNodes = [];
1922819279
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
@@ -19251,6 +19302,11 @@ namespace ts {
1925119302
potentialThisCollisions.length = 0;
1925219303
}
1925319304

19305+
if (potentialNewTargetCollisions.length) {
19306+
forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope)
19307+
potentialNewTargetCollisions.length = 0;
19308+
}
19309+
1925419310
links.flags |= NodeCheckFlags.TypeChecked;
1925519311
}
1925619312
}
@@ -21581,6 +21637,14 @@ namespace ts {
2158121637
}
2158221638
}
2158321639

21640+
function checkGrammarMetaProperty(node: MetaProperty) {
21641+
if (node.keywordToken === SyntaxKind.NewKeyword) {
21642+
if (node.name.text !== "target") {
21643+
return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_0, node.name.text, tokenToString(node.keywordToken), "target");
21644+
}
21645+
}
21646+
}
21647+
2158421648
function hasParseDiagnostics(sourceFile: SourceFile): boolean {
2158521649
return sourceFile.parseDiagnostics.length > 0;
2158621650
}

src/compiler/core.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -568,26 +568,81 @@ namespace ts {
568568
* is created if `value` was appended.
569569
* @param value The value to append to the array. If `value` is `undefined`, nothing is
570570
* appended.
571+
* @param copyOnWrite Indicates whether to return a fresh array rather than modify the
572+
* existing array.
571573
*/
572-
export function append<T>(to: T[] | undefined, value: T | undefined): T[] | undefined {
574+
export function append<T>(to: T[] | undefined, value: T | undefined, copyOnWrite?: boolean): T[] | undefined {
573575
if (value === undefined) return to;
574576
if (to === undefined) return [value];
577+
if (copyOnWrite) return [...to, value];
575578
to.push(value);
576579
return to;
577580
}
578581

582+
/**
583+
* Prepends a value to an array, returning the array.
584+
*
585+
* @param to The array to which `value` is to be prepended. If `to` is `undefined`, a new array
586+
* is created if `value` was prepended.
587+
* @param value The value to prepend to the array. If `value` is `undefined`, nothing is
588+
* prepended.
589+
* @param copyOnWrite Indicates whether to return a fresh array rather than modify the
590+
* existing array.
591+
*/
592+
export function prepend<T>(to: T[] | undefined, value: T | undefined, copyOnWrite?: boolean): T[] | undefined {
593+
if (value === undefined) return to;
594+
if (to === undefined) return [value];
595+
if (copyOnWrite) return [value, ...to];
596+
to.unshift(value);
597+
return to;
598+
}
599+
579600
/**
580601
* Appends a range of value to an array, returning the array.
581602
*
582603
* @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array
583604
* is created if `value` was appended.
584605
* @param from The values to append to the array. If `from` is `undefined`, nothing is
585606
* appended. If an element of `from` is `undefined`, that element is not appended.
607+
* @param copyOnWrite Indicates whether to return a fresh array rather than modify the
608+
* existing array.
586609
*/
587-
export function addRange<T>(to: T[] | undefined, from: T[] | undefined): T[] | undefined {
610+
export function addRange<T>(to: T[] | undefined, from: T[] | undefined, copyOnWrite?: boolean): T[] | undefined {
588611
if (from === undefined) return to;
589-
for (const v of from) {
590-
to = append(to, v);
612+
from = filter(from, isDefined);
613+
if (to === undefined) return from;
614+
if (from.length > 0) {
615+
if (copyOnWrite) {
616+
return [...to, ...from];
617+
}
618+
for (const v of from) {
619+
to.push(v);
620+
}
621+
}
622+
return to;
623+
}
624+
625+
/**
626+
* Appends a range of value to an array, returning the array.
627+
*
628+
* @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array
629+
* is created if `value` was appended.
630+
* @param from The values to append to the array. If `from` is `undefined`, nothing is
631+
* appended. If an element of `from` is `undefined`, that element is not appended.
632+
* @param copyOnWrite Indicates whether to return a fresh array rather than modify the
633+
* existing array.
634+
*/
635+
export function prependRange<T>(to: T[] | undefined, from: T[] | undefined, copyOnWrite?: boolean): T[] | undefined {
636+
if (from === undefined) return to;
637+
from = filter(from, isDefined);
638+
if (to === undefined) return from;
639+
if (from.length > 0) {
640+
if (copyOnWrite) {
641+
return [...from, ...to];
642+
}
643+
for (let i = from.length - 1; i >= 0; i--) {
644+
to.unshift(from[i]);
645+
}
591646
}
592647
return to;
593648
}

src/compiler/diagnosticMessages.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,14 @@
17711771
"category": "Error",
17721772
"code": 2542
17731773
},
1774+
"Duplicate identifier '_newTarget'. Compiler uses variable declaration '_newTarget' to capture 'new.target' meta-property reference.": {
1775+
"category": "Error",
1776+
"code": 2543
1777+
},
1778+
"Expression resolves to variable declaration '_newTarget' that compiler uses to capture 'new.target' meta-property reference.": {
1779+
"category": "Error",
1780+
"code": 2544
1781+
},
17741782
"JSX element attributes type '{0}' may not be a union type.": {
17751783
"category": "Error",
17761784
"code": 2600
@@ -3169,6 +3177,14 @@
31693177
"category": "Error",
31703178
"code": 17011
31713179
},
3180+
"'{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{0}'?": {
3181+
"category": "Error",
3182+
"code": 17012
3183+
},
3184+
"Meta-property '{0}' is only allowed in the body of a function declaration, function expression, or constructor.": {
3185+
"category": "Error",
3186+
"code": 17013
3187+
},
31723188

31733189
"Circularity detected while resolving configuration: {0}": {
31743190
"category": "Error",

src/compiler/emitter.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,8 @@ namespace ts {
660660
return emitAsExpression(<AsExpression>node);
661661
case SyntaxKind.NonNullExpression:
662662
return emitNonNullExpression(<NonNullExpression>node);
663+
case SyntaxKind.MetaProperty:
664+
return emitMetaProperty(<MetaProperty>node);
663665

664666
// JSX
665667
case SyntaxKind.JsxElement:
@@ -1249,6 +1251,12 @@ namespace ts {
12491251
write("!");
12501252
}
12511253

1254+
function emitMetaProperty(node: MetaProperty) {
1255+
writeToken(node.keywordToken, node.pos);
1256+
write(".");
1257+
emit(node.name);
1258+
}
1259+
12521260
//
12531261
// Misc
12541262
//
@@ -2571,6 +2579,13 @@ namespace ts {
25712579
return makeUniqueName("class");
25722580
}
25732581

2582+
function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) {
2583+
if (isIdentifier(node.name)) {
2584+
return generateNameForNodeCached(node.name);
2585+
}
2586+
return makeTempVariableName(TempFlags.Auto);
2587+
}
2588+
25742589
/**
25752590
* Generates a unique name from a node.
25762591
*
@@ -2592,6 +2607,10 @@ namespace ts {
25922607
return generateNameForExportDefault();
25932608
case SyntaxKind.ClassExpression:
25942609
return generateNameForClassExpression();
2610+
case SyntaxKind.MethodDeclaration:
2611+
case SyntaxKind.GetAccessor:
2612+
case SyntaxKind.SetAccessor:
2613+
return generateNameForMethodOrAccessor(<MethodDeclaration | AccessorDeclaration>node);
25952614
default:
25962615
return makeTempVariableName(TempFlags.Auto);
25972616
}
@@ -2642,6 +2661,11 @@ namespace ts {
26422661
return node;
26432662
}
26442663

2664+
function generateNameForNodeCached(node: Node) {
2665+
const nodeId = getNodeId(node);
2666+
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
2667+
}
2668+
26452669
/**
26462670
* Gets the generated identifier text from a generated identifier.
26472671
*
@@ -2652,8 +2676,7 @@ namespace ts {
26522676
// Generated names generate unique names based on their original node
26532677
// and are cached based on that node's id
26542678
const node = getNodeForGeneratedName(name);
2655-
const nodeId = getNodeId(node);
2656-
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
2679+
return generateNameForNodeCached(node);
26572680
}
26582681
else {
26592682
// Auto, Loop, and Unique names are cached based on their unique

src/compiler/parser.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ namespace ts {
198198
visitNode(cbNode, (<AsExpression>node).type);
199199
case SyntaxKind.NonNullExpression:
200200
return visitNode(cbNode, (<NonNullExpression>node).expression);
201+
case SyntaxKind.MetaProperty:
202+
return visitNode(cbNode, (<MetaProperty>node).name);
201203
case SyntaxKind.ConditionalExpression:
202204
return visitNode(cbNode, (<ConditionalExpression>node).condition) ||
203205
visitNode(cbNode, (<ConditionalExpression>node).questionToken) ||
@@ -4329,15 +4331,22 @@ namespace ts {
43294331
return isIdentifier() ? parseIdentifier() : undefined;
43304332
}
43314333

4332-
function parseNewExpression(): NewExpression {
4333-
const node = <NewExpression>createNode(SyntaxKind.NewExpression);
4334+
function parseNewExpression(): NewExpression | MetaProperty {
4335+
const fullStart = scanner.getStartPos();
43344336
parseExpected(SyntaxKind.NewKeyword);
4337+
if (parseOptional(SyntaxKind.DotToken)) {
4338+
const node = <MetaProperty>createNode(SyntaxKind.MetaProperty, fullStart);
4339+
node.keywordToken = SyntaxKind.NewKeyword;
4340+
node.name = parseIdentifierName();
4341+
return finishNode(node);
4342+
}
4343+
4344+
const node = <NewExpression>createNode(SyntaxKind.NewExpression, fullStart);
43354345
node.expression = parseMemberExpressionOrHigher();
43364346
node.typeArguments = tryParse(parseTypeArgumentsInExpression);
43374347
if (node.typeArguments || token() === SyntaxKind.OpenParenToken) {
43384348
node.arguments = parseArgumentList();
43394349
}
4340-
43414350
return finishNode(node);
43424351
}
43434352

0 commit comments

Comments
 (0)