From 76fe68d37834f7de51239a4ecc63674cf6b5885f Mon Sep 17 00:00:00 2001 From: Gabriel Matos Date: Sat, 2 Mar 2019 12:35:38 -0300 Subject: [PATCH] Wrong super keyword async scope resolution Fix runtime error when calling the super class within nested async functions. --- src/compiler/transformers/es2017.ts | 27 ++++++++++--------- .../asyncMethodWithSuperConflict_es6.js | 21 +++++++++++++++ .../asyncMethodWithSuperConflict_es6.symbols | 17 ++++++++++++ .../asyncMethodWithSuperConflict_es6.types | 24 +++++++++++++++++ .../reference/asyncMethodWithSuper_es2017.js | 15 +++++++++++ .../asyncMethodWithSuper_es2017.symbols | 17 ++++++++++++ .../asyncMethodWithSuper_es2017.types | 24 +++++++++++++++++ .../reference/asyncMethodWithSuper_es5.js | 24 +++++++++++++++++ .../asyncMethodWithSuper_es5.symbols | 17 ++++++++++++ .../reference/asyncMethodWithSuper_es5.types | 24 +++++++++++++++++ .../reference/asyncMethodWithSuper_es6.js | 21 +++++++++++++++ .../asyncMethodWithSuper_es6.symbols | 17 ++++++++++++ .../reference/asyncMethodWithSuper_es6.types | 24 +++++++++++++++++ .../asyncMethodWithSuperConflict_es6.ts | 8 ++++++ .../es2017/asyncMethodWithSuper_es2017.ts | 8 ++++++ .../async/es5/asyncMethodWithSuper_es5.ts | 8 ++++++ .../async/es6/asyncMethodWithSuper_es6.ts | 8 ++++++ 17 files changed, 291 insertions(+), 13 deletions(-) diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index e9e612f0c0941..fec83c2180dac 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -35,7 +35,7 @@ namespace ts { /** * Keeps track of property names accessed on super (`super.x`) within async functions. */ - let capturedSuperProperties: UnderscoreEscapedMap; + let capturedSuperProperties: UnderscoreEscapedMap | undefined; /** Whether the async function contains an element access on super (`super[x]`). */ let hasSuperElementAccess: boolean; /** A set of node IDs for generated super accessors (variable statements). */ @@ -86,13 +86,15 @@ namespace ts { return visitArrowFunction(node); case SyntaxKind.PropertyAccessExpression: - if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) { + if (isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) { + capturedSuperProperties = capturedSuperProperties || createUnderscoreEscapedMap(); capturedSuperProperties.set(node.name.escapedText, true); } + return visitEachChild(node, visitor, context); case SyntaxKind.ElementAccessExpression: - if (capturedSuperProperties && (node).expression.kind === SyntaxKind.SuperKeyword) { + if ((node).expression.kind === SyntaxKind.SuperKeyword) { hasSuperElementAccess = true; } return visitEachChild(node, visitor, context); @@ -418,11 +420,6 @@ namespace ts { recordDeclarationName(parameter, enclosingFunctionParameterNames); } - const savedCapturedSuperProperties = capturedSuperProperties; - const savedHasSuperElementAccess = hasSuperElementAccess; - capturedSuperProperties = createUnderscoreEscapedMap(); - hasSuperElementAccess = false; - let result: ConciseBody; if (!isArrowFunction) { const statements: Statement[] = []; @@ -446,9 +443,15 @@ namespace ts { if (emitSuperHelpers) { enableSubstitutionForAsyncMethodsWithSuper(); - const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties); - substitutedSuperAccessors[getNodeId(variableStatement)] = true; - insertStatementsAfterStandardPrologue(statements, [variableStatement]); + // Do not create the _super variable if do not have any super call. + if (capturedSuperProperties) { + const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties); + substitutedSuperAccessors[getNodeId(variableStatement)] = true; + insertStatementsAfterStandardPrologue(statements, [variableStatement]); + + // Clean up to not affect the next method with previous method variables. + capturedSuperProperties = undefined; + } } const block = createBlock(statements, /*multiLine*/ true); @@ -485,8 +488,6 @@ namespace ts { } enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames; - capturedSuperProperties = savedCapturedSuperProperties; - hasSuperElementAccess = savedHasSuperElementAccess; return result; } diff --git a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.js b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.js index 2a91022670358..a84502d95e8e9 100644 --- a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.js +++ b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.js @@ -56,6 +56,14 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } @@ -128,4 +136,17 @@ class B extends A { ({ f: _superIndex_1("x").value } = { f }); }); } + // inner async super call + inner() { + const _superIndex_1 = name => super[name]; + const _super_1 = Object.create(null, { + x: { get: () => super.x } + }); + return __awaiter(this, void 0, void 0, function* () { + (() => __awaiter(this, void 0, void 0, function* () { + _superIndex_1("x").call(this); + _super_1.x.call(this); + }))(); + }); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.symbols b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.symbols index 161b6eca39b7b..8d067343e4f3f 100644 --- a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.symbols +++ b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.symbols @@ -120,5 +120,22 @@ class B extends A { >"x" : Symbol(A.x, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 9)) >f : Symbol(f, Decl(asyncMethodWithSuperConflict_es6.ts, 55, 30)) } + + // inner async super call + async inner() { +>inner : Symbol(B.inner, Decl(asyncMethodWithSuperConflict_es6.ts, 56, 5)) + + (async () => { + super["x"](); +>super : Symbol(A, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 0)) +>"x" : Symbol(A.x, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 9)) + + super.x(); +>super.x : Symbol(A.x, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 9)) +>super : Symbol(A, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 0)) +>x : Symbol(A.x, Decl(asyncMethodWithSuperConflict_es6.ts, 0, 9)) + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.types b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.types index 9efebc2f192de..07310e50e2f00 100644 --- a/tests/baselines/reference/asyncMethodWithSuperConflict_es6.types +++ b/tests/baselines/reference/asyncMethodWithSuperConflict_es6.types @@ -146,5 +146,29 @@ class B extends A { >{ f } : { f: () => void; } >f : () => void } + + // inner async super call + async inner() { +>inner : () => Promise + + (async () => { +>(async () => { super["x"](); super.x(); })() : Promise +>(async () => { super["x"](); super.x(); }) : () => Promise +>async () => { super["x"](); super.x(); } : () => Promise + + super["x"](); +>super["x"]() : void +>super["x"] : () => void +>super : A +>"x" : "x" + + super.x(); +>super.x() : void +>super.x : () => void +>super : A +>x : () => void + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es2017.js b/tests/baselines/reference/asyncMethodWithSuper_es2017.js index 4c5a7aa9866fc..0c963850f0925 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es2017.js +++ b/tests/baselines/reference/asyncMethodWithSuper_es2017.js @@ -52,6 +52,14 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } @@ -96,4 +104,11 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es2017.symbols b/tests/baselines/reference/asyncMethodWithSuper_es2017.symbols index bee97d8a0069f..7ba13f54e4623 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es2017.symbols +++ b/tests/baselines/reference/asyncMethodWithSuper_es2017.symbols @@ -108,5 +108,22 @@ class B extends A { >"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es2017.ts, 0, 9)) >f : Symbol(f, Decl(asyncMethodWithSuper_es2017.ts, 51, 30)) } + + // inner async super call + async inner() { +>inner : Symbol(B.inner, Decl(asyncMethodWithSuper_es2017.ts, 52, 5)) + + (async () => { + super["x"](); +>super : Symbol(A, Decl(asyncMethodWithSuper_es2017.ts, 0, 0)) +>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es2017.ts, 0, 9)) + + super.x(); +>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es2017.ts, 0, 9)) +>super : Symbol(A, Decl(asyncMethodWithSuper_es2017.ts, 0, 0)) +>x : Symbol(A.x, Decl(asyncMethodWithSuper_es2017.ts, 0, 9)) + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es2017.types b/tests/baselines/reference/asyncMethodWithSuper_es2017.types index b76b13798b868..7deea3c7c6c9b 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es2017.types +++ b/tests/baselines/reference/asyncMethodWithSuper_es2017.types @@ -130,5 +130,29 @@ class B extends A { >{ f } : { f: () => void; } >f : () => void } + + // inner async super call + async inner() { +>inner : () => Promise + + (async () => { +>(async () => { super["x"](); super.x(); })() : Promise +>(async () => { super["x"](); super.x(); }) : () => Promise +>async () => { super["x"](); super.x(); } : () => Promise + + super["x"](); +>super["x"]() : void +>super["x"] : () => void +>super : A +>"x" : "x" + + super.x(); +>super.x() : void +>super.x : () => void +>super : A +>x : () => void + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es5.js b/tests/baselines/reference/asyncMethodWithSuper_es5.js index 441ad8875c412..c3dc486764299 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es5.js +++ b/tests/baselines/reference/asyncMethodWithSuper_es5.js @@ -52,6 +52,14 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } @@ -111,5 +119,21 @@ var B = /** @class */ (function (_super) { }); }); }; + // inner async super call + B.prototype.inner = function () { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + (function () { return __awaiter(_this, void 0, void 0, function () { + return __generator(this, function (_a) { + _super.prototype["x"].call(this); + _super.prototype.x.call(this); + return [2 /*return*/]; + }); + }); })(); + return [2 /*return*/]; + }); + }); + }; return B; }(A)); diff --git a/tests/baselines/reference/asyncMethodWithSuper_es5.symbols b/tests/baselines/reference/asyncMethodWithSuper_es5.symbols index c013058c85a4f..753f163bb1c16 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es5.symbols +++ b/tests/baselines/reference/asyncMethodWithSuper_es5.symbols @@ -108,5 +108,22 @@ class B extends A { >"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es5.ts, 0, 9)) >f : Symbol(f, Decl(asyncMethodWithSuper_es5.ts, 51, 30)) } + + // inner async super call + async inner() { +>inner : Symbol(B.inner, Decl(asyncMethodWithSuper_es5.ts, 52, 5)) + + (async () => { + super["x"](); +>super : Symbol(A, Decl(asyncMethodWithSuper_es5.ts, 0, 0)) +>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es5.ts, 0, 9)) + + super.x(); +>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es5.ts, 0, 9)) +>super : Symbol(A, Decl(asyncMethodWithSuper_es5.ts, 0, 0)) +>x : Symbol(A.x, Decl(asyncMethodWithSuper_es5.ts, 0, 9)) + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es5.types b/tests/baselines/reference/asyncMethodWithSuper_es5.types index 58f545dc93b25..e66511223d7a5 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es5.types +++ b/tests/baselines/reference/asyncMethodWithSuper_es5.types @@ -130,5 +130,29 @@ class B extends A { >{ f } : { f: () => void; } >f : () => void } + + // inner async super call + async inner() { +>inner : () => Promise + + (async () => { +>(async () => { super["x"](); super.x(); })() : Promise +>(async () => { super["x"](); super.x(); }) : () => Promise +>async () => { super["x"](); super.x(); } : () => Promise + + super["x"](); +>super["x"]() : void +>super["x"] : () => void +>super : A +>"x" : "x" + + super.x(); +>super.x() : void +>super.x : () => void +>super : A +>x : () => void + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es6.js b/tests/baselines/reference/asyncMethodWithSuper_es6.js index 5ff89d26e9404..768b270ed33cd 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es6.js +++ b/tests/baselines/reference/asyncMethodWithSuper_es6.js @@ -52,6 +52,14 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } @@ -112,4 +120,17 @@ class B extends A { ({ f: _superIndex("x").value } = { f }); }); } + // inner async super call + inner() { + const _superIndex = name => super[name]; + const _super = Object.create(null, { + x: { get: () => super.x } + }); + return __awaiter(this, void 0, void 0, function* () { + (() => __awaiter(this, void 0, void 0, function* () { + _superIndex("x").call(this); + _super.x.call(this); + }))(); + }); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es6.symbols b/tests/baselines/reference/asyncMethodWithSuper_es6.symbols index d33bcf3449ae5..2529fe0a37d02 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es6.symbols +++ b/tests/baselines/reference/asyncMethodWithSuper_es6.symbols @@ -108,5 +108,22 @@ class B extends A { >"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9)) >f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 51, 30)) } + + // inner async super call + async inner() { +>inner : Symbol(B.inner, Decl(asyncMethodWithSuper_es6.ts, 52, 5)) + + (async () => { + super["x"](); +>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0)) +>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9)) + + super.x(); +>super.x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9)) +>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0)) +>x : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9)) + + })(); + } } diff --git a/tests/baselines/reference/asyncMethodWithSuper_es6.types b/tests/baselines/reference/asyncMethodWithSuper_es6.types index 1333beb8f0484..e1269a9225e00 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es6.types +++ b/tests/baselines/reference/asyncMethodWithSuper_es6.types @@ -130,5 +130,29 @@ class B extends A { >{ f } : { f: () => void; } >f : () => void } + + // inner async super call + async inner() { +>inner : () => Promise + + (async () => { +>(async () => { super["x"](); super.x(); })() : Promise +>(async () => { super["x"](); super.x(); }) : () => Promise +>async () => { super["x"](); super.x(); } : () => Promise + + super["x"](); +>super["x"]() : void +>super["x"] : () => void +>super : A +>"x" : "x" + + super.x(); +>super.x() : void +>super.x : () => void +>super : A +>x : () => void + + })(); + } } diff --git a/tests/cases/conformance/async/es2017/asyncMethodWithSuperConflict_es6.ts b/tests/cases/conformance/async/es2017/asyncMethodWithSuperConflict_es6.ts index 88ed3eea5fff4..dd965c95726f6 100644 --- a/tests/cases/conformance/async/es2017/asyncMethodWithSuperConflict_es6.ts +++ b/tests/cases/conformance/async/es2017/asyncMethodWithSuperConflict_es6.ts @@ -56,4 +56,12 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } diff --git a/tests/cases/conformance/async/es2017/asyncMethodWithSuper_es2017.ts b/tests/cases/conformance/async/es2017/asyncMethodWithSuper_es2017.ts index 892acfd6dd2af..4986057bf7df2 100644 --- a/tests/cases/conformance/async/es2017/asyncMethodWithSuper_es2017.ts +++ b/tests/cases/conformance/async/es2017/asyncMethodWithSuper_es2017.ts @@ -53,4 +53,12 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } diff --git a/tests/cases/conformance/async/es5/asyncMethodWithSuper_es5.ts b/tests/cases/conformance/async/es5/asyncMethodWithSuper_es5.ts index c89369d02b7a0..f351caf0b98a5 100644 --- a/tests/cases/conformance/async/es5/asyncMethodWithSuper_es5.ts +++ b/tests/cases/conformance/async/es5/asyncMethodWithSuper_es5.ts @@ -54,4 +54,12 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } } diff --git a/tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts b/tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts index 79d110e3d8595..3757c7cd62211 100644 --- a/tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts +++ b/tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts @@ -53,4 +53,12 @@ class B extends A { // destructuring assign with element access ({ f: super["x"] } = { f }); } + + // inner async super call + async inner() { + (async () => { + super["x"](); + super.x(); + })(); + } }