Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Transforms] Down-level transformations for Async Functions #9175

Merged
merged 31 commits into from
Jul 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d506e92
Early support for generators.
rbuckton Feb 29, 2016
88b38f8
Merge branch 'transforms-transformer-module' into transforms-transfor…
rbuckton Mar 2, 2016
aaf3ab7
Merge branch 'transforms-transformer-module' into transforms-transfor…
rbuckton Mar 2, 2016
c267691
Fixed labels
rbuckton Mar 3, 2016
83b4cbb
merge branch 'transforms-fixMoreSourceMaps' into transforms-transform…
rbuckton May 18, 2016
4ae83fa
Fix issues after merge
rbuckton May 18, 2016
e85d3e6
Merge branch 'transforms' into transforms-generators
rbuckton Jun 13, 2016
0948f73
Cleanup, only support generators for async functions, added emit helper.
rbuckton Jun 13, 2016
8190666
Adds progress indicators to the runtests-parallel build task.
rbuckton Jun 9, 2016
3681e3c
Fixed typo
rbuckton Jun 9, 2016
4afb8c4
Fixes an issue with runtests-parallel when global mocha is not instal…
rbuckton Jun 10, 2016
aa4662e
Fixes runtests-parallel not reporting failure for failed tests.
rbuckton Jun 13, 2016
27931d5
Cleanup, fix linter errors, follow hoisted declarations.
rbuckton Jun 13, 2016
ea61f2b
Added tests and fixed related emit issues.
rbuckton Jun 15, 2016
9073572
Merge branch 'transforms' into transforms-generators
rbuckton Jun 15, 2016
5e31b25
Merge branch 'transforms' into transforms-generators
rbuckton Jun 15, 2016
4eb2a82
Merge branch 'transforms' into transforms-generators
rbuckton Jun 15, 2016
278a350
Add support for external helpers module, cleanup, and accept new base…
rbuckton Jun 15, 2016
c11b560
es6 test conformance cleanup
rbuckton Jun 27, 2016
a858db6
Remove extraneous comments.
rbuckton Jun 27, 2016
4a16f65
Removed extranous original parameter.
rbuckton Jun 27, 2016
393ee28
Move 'use strict' directive out of generator for async function.
rbuckton Jun 27, 2016
5b2e11c
Added es5 conformance tests for await in binary.
rbuckton Jun 27, 2016
48a9562
Added es5 conformance tests for async function declarations. Add abil…
rbuckton Jun 27, 2016
0c647c3
Added es5 conformance tests for awaiting a call
rbuckton Jun 27, 2016
203dab4
Added additional es5 conformance tests, better emit for functions ret…
rbuckton Jun 28, 2016
644e4da
Added es5 conformance tests for async arrow functions, add error for …
rbuckton Jun 28, 2016
9e3d6f8
Additional comments per PR feedback.
rbuckton Jun 28, 2016
252cc25
Merge branch 'transforms' into transforms-generators
rbuckton Jul 18, 2016
c832444
Accept baselines plus new linter warnings.
rbuckton Jul 18, 2016
c285767
Updated comment
rbuckton Jul 18, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var compilerSources = [
"transformers/module/module.ts",
"transformers/jsx.ts",
"transformers/es7.ts",
"transformers/generators.ts",
"transformers/es6.ts",
"transformer.ts",
"sourcemap.ts",
Expand Down Expand Up @@ -82,6 +83,7 @@ var servicesSources = [
"transformers/module/module.ts",
"transformers/jsx.ts",
"transformers/es7.ts",
"transformers/generators.ts",
"transformers/es6.ts",
"transformer.ts",
"sourcemap.ts",
Expand Down Expand Up @@ -747,7 +749,6 @@ function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stack
taskConfigsFolder: taskConfigsFolder,
stackTraceLimit: stackTraceLimit
});
console.log('Running tests with config: ' + testConfigContents);
fs.writeFileSync('test.config', testConfigContents);
}

Expand Down
59 changes: 47 additions & 12 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ namespace ts {
}
}

function checkStrictModeNumericLiteral(node: LiteralExpression) {
function checkStrictModeNumericLiteral(node: NumericLiteral) {
if (inStrictMode && node.isOctalLiteral) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
Expand Down Expand Up @@ -1786,7 +1786,7 @@ namespace ts {
case SyntaxKind.DeleteExpression:
return checkStrictModeDeleteExpression(<DeleteExpression>node);
case SyntaxKind.NumericLiteral:
return checkStrictModeNumericLiteral(<LiteralExpression>node);
return checkStrictModeNumericLiteral(<NumericLiteral>node);
case SyntaxKind.PostfixUnaryExpression:
return checkStrictModePostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.PrefixUnaryExpression:
Expand Down Expand Up @@ -2568,6 +2568,7 @@ namespace ts {
const modifierFlags = getModifierFlags(node);
const body = node.body;
const typeParameters = node.typeParameters;
const asteriskToken = node.asteriskToken;

// A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded,
// generic, or has a decorator.
Expand All @@ -2578,6 +2579,11 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}

// Currently, we only support generators that were originally async function bodies.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.MethodOrAccessorExcludes;
}
Expand Down Expand Up @@ -2625,7 +2631,7 @@ namespace ts {
transformFlags = TransformFlags.AssertTypeScript;
}
else {
transformFlags = subtreeFlags;
transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion;

// If a FunctionDeclaration is exported, then it is either ES6 or TypeScript syntax.
if (modifierFlags & ModifierFlags.Export) {
Expand All @@ -2637,12 +2643,21 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}

// If a FunctionDeclaration has an asterisk token, is exported, or its
// subtree has marked the container as needing to capture the lexical `this`,
// then this node is ES6 syntax.
if (asteriskToken || (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask)) {
// If a FunctionDeclaration's subtree has marked the container as needing to capture the
// lexical this, or the function contains parameters with initializers, then this node is
// ES6 syntax.
if (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask) {
transformFlags |= TransformFlags.AssertES6;
}

// If a FunctionDeclaration is generator function and is the body of a
// transformed async function, then this node can be transformed to a
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
Expand All @@ -2659,12 +2674,22 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}

// If a FunctionExpression contains an asterisk token, or its subtree has marked the container
// as needing to capture the lexical this, then this node is ES6 syntax.
if (asteriskToken || (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask)) {
// If a FunctionExpression's subtree has marked the container as needing to capture the
// lexical this, or the function contains parameters with initializers, then this node is
// ES6 syntax.
if (subtreeFlags & TransformFlags.ES6FunctionSyntaxMask) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think comment need to be updated?

transformFlags |= TransformFlags.AssertES6;
}

// If a FunctionExpression is generator function and is the body of a
// transformed async function, then this node can be transformed to a
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can this node.emitFlags & NodeEmitFlags.AsyncFunctionBody be observed here since the flag is set in the transformer's factory.

Also what happen to the case of asteriskToken but not AsyncFunctionBody?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TransformFlags aggregation happens both during the bind phase and during each successive transformation phase (though usually it has less work to do as it can reuse the flags from unchanged subtrees).

In this case, we only add the flag if the generator was created as a result of an async function transformation.

We do not down-level generators that are not the result of an async function transformation.

transformFlags |= TransformFlags.AssertGenerator;
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.FunctionExcludes;
}
Expand Down Expand Up @@ -2794,7 +2819,7 @@ namespace ts {
}

function computeVariableDeclarationList(node: VariableDeclarationList, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
let transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion;

if (subtreeFlags & TransformFlags.ContainsBindingPattern) {
transformFlags |= TransformFlags.AssertES6;
Expand Down Expand Up @@ -2859,11 +2884,15 @@ namespace ts {
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.ForOfStatement:
case SyntaxKind.YieldExpression:
// These nodes are ES6 syntax.
transformFlags |= TransformFlags.AssertES6;
break;

case SyntaxKind.YieldExpression:
// This node is ES6 syntax.
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsYield;
break;

case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.NeverKeyword:
Expand Down Expand Up @@ -2985,6 +3014,12 @@ namespace ts {
}

break;

case SyntaxKind.ReturnStatement:
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion;
break;
}

node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
Expand Down
49 changes: 29 additions & 20 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ namespace ts {
let getGlobalESSymbolConstructorSymbol: () => Symbol;

let getGlobalPromiseConstructorSymbol: () => Symbol;
let tryGetGlobalPromiseConstructorSymbol: () => Symbol;

let globalObjectType: ObjectType;
let globalFunctionType: ObjectType;
Expand Down Expand Up @@ -8337,10 +8338,13 @@ namespace ts {
// can explicitly bound arguments objects
if (symbol === argumentsSymbol) {
const container = getContainingFunction(node);
if (container.kind === SyntaxKind.ArrowFunction) {
if (languageVersion < ScriptTarget.ES6) {
if (languageVersion < ScriptTarget.ES6) {
if (container.kind === SyntaxKind.ArrowFunction) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression);
}
else if (hasModifier(container, ModifierFlags.Async)) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_async_function_or_method_in_ES3_and_ES5_Consider_using_a_standard_function_or_method);
}
}

if (node.flags & NodeFlags.AwaitContext) {
Expand Down Expand Up @@ -12991,7 +12995,7 @@ namespace ts {
return type;
}

function checkNumericLiteral(node: LiteralExpression): Type {
function checkNumericLiteral(node: NumericLiteral): Type {
// Grammar checking
checkGrammarNumericLiteral(node);
return numberType;
Expand All @@ -13011,7 +13015,7 @@ namespace ts {
case SyntaxKind.FalseKeyword:
return booleanType;
case SyntaxKind.NumericLiteral:
return checkNumericLiteral(<LiteralExpression>node);
return checkNumericLiteral(<NumericLiteral>node);
case SyntaxKind.TemplateExpression:
return checkTemplateExpression(<TemplateExpression>node);
case SyntaxKind.StringLiteral:
Expand Down Expand Up @@ -14194,7 +14198,7 @@ namespace ts {
* @param returnType The return type of a FunctionLikeDeclaration
* @param location The node on which to report the error.
*/
function checkCorrectPromiseType(returnType: Type, location: Node) {
function checkCorrectPromiseType(returnType: Type, location: Node, diagnostic: DiagnosticMessage, typeName?: string) {
if (returnType === unknownType) {
// The return type already had some other error, so we ignore and return
// the unknown type.
Expand All @@ -14213,7 +14217,7 @@ namespace ts {

// The promise type was not a valid type reference to the global promise type, so we
// report an error and return the unknown type.
error(location, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
error(location, diagnostic, typeName);
return unknownType;
}

Expand All @@ -14233,7 +14237,7 @@ namespace ts {
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
if (languageVersion >= ScriptTarget.ES6) {
const returnType = getTypeFromTypeNode(node.type);
return checkCorrectPromiseType(returnType, node.type);
return checkCorrectPromiseType(returnType, node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
}

const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType();
Expand Down Expand Up @@ -14279,19 +14283,19 @@ namespace ts {

const promiseConstructor = getNodeLinks(node.type).resolvedSymbol;
if (!promiseConstructor || !symbolIsValue(promiseConstructor)) {
// try to fall back to global promise type.
const typeName = promiseConstructor
? symbolToString(promiseConstructor)
: typeToString(promiseType);
error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeName);
return unknownType;
return checkCorrectPromiseType(promiseType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeName);
}

// If the Promise constructor, resolved locally, is an alias symbol we should mark it as referenced.
checkReturnTypeAnnotationAsExpression(node);

// Validate the promise constructor type.
const promiseConstructorType = getTypeOfSymbol(promiseConstructor);
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) {
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) {
return unknownType;
}

Expand Down Expand Up @@ -16272,7 +16276,7 @@ namespace ts {
}
return undefined;
case SyntaxKind.NumericLiteral:
return +(<LiteralExpression>e).text;
return +(<NumericLiteral>e).text;
case SyntaxKind.ParenthesizedExpression:
return evalConstant((<ParenthesizedExpression>e).expression);
case SyntaxKind.Identifier:
Expand Down Expand Up @@ -17491,7 +17495,7 @@ namespace ts {
if (objectType === unknownType) return undefined;
const apparentType = getApparentType(objectType);
if (apparentType === unknownType) return undefined;
return getPropertyOfType(apparentType, (<LiteralExpression>node).text);
return getPropertyOfType(apparentType, (<NumericLiteral>node).text);
}
break;
}
Expand Down Expand Up @@ -17976,6 +17980,11 @@ namespace ts {
function getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind {
// Resolve the symbol as a value to ensure the type can be reached at runtime during emit.
const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location);
const globalPromiseSymbol = tryGetGlobalPromiseConstructorSymbol();
if (globalPromiseSymbol && valueSymbol === globalPromiseSymbol) {
return TypeReferenceSerializationKind.Promise;
}

const constructorType = valueSymbol ? getTypeOfSymbol(valueSymbol) : undefined;
if (constructorType && isConstructorType(constructorType)) {
return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue;
Expand All @@ -17994,8 +18003,8 @@ namespace ts {
else if (type.flags & TypeFlags.Any) {
return TypeReferenceSerializationKind.ObjectType;
}
else if (isTypeOfKind(type, TypeFlags.Void)) {
return TypeReferenceSerializationKind.VoidType;
else if (isTypeOfKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) {
return TypeReferenceSerializationKind.VoidNullableOrNeverType;
}
else if (isTypeOfKind(type, TypeFlags.Boolean)) {
return TypeReferenceSerializationKind.BooleanType;
Expand Down Expand Up @@ -18293,6 +18302,7 @@ namespace ts {
getGlobalPromiseLikeType = memoize(() => getGlobalType("PromiseLike", /*arity*/ 1));
getInstantiatedGlobalPromiseLikeType = memoize(createInstantiatedPromiseLikeType);
getGlobalPromiseConstructorSymbol = memoize(() => getGlobalValueSymbol("Promise"));
tryGetGlobalPromiseConstructorSymbol = memoize(() => getGlobalSymbol("Promise", SymbolFlags.Value, /*diagnostic*/ undefined) && getGlobalPromiseConstructorSymbol());
getGlobalPromiseConstructorLikeType = memoize(() => getGlobalType("PromiseConstructorLike"));
getGlobalThenableType = memoize(createThenableType);

Expand Down Expand Up @@ -18348,6 +18358,9 @@ namespace ts {
}
if (requestedExternalEmitHelpers & NodeFlags.HasAsyncFunctions) {
verifyHelperSymbol(exports, "__awaiter", SymbolFlags.Value);
if (languageVersion < ScriptTarget.ES6) {
verifyHelperSymbol(exports, "__generator", SymbolFlags.Value);
}
}
}
}
Expand Down Expand Up @@ -18654,10 +18667,6 @@ namespace ts {
}

function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean {
if (languageVersion < ScriptTarget.ES6) {
return grammarErrorOnNode(asyncModifier, Diagnostics.Async_functions_are_only_available_when_targeting_ECMAScript_2015_or_higher);
}

switch (node.kind) {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionDeclaration:
Expand Down Expand Up @@ -18967,7 +18976,7 @@ namespace ts {
// Grammar checking for computedPropertyName and shorthandPropertyAssignment
checkGrammarForInvalidQuestionMark(prop, (<PropertyAssignment>prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional);
if (name.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(<LiteralExpression>name);
checkGrammarNumericLiteral(<NumericLiteral>name);
}
currentKind = Property;
}
Expand Down Expand Up @@ -19489,7 +19498,7 @@ namespace ts {
}
}

function checkGrammarNumericLiteral(node: LiteralExpression): boolean {
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
// Grammar checking
if (node.isOctalLiteral && languageVersion >= ScriptTarget.ES5) {
return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher);
Expand Down
Loading