Skip to content

Commit 67a4fe5

Browse files
committed
Support for assignment/destructuring using super in an async method
1 parent 2745895 commit 67a4fe5

File tree

7 files changed

+347
-40
lines changed

7 files changed

+347
-40
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6902,6 +6902,16 @@ namespace ts {
69026902
return false;
69036903
}
69046904

6905+
function isSuperPropertyAccess(node: Node) {
6906+
return node.kind === SyntaxKind.PropertyAccessExpression
6907+
&& (<PropertyAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
6908+
}
6909+
6910+
function isSuperElementAccess(node: Node) {
6911+
return node.kind === SyntaxKind.ElementAccessExpression
6912+
&& (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
6913+
}
6914+
69056915
function checkSuperExpression(node: Node): Type {
69066916
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
69076917
const classDeclaration = getContainingClass(node);
@@ -6935,7 +6945,12 @@ namespace ts {
69356945

69366946
// Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference.
69376947
if (container.kind === SyntaxKind.MethodDeclaration && container.flags & NodeFlags.Async) {
6938-
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
6948+
if ((isSuperPropertyAccess(node.parent) || isSuperElementAccess(node.parent)) && isAssignmentTarget(node.parent)) {
6949+
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding;
6950+
}
6951+
else {
6952+
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
6953+
}
69396954
}
69406955

69416956
if (needToCaptureLexicalThis) {

src/compiler/emitter.ts

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
21262126
return;
21272127
}
21282128

2129+
if (languageVersion === ScriptTarget.ES6 &&
2130+
node.expression.kind === SyntaxKind.SuperKeyword &&
2131+
isInAsyncMethodWithSuperInES6(node)) {
2132+
const name = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
2133+
name.text = node.name.text;
2134+
emitSuperAccessInAsyncMethod(node.expression, name);
2135+
return;
2136+
}
2137+
21292138
emit(node.expression);
21302139
const indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken);
21312140

@@ -2207,6 +2216,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
22072216
if (tryEmitConstantValue(node)) {
22082217
return;
22092218
}
2219+
2220+
if (languageVersion === ScriptTarget.ES6 &&
2221+
node.expression.kind === SyntaxKind.SuperKeyword &&
2222+
isInAsyncMethodWithSuperInES6(node)) {
2223+
emitSuperAccessInAsyncMethod(node.expression, node.argumentExpression);
2224+
return;
2225+
}
2226+
22102227
emit(node.expression);
22112228
write("[");
22122229
emit(node.argumentExpression);
@@ -2292,21 +2309,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
22922309
&& (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
22932310
}
22942311

2295-
function isInAsyncMethodWithSuperInES6(node: CallExpression) {
2312+
function isInAsyncMethodWithSuperInES6(node: Node) {
22962313
if (languageVersion === ScriptTarget.ES6) {
22972314
const container = getSuperContainer(node, /*includeFunctions*/ false);
2298-
if (container && resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuper) {
2315+
if (container && resolver.getNodeCheckFlags(container) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding)) {
22992316
return true;
23002317
}
23012318
}
23022319

23032320
return false;
23042321
}
23052322

2306-
function emitSuperAccessInAsyncMethod(node: Expression) {
2323+
function emitSuperAccessInAsyncMethod(superNode: Node, argumentExpression: Expression) {
2324+
const container = getSuperContainer(superNode, /*includeFunctions*/ false);
2325+
const isSuperBinding = resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuperBinding;
23072326
write("_super(");
2308-
emit(node);
2309-
write(")");
2327+
emit(argumentExpression);
2328+
write(isSuperBinding ? ").value" : ")");
23102329
}
23112330

23122331
function emitCallExpression(node: CallExpression) {
@@ -2323,26 +2342,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
23232342
superCall = true;
23242343
}
23252344
else {
2326-
if (isSuperPropertyAccess(expression)) {
2327-
superCall = true;
2328-
if (isInAsyncMethodWithSuperInES6(node)) {
2329-
isAsyncMethodWithSuper = true;
2330-
const name = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
2331-
name.text = expression.name.text;
2332-
emitSuperAccessInAsyncMethod(name);
2333-
}
2334-
}
2335-
else if (isSuperElementAccess(expression)) {
2336-
superCall = true;
2337-
if (isInAsyncMethodWithSuperInES6(node)) {
2338-
isAsyncMethodWithSuper = true;
2339-
emitSuperAccessInAsyncMethod(expression.argumentExpression);
2340-
}
2341-
}
2342-
2343-
if (!isAsyncMethodWithSuper) {
2344-
emit(expression);
2345-
}
2345+
superCall = isSuperPropertyAccess(expression) || isSuperElementAccess(expression);
2346+
isAsyncMethodWithSuper = superCall && isInAsyncMethodWithSuperInES6(node);
2347+
emit(expression);
23462348
}
23472349

23482350
if (superCall && (languageVersion < ScriptTarget.ES6 || isAsyncMethodWithSuper)) {
@@ -4497,8 +4499,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
44974499
increaseIndent();
44984500
writeLine();
44994501

4500-
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
4501-
write("const _super = name => super[name];");
4502+
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
4503+
writeLines(`
4504+
const _super = (function (geti, seti) {
4505+
const cache = Object.create(null);
4506+
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
4507+
})(name => super[name], (name, value) => super[name] = value);`);
4508+
writeLine();
4509+
}
4510+
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
4511+
write(`const _super = name => super[name];`);
45024512
writeLine();
45034513
}
45044514

src/compiler/types.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,13 +2023,14 @@ namespace ts {
20232023
SuperStatic = 0x00000200, // Static 'super' reference
20242024
ContextChecked = 0x00000400, // Contextual types have been assigned
20252025
AsyncMethodWithSuper = 0x00000800,
2026-
CaptureArguments = 0x00001000, // Lexical 'arguments' used in body (for async functions)
2026+
AsyncMethodWithSuperBinding = 0x00001000,
2027+
CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions)
20272028

20282029
// Values for enum members have been computed, and any errors have been reported for them.
2029-
EnumValuesComputed = 0x00002000,
2030-
BlockScopedBindingInLoop = 0x00004000,
2031-
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.
2032-
LoopWithBlockScopedBindingCapturedInFunction = 0x00010000, // Loop that contains block scoped variable captured in closure
2030+
EnumValuesComputed = 0x00004000,
2031+
BlockScopedBindingInLoop = 0x00008000,
2032+
LexicalModuleMergesWithClass = 0x00010000, // Instantiated lexical module declaration is merged with a previous class declaration.
2033+
LoopWithBlockScopedBindingCapturedInFunction = 0x00020000, // Loop that contains block scoped variable captured in closure
20332034
}
20342035

20352036
/* @internal */

tests/baselines/reference/asyncMethodWithSuper_es6.js

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,48 @@ class A {
55
}
66

77
class B extends A {
8-
async y() {
8+
// async method with only call/get on 'super' does not require a binding
9+
async simple() {
10+
// call with property access
911
super.x();
12+
13+
// call with element access
14+
super["x"]();
15+
16+
// property access (read)
17+
const a = super.x;
18+
19+
// element access (read)
20+
const b = super["x"];
21+
}
22+
23+
// async method with assignment/destructuring on 'super' requires a binding
24+
async advanced() {
25+
const f = () => {};
26+
27+
// call with property access
28+
super.x();
29+
30+
// call with element access
1031
super["x"]();
32+
33+
// property access (read)
34+
const a = super.x;
35+
36+
// element access (read)
37+
const b = super["x"];
38+
39+
// property access (assign)
40+
super.x = f;
41+
42+
// element access (assign)
43+
super["x"] = f;
44+
45+
// destructuring assign with property access
46+
({ f: super.x } = { f });
47+
48+
// destructuring assign with element access
49+
({ f: super["x"] } = { f });
1150
}
1251
}
1352

@@ -17,11 +56,44 @@ class A {
1756
}
1857
}
1958
class B extends A {
20-
y() {
59+
// async method with only call/get on 'super' does not require a binding
60+
simple() {
2161
const _super = name => super[name];
2262
return __awaiter(this, void 0, Promise, function* () {
63+
// call with property access
2364
_super("x").call(this);
65+
// call with element access
2466
_super("x").call(this);
67+
// property access (read)
68+
const a = _super("x");
69+
// element access (read)
70+
const b = _super("x");
71+
});
72+
}
73+
// async method with assignment/destructuring on 'super' requires a binding
74+
advanced() {
75+
const _super = (function (geti, seti) {
76+
const cache = Object.create(null);
77+
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
78+
})(name => super[name], (name, value) => super[name] = value);
79+
return __awaiter(this, void 0, Promise, function* () {
80+
const f = () => { };
81+
// call with property access
82+
_super("x").value.call(this);
83+
// call with element access
84+
_super("x").value.call(this);
85+
// property access (read)
86+
const a = _super("x").value;
87+
// element access (read)
88+
const b = _super("x").value;
89+
// property access (assign)
90+
_super("x").value = f;
91+
// element access (assign)
92+
_super("x").value = f;
93+
// destructuring assign with property access
94+
({ f: _super("x").value } = { f });
95+
// destructuring assign with element access
96+
({ f: _super("x").value } = { f });
2597
});
2698
}
2799
}

tests/baselines/reference/asyncMethodWithSuper_es6.symbols

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,92 @@ class B extends A {
1111
>B : Symbol(B, Decl(asyncMethodWithSuper_es6.ts, 3, 1))
1212
>A : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
1313

14-
async y() {
15-
>y : Symbol(y, Decl(asyncMethodWithSuper_es6.ts, 5, 19))
14+
// async method with only call/get on 'super' does not require a binding
15+
async simple() {
16+
>simple : Symbol(simple, Decl(asyncMethodWithSuper_es6.ts, 5, 19))
1617

18+
// call with property access
1719
super.x();
1820
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
1921
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
2022
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
2123

24+
// call with element access
2225
super["x"]();
2326
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
2427
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
28+
29+
// property access (read)
30+
const a = super.x;
31+
>a : Symbol(a, Decl(asyncMethodWithSuper_es6.ts, 15, 13))
32+
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
33+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
34+
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
35+
36+
// element access (read)
37+
const b = super["x"];
38+
>b : Symbol(b, Decl(asyncMethodWithSuper_es6.ts, 18, 13))
39+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
40+
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
41+
}
42+
43+
// async method with assignment/destructuring on 'super' requires a binding
44+
async advanced() {
45+
>advanced : Symbol(advanced, Decl(asyncMethodWithSuper_es6.ts, 19, 5))
46+
47+
const f = () => {};
48+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
49+
50+
// call with property access
51+
super.x();
52+
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
53+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
54+
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
55+
56+
// call with element access
57+
super["x"]();
58+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
59+
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
60+
61+
// property access (read)
62+
const a = super.x;
63+
>a : Symbol(a, Decl(asyncMethodWithSuper_es6.ts, 32, 13))
64+
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
65+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
66+
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
67+
68+
// element access (read)
69+
const b = super["x"];
70+
>b : Symbol(b, Decl(asyncMethodWithSuper_es6.ts, 35, 13))
71+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
72+
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
73+
74+
// property access (assign)
75+
super.x = f;
76+
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
77+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
78+
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
79+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
80+
81+
// element access (assign)
82+
super["x"] = f;
83+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
84+
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
85+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
86+
87+
// destructuring assign with property access
88+
({ f: super.x } = { f });
89+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 44, 10))
90+
>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
91+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
92+
>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
93+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 44, 27))
94+
95+
// destructuring assign with element access
96+
({ f: super["x"] } = { f });
97+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 47, 10))
98+
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
99+
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
100+
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 47, 30))
25101
}
26102
}

0 commit comments

Comments
 (0)