Skip to content

Commit

Permalink
Merge pull request #9175 from Microsoft/transforms-generators
Browse files Browse the repository at this point in the history
[Transforms] Down-level transformations for Async Functions
  • Loading branch information
rbuckton authored Jul 20, 2016
2 parents 1c9df84 + c285767 commit d4ad7f3
Show file tree
Hide file tree
Showing 406 changed files with 20,386 additions and 465 deletions.
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) {
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) {
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

0 comments on commit d4ad7f3

Please sign in to comment.