Alternate approach to fix super calls in async methods.

This commit is contained in:
Ron Buckton
2015-12-01 11:13:54 -08:00
parent b40079e1da
commit 2745895064
14 changed files with 176 additions and 32 deletions
+9 -5
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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); } }
+1 -1
View File
@@ -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
}
}
+1 -1
View File
@@ -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"]();
}
}