mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Alternate approach to fix super calls in async methods.
This commit is contained in:
@@ -6756,7 +6756,6 @@ namespace ts {
|
||||
|
||||
if (node.parserContextFlags & ParserContextFlags.Await) {
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments;
|
||||
getNodeLinks(node).flags |= NodeCheckFlags.LexicalArguments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6934,6 +6933,11 @@ namespace ts {
|
||||
|
||||
getNodeLinks(node).flags |= nodeCheckFlag;
|
||||
|
||||
// Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference.
|
||||
if (container.kind === SyntaxKind.MethodDeclaration && container.flags & NodeFlags.Async) {
|
||||
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
|
||||
}
|
||||
|
||||
if (needToCaptureLexicalThis) {
|
||||
// call expressions are allowed only in constructors so they should always capture correct 'this'
|
||||
// super property access expressions can also appear in arrow functions -
|
||||
@@ -9858,7 +9862,7 @@ namespace ts {
|
||||
return aggregatedTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
*TypeScript Specification 1.0 (6.3) - July 2014
|
||||
* An explicitly typed function whose return type isn't the Void or the Any type
|
||||
* must have at least one return statement somewhere in its body.
|
||||
@@ -9884,15 +9888,15 @@ namespace ts {
|
||||
const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn;
|
||||
|
||||
if (returnType && !hasExplicitReturn) {
|
||||
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
|
||||
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
|
||||
// this function does not conform to the specification.
|
||||
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
|
||||
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
|
||||
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
|
||||
}
|
||||
else if (compilerOptions.noImplicitReturns) {
|
||||
if (!returnType) {
|
||||
// If return type annotation is omitted check if function has any explicit return statements.
|
||||
// If it does not have any - its inferred return type is void - don't do any checks.
|
||||
// If it does not have any - its inferred return type is void - don't do any checks.
|
||||
// Otherwise get inferred return type from function body and report error only if it is not void / anytype
|
||||
const inferredReturnType = hasExplicitReturn
|
||||
? getReturnTypeOfSignature(getSignatureFromDeclaration(func))
|
||||
|
||||
+64
-19
@@ -321,7 +321,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
const awaiterHelper = `
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
generator = generator.call(thisArg, _arguments);
|
||||
generator = generator.apply(thisArg, _arguments);
|
||||
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
|
||||
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
|
||||
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
|
||||
@@ -1496,11 +1496,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
}
|
||||
|
||||
function emitExpressionIdentifier(node: Identifier) {
|
||||
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalArguments) {
|
||||
write("_arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
const container = resolver.getReferencedExportContainer(node);
|
||||
if (container) {
|
||||
if (container.kind === SyntaxKind.SourceFile) {
|
||||
@@ -2287,23 +2282,72 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
write(")");
|
||||
}
|
||||
|
||||
function isSuperPropertyAccess(node: Expression): node is PropertyAccessExpression {
|
||||
return node.kind === SyntaxKind.PropertyAccessExpression
|
||||
&& (<PropertyAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
|
||||
}
|
||||
|
||||
function isSuperElementAccess(node: Expression): node is ElementAccessExpression {
|
||||
return node.kind === SyntaxKind.ElementAccessExpression
|
||||
&& (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
|
||||
}
|
||||
|
||||
function isInAsyncMethodWithSuperInES6(node: CallExpression) {
|
||||
if (languageVersion === ScriptTarget.ES6) {
|
||||
const container = getSuperContainer(node, /*includeFunctions*/ false);
|
||||
if (container && resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuper) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function emitSuperAccessInAsyncMethod(node: Expression) {
|
||||
write("_super(");
|
||||
emit(node);
|
||||
write(")");
|
||||
}
|
||||
|
||||
function emitCallExpression(node: CallExpression) {
|
||||
if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) {
|
||||
emitCallWithSpread(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const expression = node.expression;
|
||||
let superCall = false;
|
||||
if (node.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
emitSuper(node.expression);
|
||||
let isAsyncMethodWithSuper = false;
|
||||
if (expression.kind === SyntaxKind.SuperKeyword) {
|
||||
emitSuper(expression);
|
||||
superCall = true;
|
||||
}
|
||||
else {
|
||||
emit(node.expression);
|
||||
superCall = node.expression.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.expression).expression.kind === SyntaxKind.SuperKeyword;
|
||||
if (isSuperPropertyAccess(expression)) {
|
||||
superCall = true;
|
||||
if (isInAsyncMethodWithSuperInES6(node)) {
|
||||
isAsyncMethodWithSuper = true;
|
||||
const name = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
|
||||
name.text = expression.name.text;
|
||||
emitSuperAccessInAsyncMethod(name);
|
||||
}
|
||||
}
|
||||
else if (isSuperElementAccess(expression)) {
|
||||
superCall = true;
|
||||
if (isInAsyncMethodWithSuperInES6(node)) {
|
||||
isAsyncMethodWithSuper = true;
|
||||
emitSuperAccessInAsyncMethod(expression.argumentExpression);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAsyncMethodWithSuper) {
|
||||
emit(expression);
|
||||
}
|
||||
}
|
||||
if (superCall && languageVersion < ScriptTarget.ES6) {
|
||||
|
||||
if (superCall && (languageVersion < ScriptTarget.ES6 || isAsyncMethodWithSuper)) {
|
||||
write(".call(");
|
||||
emitThis(node.expression);
|
||||
emitThis(expression);
|
||||
if (node.arguments.length) {
|
||||
write(", ");
|
||||
emitCommaList(node.arguments);
|
||||
@@ -2980,7 +3024,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
}
|
||||
else {
|
||||
// this is top level converted loop so we need to create an alias for 'this' here
|
||||
// NOTE:
|
||||
// NOTE:
|
||||
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
|
||||
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
|
||||
write(`var ${convertedLoopState.thisName} = this;`);
|
||||
@@ -4452,6 +4496,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
write(" {");
|
||||
increaseIndent();
|
||||
writeLine();
|
||||
|
||||
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
|
||||
write("const _super = name => super[name];");
|
||||
writeLine();
|
||||
}
|
||||
|
||||
write("return");
|
||||
}
|
||||
|
||||
@@ -4472,12 +4522,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
}
|
||||
|
||||
// Emit the call to __awaiter.
|
||||
if (hasLexicalArguments) {
|
||||
write(", function* (_arguments)");
|
||||
}
|
||||
else {
|
||||
write(", function* ()");
|
||||
}
|
||||
write(", function* ()");
|
||||
|
||||
// Emit the signature and body for the inner generator function.
|
||||
emitFunctionBody(node);
|
||||
|
||||
@@ -2022,7 +2022,7 @@ namespace ts {
|
||||
SuperInstance = 0x00000100, // Instance 'super' reference
|
||||
SuperStatic = 0x00000200, // Static 'super' reference
|
||||
ContextChecked = 0x00000400, // Contextual types have been assigned
|
||||
LexicalArguments = 0x00000800,
|
||||
AsyncMethodWithSuper = 0x00000800,
|
||||
CaptureArguments = 0x00001000, // Lexical 'arguments' used in body (for async functions)
|
||||
|
||||
// Values for enum members have been computed, and any errors have been reported for them.
|
||||
|
||||
@@ -11,6 +11,6 @@ class C {
|
||||
class C {
|
||||
method() {
|
||||
function other() { }
|
||||
var fn = () => __awaiter(this, arguments, Promise, function* (_arguments) { return yield other.apply(this, _arguments); });
|
||||
var fn = () => __awaiter(this, arguments, Promise, function* () { return yield other.apply(this, arguments); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ module M {
|
||||
//// [asyncAwaitIsolatedModules_es6.js]
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
generator = generator.call(thisArg, _arguments);
|
||||
generator = generator.apply(thisArg, _arguments);
|
||||
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
|
||||
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
|
||||
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
|
||||
|
||||
@@ -42,7 +42,7 @@ module M {
|
||||
//// [asyncAwait_es6.js]
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
generator = generator.call(thisArg, _arguments);
|
||||
generator = generator.apply(thisArg, _arguments);
|
||||
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
|
||||
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
|
||||
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//// [asyncMethodWithSuper_es6.ts]
|
||||
class A {
|
||||
x() {
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
async y() {
|
||||
super.x();
|
||||
super["x"]();
|
||||
}
|
||||
}
|
||||
|
||||
//// [asyncMethodWithSuper_es6.js]
|
||||
class A {
|
||||
x() {
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
y() {
|
||||
const _super = name => super[name];
|
||||
return __awaiter(this, void 0, Promise, function* () {
|
||||
_super("x").call(this);
|
||||
_super("x").call(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
=== tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts ===
|
||||
class A {
|
||||
>A : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
|
||||
|
||||
x() {
|
||||
>x : Symbol(x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
>B : Symbol(B, Decl(asyncMethodWithSuper_es6.ts, 3, 1))
|
||||
>A : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
|
||||
|
||||
async y() {
|
||||
>y : Symbol(y, Decl(asyncMethodWithSuper_es6.ts, 5, 19))
|
||||
|
||||
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))
|
||||
|
||||
super["x"]();
|
||||
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
|
||||
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
=== tests/cases/conformance/async/es6/asyncMethodWithSuper_es6.ts ===
|
||||
class A {
|
||||
>A : A
|
||||
|
||||
x() {
|
||||
>x : () => void
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
>B : B
|
||||
>A : A
|
||||
|
||||
async y() {
|
||||
>y : () => Promise<void>
|
||||
|
||||
super.x();
|
||||
>super.x() : void
|
||||
>super.x : () => void
|
||||
>super : A
|
||||
>x : () => void
|
||||
|
||||
super["x"]();
|
||||
>super["x"]() : void
|
||||
>super["x"] : () => void
|
||||
>super : A
|
||||
>"x" : string
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ function g() { }
|
||||
//// [a.js]
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
generator = generator.call(thisArg, _arguments);
|
||||
generator = generator.apply(thisArg, _arguments);
|
||||
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
|
||||
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
|
||||
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
|
||||
|
||||
@@ -33,7 +33,7 @@ let x1 = () => { use("Test"); }
|
||||
//// [reachabilityChecks7.js]
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
generator = generator.call(thisArg, _arguments);
|
||||
generator = generator.apply(thisArg, _arguments);
|
||||
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
|
||||
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
|
||||
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
|
||||
|
||||
@@ -34,7 +34,7 @@ var Bar = (function (_super) {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
Bar.prototype[symbol] = function () {
|
||||
return _super.prototype[symbol]();
|
||||
return _super.prototype[symbol].call(this);
|
||||
};
|
||||
return Bar;
|
||||
})(Foo);
|
||||
|
||||
@@ -34,7 +34,7 @@ var Bar = (function (_super) {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
Bar[symbol] = function () {
|
||||
return _super[symbol]();
|
||||
return _super[symbol].call(this);
|
||||
};
|
||||
return Bar;
|
||||
})(Foo);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// @target: ES6
|
||||
// @noEmitHelpers: true
|
||||
class A {
|
||||
x() {
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
async y() {
|
||||
super.x();
|
||||
super["x"]();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user