From 3e0c84e43b43bd08ce587cd08882e910a08ce9fc Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 25 Jan 2016 17:44:07 -0800 Subject: [PATCH] PR feedback --- src/compiler/checker.ts | 83 +++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca41e509664..c628f254aba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -376,8 +376,8 @@ namespace ts { const moduleAugmentation = moduleName.parent; if (moduleAugmentation.symbol.valueDeclaration !== moduleAugmentation) { // this is a combined symbol for multiple augmentations within the same file. - // its symbol already has accumulated information for all declarations - // so we need to add it just once - do the work only for first declaration + // its symbol already has accumulated information for all declarations + // so we need to add it just once - do the work only for first declaration Debug.assert(moduleAugmentation.symbol.declarations.length > 1); return; } @@ -386,7 +386,7 @@ namespace ts { mergeSymbolTable(globals, moduleAugmentation.symbol.exports); } else { - // find a module that about to be augmented + // find a module that about to be augmented let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found); if (!mainModule) { return; @@ -810,7 +810,7 @@ namespace ts { } // No static member is present. - // Check if we're in an instance method and look for a relevant instance member. + // Check if we're in an instance method and look for a relevant instance member. if (location === container && !(location.flags & NodeFlags.Static)) { const instanceType = (getDeclaredTypeOfSymbol(classSymbol)).thisType; if (getPropertyOfType(instanceType, name)) { @@ -1161,7 +1161,7 @@ namespace ts { return getMergedSymbol(sourceFile.symbol); } if (moduleNotFoundError) { - // report errors only if it was requested + // report errors only if it was requested error(moduleReferenceLiteral, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); } return undefined; @@ -7115,14 +7115,10 @@ namespace ts { return false; } - function isSuperPropertyAccess(node: Node) { - return node.kind === SyntaxKind.PropertyAccessExpression - && (node).expression.kind === SyntaxKind.SuperKeyword; - } - - function isSuperElementAccess(node: Node) { - return node.kind === SyntaxKind.ElementAccessExpression - && (node).expression.kind === SyntaxKind.SuperKeyword; + function isSuperPropertyOrElementAccess(node: Node) { + return (node.kind === SyntaxKind.PropertyAccessExpression + || node.kind === SyntaxKind.ElementAccessExpression) + && (node).expression.kind === SyntaxKind.SuperKeyword; } function checkSuperExpression(node: Node): Type { @@ -7177,8 +7173,63 @@ 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. + // This is due to the fact that we emit the body of an async function inside of a generator function. As generator + // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper + // uses an arrow function, which is permitted to reference `super`. + // + // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property + // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value + // of a property or indexed access, either as part of an assignment expression or destructuring assignment. + // + // The simplest case is reading a value, in which case we will emit something like the following: + // + // // ts + // ... + // async asyncMethod() { + // let x = await super.asyncMethod(); + // return x; + // } + // ... + // + // // js + // ... + // asyncMethod() { + // const _super = name => super[name]; + // return __awaiter(this, arguments, Promise, function *() { + // let x = yield _super("asyncMethod").call(this); + // return x; + // }); + // } + // ... + // + // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases + // are legal in ES6, but also likely less frequent, we emit the same more complex helper for both scenarios: + // + // // ts + // ... + // async asyncMethod(ar: Promise) { + // [super.a, super.b] = await ar; + // } + // ... + // + // // js + // ... + // asyncMethod(ar) { + // 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, arguments, Promise, function *() { + // [_super("a").value, _super("b").value] = yield ar; + // }); + // } + // ... + // + // This helper creates an object with a "value" property that wraps the `super` property or indexed access for both get and set. + // This is required for destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment + // while a property access can. if (container.kind === SyntaxKind.MethodDeclaration && container.flags & NodeFlags.Async) { - if ((isSuperPropertyAccess(node.parent) || isSuperElementAccess(node.parent)) && isAssignmentTarget(node.parent)) { + if (isSuperPropertyOrElementAccess(node.parent) && isAssignmentTarget(node.parent)) { getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } else { @@ -14224,10 +14275,10 @@ namespace ts { if (isAmbientExternalModule) { if (isExternalModuleAugmentation(node)) { // body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module) - // otherwise we'll be swamped in cascading errors. + // otherwise we'll be swamped in cascading errors. // We can detect if augmentation was applied using following rules: // - augmentation for a global scope is always applied - // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). + // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Merged); if (checkBody) { // body of ambient external module is always a module block