Support for assignment/destructuring using super in an async method

This commit is contained in:
Ron Buckton
2015-12-01 14:29:37 -08:00
parent 2745895064
commit 67a4fe5d67
7 changed files with 347 additions and 40 deletions

View File

@@ -6902,6 +6902,16 @@ namespace ts {
return false;
}
function isSuperPropertyAccess(node: Node) {
return node.kind === SyntaxKind.PropertyAccessExpression
&& (<PropertyAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
}
function isSuperElementAccess(node: Node) {
return node.kind === SyntaxKind.ElementAccessExpression
&& (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
}
function checkSuperExpression(node: Node): Type {
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
const classDeclaration = getContainingClass(node);
@@ -6935,7 +6945,12 @@ namespace ts {
// 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 ((isSuperPropertyAccess(node.parent) || isSuperElementAccess(node.parent)) && isAssignmentTarget(node.parent)) {
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding;
}
else {
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
}
}
if (needToCaptureLexicalThis) {

View File

@@ -2126,6 +2126,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return;
}
if (languageVersion === ScriptTarget.ES6 &&
node.expression.kind === SyntaxKind.SuperKeyword &&
isInAsyncMethodWithSuperInES6(node)) {
const name = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
name.text = node.name.text;
emitSuperAccessInAsyncMethod(node.expression, name);
return;
}
emit(node.expression);
const indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken);
@@ -2207,6 +2216,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
if (tryEmitConstantValue(node)) {
return;
}
if (languageVersion === ScriptTarget.ES6 &&
node.expression.kind === SyntaxKind.SuperKeyword &&
isInAsyncMethodWithSuperInES6(node)) {
emitSuperAccessInAsyncMethod(node.expression, node.argumentExpression);
return;
}
emit(node.expression);
write("[");
emit(node.argumentExpression);
@@ -2292,10 +2309,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
&& (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
}
function isInAsyncMethodWithSuperInES6(node: CallExpression) {
function isInAsyncMethodWithSuperInES6(node: Node) {
if (languageVersion === ScriptTarget.ES6) {
const container = getSuperContainer(node, /*includeFunctions*/ false);
if (container && resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuper) {
if (container && resolver.getNodeCheckFlags(container) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding)) {
return true;
}
}
@@ -2303,10 +2320,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return false;
}
function emitSuperAccessInAsyncMethod(node: Expression) {
function emitSuperAccessInAsyncMethod(superNode: Node, argumentExpression: Expression) {
const container = getSuperContainer(superNode, /*includeFunctions*/ false);
const isSuperBinding = resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuperBinding;
write("_super(");
emit(node);
write(")");
emit(argumentExpression);
write(isSuperBinding ? ").value" : ")");
}
function emitCallExpression(node: CallExpression) {
@@ -2323,26 +2342,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
superCall = true;
}
else {
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);
}
superCall = isSuperPropertyAccess(expression) || isSuperElementAccess(expression);
isAsyncMethodWithSuper = superCall && isInAsyncMethodWithSuperInES6(node);
emit(expression);
}
if (superCall && (languageVersion < ScriptTarget.ES6 || isAsyncMethodWithSuper)) {
@@ -4497,8 +4499,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
increaseIndent();
writeLine();
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
write("const _super = name => super[name];");
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
writeLines(`
const _super = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`);
writeLine();
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
write(`const _super = name => super[name];`);
writeLine();
}

View File

@@ -2023,13 +2023,14 @@ namespace ts {
SuperStatic = 0x00000200, // Static 'super' reference
ContextChecked = 0x00000400, // Contextual types have been assigned
AsyncMethodWithSuper = 0x00000800,
CaptureArguments = 0x00001000, // Lexical 'arguments' used in body (for async functions)
AsyncMethodWithSuperBinding = 0x00001000,
CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions)
// Values for enum members have been computed, and any errors have been reported for them.
EnumValuesComputed = 0x00002000,
BlockScopedBindingInLoop = 0x00004000,
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.
LoopWithBlockScopedBindingCapturedInFunction = 0x00010000, // Loop that contains block scoped variable captured in closure
EnumValuesComputed = 0x00004000,
BlockScopedBindingInLoop = 0x00008000,
LexicalModuleMergesWithClass = 0x00010000, // Instantiated lexical module declaration is merged with a previous class declaration.
LoopWithBlockScopedBindingCapturedInFunction = 0x00020000, // Loop that contains block scoped variable captured in closure
}
/* @internal */

View File

@@ -5,9 +5,48 @@ class A {
}
class B extends A {
async y() {
// async method with only call/get on 'super' does not require a binding
async simple() {
// call with property access
super.x();
// call with element access
super["x"]();
// property access (read)
const a = super.x;
// element access (read)
const b = super["x"];
}
// async method with assignment/destructuring on 'super' requires a binding
async advanced() {
const f = () => {};
// call with property access
super.x();
// call with element access
super["x"]();
// property access (read)
const a = super.x;
// element access (read)
const b = super["x"];
// property access (assign)
super.x = f;
// element access (assign)
super["x"] = f;
// destructuring assign with property access
({ f: super.x } = { f });
// destructuring assign with element access
({ f: super["x"] } = { f });
}
}
@@ -17,11 +56,44 @@ class A {
}
}
class B extends A {
y() {
// async method with only call/get on 'super' does not require a binding
simple() {
const _super = name => super[name];
return __awaiter(this, void 0, Promise, function* () {
// call with property access
_super("x").call(this);
// call with element access
_super("x").call(this);
// property access (read)
const a = _super("x");
// element access (read)
const b = _super("x");
});
}
// async method with assignment/destructuring on 'super' requires a binding
advanced() {
const _super = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);
return __awaiter(this, void 0, Promise, function* () {
const f = () => { };
// call with property access
_super("x").value.call(this);
// call with element access
_super("x").value.call(this);
// property access (read)
const a = _super("x").value;
// element access (read)
const b = _super("x").value;
// property access (assign)
_super("x").value = f;
// element access (assign)
_super("x").value = f;
// destructuring assign with property access
({ f: _super("x").value } = { f });
// destructuring assign with element access
({ f: _super("x").value } = { f });
});
}
}

View File

@@ -11,16 +11,92 @@ 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))
// async method with only call/get on 'super' does not require a binding
async simple() {
>simple : Symbol(simple, Decl(asyncMethodWithSuper_es6.ts, 5, 19))
// call with property access
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))
// call with element access
super["x"]();
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
// property access (read)
const a = super.x;
>a : Symbol(a, Decl(asyncMethodWithSuper_es6.ts, 15, 13))
>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))
// element access (read)
const b = super["x"];
>b : Symbol(b, Decl(asyncMethodWithSuper_es6.ts, 18, 13))
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
}
// async method with assignment/destructuring on 'super' requires a binding
async advanced() {
>advanced : Symbol(advanced, Decl(asyncMethodWithSuper_es6.ts, 19, 5))
const f = () => {};
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
// call with property access
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))
// call with element access
super["x"]();
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
// property access (read)
const a = super.x;
>a : Symbol(a, Decl(asyncMethodWithSuper_es6.ts, 32, 13))
>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))
// element access (read)
const b = super["x"];
>b : Symbol(b, Decl(asyncMethodWithSuper_es6.ts, 35, 13))
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
// property access (assign)
super.x = f;
>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))
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
// element access (assign)
super["x"] = f;
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 23, 13))
// destructuring assign with property access
({ f: super.x } = { f });
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 44, 10))
>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))
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 44, 27))
// destructuring assign with element access
({ f: super["x"] } = { f });
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 47, 10))
>super : Symbol(A, Decl(asyncMethodWithSuper_es6.ts, 0, 0))
>"x" : Symbol(A.x, Decl(asyncMethodWithSuper_es6.ts, 0, 9))
>f : Symbol(f, Decl(asyncMethodWithSuper_es6.ts, 47, 30))
}
}

View File

@@ -11,19 +11,113 @@ class B extends A {
>B : B
>A : A
async y() {
>y : () => Promise<void>
// async method with only call/get on 'super' does not require a binding
async simple() {
>simple : () => Promise<void>
// call with property access
super.x();
>super.x() : void
>super.x : () => void
>super : A
>x : () => void
// call with element access
super["x"]();
>super["x"]() : void
>super["x"] : () => void
>super : A
>"x" : string
// property access (read)
const a = super.x;
>a : () => void
>super.x : () => void
>super : A
>x : () => void
// element access (read)
const b = super["x"];
>b : () => void
>super["x"] : () => void
>super : A
>"x" : string
}
// async method with assignment/destructuring on 'super' requires a binding
async advanced() {
>advanced : () => Promise<void>
const f = () => {};
>f : () => void
>() => {} : () => void
// call with property access
super.x();
>super.x() : void
>super.x : () => void
>super : A
>x : () => void
// call with element access
super["x"]();
>super["x"]() : void
>super["x"] : () => void
>super : A
>"x" : string
// property access (read)
const a = super.x;
>a : () => void
>super.x : () => void
>super : A
>x : () => void
// element access (read)
const b = super["x"];
>b : () => void
>super["x"] : () => void
>super : A
>"x" : string
// property access (assign)
super.x = f;
>super.x = f : () => void
>super.x : () => void
>super : A
>x : () => void
>f : () => void
// element access (assign)
super["x"] = f;
>super["x"] = f : () => void
>super["x"] : () => void
>super : A
>"x" : string
>f : () => void
// destructuring assign with property access
({ f: super.x } = { f });
>({ f: super.x } = { f }) : { f: () => void; }
>{ f: super.x } = { f } : { f: () => void; }
>{ f: super.x } : { f: () => void; }
>f : () => void
>super.x : () => void
>super : A
>x : () => void
>{ f } : { f: () => void; }
>f : () => void
// destructuring assign with element access
({ f: super["x"] } = { f });
>({ f: super["x"] } = { f }) : { f: () => void; }
>{ f: super["x"] } = { f } : { f: () => void; }
>{ f: super["x"] } : { f: () => void; }
>f : () => void
>super["x"] : () => void
>super : A
>"x" : string
>{ f } : { f: () => void; }
>f : () => void
}
}

View File

@@ -6,8 +6,47 @@ class A {
}
class B extends A {
async y() {
// async method with only call/get on 'super' does not require a binding
async simple() {
// call with property access
super.x();
// call with element access
super["x"]();
// property access (read)
const a = super.x;
// element access (read)
const b = super["x"];
}
// async method with assignment/destructuring on 'super' requires a binding
async advanced() {
const f = () => {};
// call with property access
super.x();
// call with element access
super["x"]();
// property access (read)
const a = super.x;
// element access (read)
const b = super["x"];
// property access (assign)
super.x = f;
// element access (assign)
super["x"] = f;
// destructuring assign with property access
({ f: super.x } = { f });
// destructuring assign with element access
({ f: super["x"] } = { f });
}
}