Skip to content

Commit 3abd351

Browse files
authored
Fix super property transform in async arrow in method (#51240)
1 parent eed0511 commit 3abd351

File tree

8 files changed

+438
-17
lines changed

8 files changed

+438
-17
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26706,13 +26706,16 @@ namespace ts {
2670626706
const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
2670726707
let container = immediateContainer;
2670826708
let needToCaptureLexicalThis = false;
26709+
let inAsyncFunction = false;
2670926710

2671026711
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
2671126712
if (!isCallExpression) {
2671226713
while (container && container.kind === SyntaxKind.ArrowFunction) {
26714+
if (hasSyntacticModifier(container, ModifierFlags.Async)) inAsyncFunction = true;
2671326715
container = getSuperContainer(container, /*stopOnFunctions*/ true);
2671426716
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES2015;
2671526717
}
26718+
if (container && hasSyntacticModifier(container, ModifierFlags.Async)) inAsyncFunction = true;
2671626719
}
2671726720

2671826721
const canUseSuperExpression = isLegalUsageOfSuperExpression(container);
@@ -26824,12 +26827,12 @@ namespace ts {
2682426827
// as a call expression cannot be used as the target of a destructuring assignment while a property access can.
2682526828
//
2682626829
// For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations.
26827-
if (container.kind === SyntaxKind.MethodDeclaration && hasSyntacticModifier(container, ModifierFlags.Async)) {
26830+
if (container.kind === SyntaxKind.MethodDeclaration && inAsyncFunction) {
2682826831
if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) {
26829-
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding;
26832+
getNodeLinks(container).flags |= NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync;
2683026833
}
2683126834
else {
26832-
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
26835+
getNodeLinks(container).flags |= NodeCheckFlags.MethodWithSuperPropertyAccessInAsync;
2683326836
}
2683426837
}
2683526838

src/compiler/transformers/es2017.ts

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ namespace ts {
140140
return visitEachChild(node, visitor, context);
141141

142142
case SyntaxKind.GetAccessor:
143+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitGetAccessorDeclaration, node as GetAccessorDeclaration);
143144
case SyntaxKind.SetAccessor:
145+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitSetAccessorDeclaration, node as SetAccessorDeclaration);
144146
case SyntaxKind.Constructor:
147+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitConstructorDeclaration, node as ConstructorDeclaration);
145148
case SyntaxKind.ClassDeclaration:
146149
case SyntaxKind.ClassExpression:
147150
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node);
@@ -278,6 +281,15 @@ namespace ts {
278281
);
279282
}
280283

284+
function visitConstructorDeclaration(node: ConstructorDeclaration) {
285+
return factory.updateConstructorDeclaration(
286+
node,
287+
visitNodes(node.modifiers, visitor, isModifierLike),
288+
visitParameterList(node.parameters, visitor, context),
289+
transformMethodBody(node)
290+
);
291+
}
292+
281293
/**
282294
* Visits a MethodDeclaration node.
283295
*
@@ -298,7 +310,28 @@ namespace ts {
298310
/*type*/ undefined,
299311
getFunctionFlags(node) & FunctionFlags.Async
300312
? transformAsyncFunctionBody(node)
301-
: visitFunctionBody(node.body, visitor, context)
313+
: transformMethodBody(node)
314+
);
315+
}
316+
317+
function visitGetAccessorDeclaration(node: GetAccessorDeclaration) {
318+
return factory.updateGetAccessorDeclaration(
319+
node,
320+
visitNodes(node.modifiers, visitor, isModifierLike),
321+
node.name,
322+
visitParameterList(node.parameters, visitor, context),
323+
/*type*/ undefined,
324+
transformMethodBody(node)
325+
);
326+
}
327+
328+
function visitSetAccessorDeclaration(node: SetAccessorDeclaration) {
329+
return factory.updateSetAccessorDeclaration(
330+
node,
331+
visitNodes(node.modifiers, visitor, isModifierLike),
332+
node.name,
333+
visitParameterList(node.parameters, visitor, context),
334+
transformMethodBody(node)
302335
);
303336
}
304337

@@ -446,6 +479,50 @@ namespace ts {
446479
return false;
447480
}
448481

482+
function transformMethodBody(node: MethodDeclaration | AccessorDeclaration | ConstructorDeclaration): FunctionBody | undefined {
483+
Debug.assertIsDefined(node.body);
484+
485+
const savedCapturedSuperProperties = capturedSuperProperties;
486+
const savedHasSuperElementAccess = hasSuperElementAccess;
487+
capturedSuperProperties = new Set();
488+
hasSuperElementAccess = false;
489+
490+
let updated = visitFunctionBody(node.body, visitor, context);
491+
492+
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
493+
// This step isn't needed if we eventually transform this to ES5.
494+
const originalMethod = getOriginalNode(node, isFunctionLikeDeclaration);
495+
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 &&
496+
resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) &&
497+
(getFunctionFlags(originalMethod) & FunctionFlags.AsyncGenerator) !== FunctionFlags.AsyncGenerator;
498+
499+
if (emitSuperHelpers) {
500+
enableSubstitutionForAsyncMethodsWithSuper();
501+
if (capturedSuperProperties.size) {
502+
const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties);
503+
substitutedSuperAccessors[getNodeId(variableStatement)] = true;
504+
505+
const statements = updated.statements.slice();
506+
insertStatementsAfterStandardPrologue(statements, [variableStatement]);
507+
updated = factory.updateBlock(updated, statements);
508+
}
509+
510+
if (hasSuperElementAccess) {
511+
// Emit helpers for super element access expressions (`super[x]`).
512+
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
513+
addEmitHelper(updated, advancedAsyncSuperHelper);
514+
}
515+
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
516+
addEmitHelper(updated, asyncSuperHelper);
517+
}
518+
}
519+
}
520+
521+
capturedSuperProperties = savedCapturedSuperProperties;
522+
hasSuperElementAccess = savedHasSuperElementAccess;
523+
return updated;
524+
}
525+
449526
function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody;
450527
function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody;
451528
function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody {
@@ -495,7 +572,7 @@ namespace ts {
495572

496573
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
497574
// This step isn't needed if we eventually transform this to ES5.
498-
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuperBinding | NodeCheckFlags.AsyncMethodWithSuper);
575+
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync);
499576

500577
if (emitSuperHelpers) {
501578
enableSubstitutionForAsyncMethodsWithSuper();
@@ -511,10 +588,10 @@ namespace ts {
511588

512589
if (emitSuperHelpers && hasSuperElementAccess) {
513590
// Emit helpers for super element access expressions (`super[x]`).
514-
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
591+
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
515592
addEmitHelper(block, advancedAsyncSuperHelper);
516593
}
517-
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
594+
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
518595
addEmitHelper(block, asyncSuperHelper);
519596
}
520597
}
@@ -601,7 +678,7 @@ namespace ts {
601678
// If we need to support substitutions for `super` in an async method,
602679
// we should track it here.
603680
if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
604-
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
681+
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync);
605682
if (superContainerFlags !== enclosingSuperContainerFlags) {
606683
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
607684
enclosingSuperContainerFlags = superContainerFlags;
@@ -698,7 +775,7 @@ namespace ts {
698775
}
699776

700777
function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
701-
if (enclosingSuperContainerFlags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
778+
if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
702779
return setTextRange(
703780
factory.createPropertyAccessExpression(
704781
factory.createCallExpression(
@@ -728,7 +805,7 @@ namespace ts {
728805
export function createSuperAccessVariableStatement(factory: NodeFactory, resolver: EmitResolver, node: FunctionLikeDeclaration, names: Set<__String>) {
729806
// Create a variable declaration with a getter/setter (if binding) definition for each name:
730807
// const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... });
731-
const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) !== 0;
808+
const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) !== 0;
732809
const accessors: PropertyAssignment[] = [];
733810
names.forEach((_, key) => {
734811
const name = unescapeLeadingUnderscores(key);

src/compiler/transformers/es2018.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ namespace ts {
10131013

10141014
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
10151015
// This step isn't needed if we eventually transform this to ES5.
1016-
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuperBinding | NodeCheckFlags.AsyncMethodWithSuper);
1016+
const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync);
10171017

10181018
if (emitSuperHelpers) {
10191019
enableSubstitutionForAsyncMethodsWithSuper();
@@ -1028,10 +1028,10 @@ namespace ts {
10281028
const block = factory.updateBlock(node.body!, statements);
10291029

10301030
if (emitSuperHelpers && hasSuperElementAccess) {
1031-
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
1031+
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
10321032
addEmitHelper(block, advancedAsyncSuperHelper);
10331033
}
1034-
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
1034+
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
10351035
addEmitHelper(block, asyncSuperHelper);
10361036
}
10371037
}
@@ -1185,7 +1185,7 @@ namespace ts {
11851185
// If we need to support substitutions for `super` in an async method,
11861186
// we should track it here.
11871187
if (enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
1188-
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
1188+
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync);
11891189
if (superContainerFlags !== enclosingSuperContainerFlags) {
11901190
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
11911191
enclosingSuperContainerFlags = superContainerFlags;
@@ -1282,7 +1282,7 @@ namespace ts {
12821282
}
12831283

12841284
function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
1285-
if (enclosingSuperContainerFlags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
1285+
if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
12861286
return setTextRange(
12871287
factory.createPropertyAccessExpression(
12881288
factory.createCallExpression(

src/compiler/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,8 +5527,8 @@ namespace ts {
55275527
SuperInstance = 0x00000100, // Instance 'super' reference
55285528
SuperStatic = 0x00000200, // Static 'super' reference
55295529
ContextChecked = 0x00000400, // Contextual types have been assigned
5530-
AsyncMethodWithSuper = 0x00000800, // An async method that reads a value from a member of 'super'.
5531-
AsyncMethodWithSuperBinding = 0x00001000, // An async method that assigns a value to a member of 'super'.
5530+
MethodWithSuperPropertyAccessInAsync = 0x00000800, // A method that contains a SuperProperty access in an async context.
5531+
MethodWithSuperPropertyAssignmentInAsync = 0x00001000, // A method that contains a SuperProperty assignment in an async context.
55325532
CaptureArguments = 0x00002000, // Lexical 'arguments' used in body
55335533
EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them.
55345534
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.

tests/baselines/reference/asyncMethodWithSuper_es6.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,32 @@ class B extends A {
185185
(async () => super["x"] = f);
186186
}
187187
}
188+
189+
// https://github.com/microsoft/TypeScript/issues/46828
190+
class Base {
191+
set setter(x: any) {}
192+
get getter(): any { return; }
193+
method(x: string): any {}
194+
195+
static set setter(x: any) {}
196+
static get getter(): any { return; }
197+
static method(x: string): any {}
198+
}
199+
200+
class Derived extends Base {
201+
a() { return async () => super.method('') }
202+
b() { return async () => super.getter }
203+
c() { return async () => super.setter = '' }
204+
d() { return async () => super["method"]('') }
205+
e() { return async () => super["getter"] }
206+
f() { return async () => super["setter"] = '' }
207+
static a() { return async () => super.method('') }
208+
static b() { return async () => super.getter }
209+
static c() { return async () => super.setter = '' }
210+
static d() { return async () => super["method"]('') }
211+
static e() { return async () => super["getter"] }
212+
static f() { return async () => super["setter"] = '' }
213+
}
188214

189215

190216
//// [asyncMethodWithSuper_es6.js]
@@ -377,3 +403,62 @@ class B extends A {
377403
});
378404
}
379405
}
406+
// https://github.com/microsoft/TypeScript/issues/46828
407+
class Base {
408+
set setter(x) { }
409+
get getter() { return; }
410+
method(x) { }
411+
static set setter(x) { }
412+
static get getter() { return; }
413+
static method(x) { }
414+
}
415+
class Derived extends Base {
416+
a() { const _super = Object.create(null, {
417+
method: { get: () => super.method }
418+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.method.call(this, ''); }); }
419+
b() { const _super = Object.create(null, {
420+
getter: { get: () => super.getter }
421+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.getter; }); }
422+
c() { const _super = Object.create(null, {
423+
setter: { get: () => super.setter, set: v => super.setter = v }
424+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.setter = ''; }); }
425+
d() {
426+
const _superIndex = name => super[name];
427+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("method").call(this, ''); });
428+
}
429+
e() {
430+
const _superIndex = name => super[name];
431+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("getter"); });
432+
}
433+
f() {
434+
const _superIndex = (function (geti, seti) {
435+
const cache = Object.create(null);
436+
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
437+
})(name => super[name], (name, value) => super[name] = value);
438+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("setter").value = ''; });
439+
}
440+
static a() { const _super = Object.create(null, {
441+
method: { get: () => super.method }
442+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.method.call(this, ''); }); }
443+
static b() { const _super = Object.create(null, {
444+
getter: { get: () => super.getter }
445+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.getter; }); }
446+
static c() { const _super = Object.create(null, {
447+
setter: { get: () => super.setter, set: v => super.setter = v }
448+
}); return () => __awaiter(this, void 0, void 0, function* () { return _super.setter = ''; }); }
449+
static d() {
450+
const _superIndex = name => super[name];
451+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("method").call(this, ''); });
452+
}
453+
static e() {
454+
const _superIndex = name => super[name];
455+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("getter"); });
456+
}
457+
static f() {
458+
const _superIndex = (function (geti, seti) {
459+
const cache = Object.create(null);
460+
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
461+
})(name => super[name], (name, value) => super[name] = value);
462+
return () => __awaiter(this, void 0, void 0, function* () { return _superIndex("setter").value = ''; });
463+
}
464+
}

0 commit comments

Comments
 (0)