diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4ea2489de8886..08233c24fa013 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -37,7 +37,7 @@ namespace ts { return ModuleInstanceState.ConstEnumOnly; } // 3. non-exported import declarations - else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(node.flags & NodeFlags.Export)) { + else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(hasModifier(node, ModifierFlags.Export))) { return ModuleInstanceState.NonInstantiated; } // 4. other uninstantiated module declarations. @@ -133,7 +133,7 @@ namespace ts { let classifiableNames: Map; // state used to aggregate transform flags during bind. - let subtreeTransformFlags: TransformFlags; + let subtreeTransformFlags: TransformFlags = TransformFlags.None; let skipTransformFlagAggregation: boolean; function bindSourceFile(f: SourceFile, opts: CompilerOptions) { @@ -141,7 +141,6 @@ namespace ts { options = opts; inStrictMode = !!file.externalModuleIndicator; classifiableNames = {}; - subtreeTransformFlags = undefined; skipTransformFlagAggregation = isDeclarationFile(file); Symbol = objectAllocator.getSymbolConstructor(); @@ -167,6 +166,7 @@ namespace ts { hasAsyncFunctions = false; hasDecorators = false; hasParameterDecorators = false; + subtreeTransformFlags = TransformFlags.None; } return bindSourceFile; @@ -256,7 +256,7 @@ namespace ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.ClassDeclaration: - return node.flags & NodeFlags.Default ? "default" : undefined; + return hasModifier(node, ModifierFlags.Default) ? "default" : undefined; case SyntaxKind.JSDocFunctionType: return isJSDocConstructSignature(node) ? "__new" : "__call"; case SyntaxKind.Parameter: @@ -284,7 +284,7 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - const isDefaultExport = node.flags & NodeFlags.Default; + const isDefaultExport = hasModifier(node, ModifierFlags.Default); // The exported symbol for an export default function/class node is always named "default" const name = isDefaultExport && parent ? "default" : getDeclarationName(node); @@ -329,7 +329,7 @@ namespace ts { : Diagnostics.Duplicate_identifier_0; forEach(symbol.declarations, declaration => { - if (declaration.flags & NodeFlags.Default) { + if (hasModifier(declaration, ModifierFlags.Default)) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; } }); @@ -353,7 +353,7 @@ namespace ts { } function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { - const hasExportModifier = getCombinedNodeFlags(node) & NodeFlags.Export; + const hasExportModifier = getCombinedModifierFlags(node) & ModifierFlags.Export; if (symbolFlags & SymbolFlags.Alias) { if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); @@ -862,7 +862,7 @@ namespace ts { } function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { - return node.flags & NodeFlags.Static + return hasModifier(node, ModifierFlags.Static) ? declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes) : declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); } @@ -899,7 +899,7 @@ namespace ts { function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (isAmbientModule(node)) { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible); } declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); @@ -1184,41 +1184,22 @@ namespace ts { // symbols we do specialized work when we recurse. For example, we'll keep track of // the current 'container' node when it changes. This helps us know which symbol table // a local should go into for example. - aggregateTransformFlagsIfNeededAndBindChildren(node); - - inStrictMode = savedInStrictMode; - } - - function aggregateTransformFlagsIfNeededAndBindChildren(node: Node) { - if (node.transformFlags !== undefined) { - skipTransformFlagAggregationAndBindChildren(node); - } - else { - aggregateTransformFlagsAndBindChildren(node); + if (skipTransformFlagAggregation) { + bindChildren(node); } - } - - function skipTransformFlagAggregationAndBindChildren(node: Node) { - if (!skipTransformFlagAggregation) { + else if (node.transformFlags & TransformFlags.HasComputedFlags) { skipTransformFlagAggregation = true; bindChildren(node); skipTransformFlagAggregation = false; } else { - bindChildren(node); - } - } - - function aggregateTransformFlagsAndBindChildren(node: Node) { - if (!skipTransformFlagAggregation) { const savedSubtreeTransformFlags = subtreeTransformFlags; subtreeTransformFlags = 0; bindChildren(node); subtreeTransformFlags = savedSubtreeTransformFlags | computeTransformFlagsForNode(node, subtreeTransformFlags); } - else { - bindChildren(node); - } + + inStrictMode = savedInStrictMode; } function updateStrictMode(node: Node) { @@ -1799,15 +1780,9 @@ namespace ts { * @param subtreeFlags Transform flags computed for this node's subtree */ export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags): TransformFlags { - // Ambient nodes are TypeScript syntax and the flags of their subtree are ignored. - if (node.flags & NodeFlags.Ambient) { - return (node.transformFlags = TransformFlags.AssertTypeScript) - & ~(node.excludeTransformFlags = TransformFlags.NodeExcludes); - } - // Mark transformations needed for each node - let transformFlags: TransformFlags; - let excludeFlags: TransformFlags; + let transformFlags = TransformFlags.None; + let excludeFlags = TransformFlags.None; switch (node.kind) { case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: @@ -1823,7 +1798,7 @@ namespace ts { case SyntaxKind.AsExpression: case SyntaxKind.ReadonlyKeyword: // These nodes are TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; break; case SyntaxKind.JsxElement: @@ -1835,12 +1810,12 @@ namespace ts { case SyntaxKind.JsxSpreadAttribute: case SyntaxKind.JsxExpression: // These nodes are Jsx syntax. - transformFlags |= TransformFlags.AssertJsx; + transformFlags = TransformFlags.AssertJsx; break; case SyntaxKind.ExportKeyword: // This node is both ES6 and TypeScript syntax. - transformFlags |= TransformFlags.AssertES6 | TransformFlags.TypeScript; + transformFlags = TransformFlags.AssertES6 | TransformFlags.TypeScript; break; case SyntaxKind.DefaultKeyword: @@ -1854,10 +1829,9 @@ namespace ts { case SyntaxKind.ForOfStatement: case SyntaxKind.YieldExpression: // These nodes are ES6 syntax. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; break; - case SyntaxKind.AnyKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.StringKeyword: @@ -1886,36 +1860,42 @@ namespace ts { case SyntaxKind.ThisType: case SyntaxKind.StringLiteralType: // Types and signatures are TypeScript syntax, and exclude all other facts. + subtreeFlags = TransformFlags.None; excludeFlags = TransformFlags.TypeExcludes; - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; break; case SyntaxKind.ComputedPropertyName: // Even though computed property names are ES6, we don't treat them as such. // This is so that they can flow through PropertyName transforms unaffected. // Instead, we mark the container as ES6, so that it can properly handle the transform. - transformFlags |= TransformFlags.ContainsComputedPropertyName; + transformFlags = TransformFlags.ContainsComputedPropertyName; break; case SyntaxKind.SpreadElementExpression: // This node is ES6 syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadElementExpression; + transformFlags = TransformFlags.ContainsSpreadElementExpression; break; case SyntaxKind.SuperKeyword: // This node is ES6 syntax. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; break; case SyntaxKind.ThisKeyword: // Mark this node and its ancestors as containing a lexical `this` keyword. - transformFlags |= TransformFlags.ContainsLexicalThis; + transformFlags = TransformFlags.ContainsLexicalThis; break; case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: // These nodes are ES6 syntax. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; + break; + + case SyntaxKind.Decorator: + // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. + transformFlags = TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators; break; case SyntaxKind.ObjectLiteralExpression: @@ -1923,19 +1903,12 @@ namespace ts { if (subtreeFlags & TransformFlags.ContainsComputedPropertyName) { // If an ObjectLiteralExpression contains a ComputedPropertyName, then it // is an ES6 node. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; } break; case SyntaxKind.CallExpression: - excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression - || isSuperCall(node)) { - // If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6 - // node. - transformFlags |= TransformFlags.AssertES6; - } - break; + return computeCallExpression(node, subtreeFlags); case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.NewExpression: @@ -1943,163 +1916,66 @@ namespace ts { if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) { // If the this node contains a SpreadElementExpression, then it is an ES6 // node. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; } - break; - case SyntaxKind.Decorator: - // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators; break; case SyntaxKind.ModuleDeclaration: + // An ambient declaration is TypeScript syntax. + if (hasModifier(node, ModifierFlags.Ambient)) { + subtreeFlags = TransformFlags.None; + } + // This node is TypeScript syntax, and excludes markers that should not escape the module scope. excludeFlags = TransformFlags.ModuleExcludes; - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; break; case SyntaxKind.ParenthesizedExpression: - // If the node is synthesized, it means the emitter put the parentheses there, - // not the user. If we didn't want them, the emitter would not have put them - // there. - if (!nodeIsSynthesized(node)) { - if ((node).expression.kind === SyntaxKind.AsExpression - || (node).expression.kind === SyntaxKind.TypeAssertionExpression) { - transformFlags = TransformFlags.AssertTypeScript; - } - } - - break; + return computeParenthesizedExpression(node, subtreeFlags); case SyntaxKind.BinaryExpression: - if (isDestructuringAssignment(node)) { - // Destructuring assignments are ES6 syntax. - transformFlags |= TransformFlags.AssertES6; - } - else if ((node).operatorToken.kind === SyntaxKind.AsteriskAsteriskToken - || (node).operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { - // Exponentiation is ES7 syntax. - transformFlags |= TransformFlags.AssertES7; + return computeBinaryExpression(node, subtreeFlags); + + case SyntaxKind.ExpressionStatement: + // If the expression of an expression statement is a destructuring assignment, + // then we treat the statement as ES6 so that we can indicate that we do not + // need to hold on to the right-hand side. + if ((node).expression.transformFlags & TransformFlags.DestructuringAssignment) { + transformFlags = TransformFlags.AssertES6; } break; case SyntaxKind.Parameter: - // If the parameter has a question token, then it is TypeScript syntax. - if ((node).questionToken) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // If a parameter has an accessibility modifier, then it is TypeScript syntax. - if ((node).flags & NodeFlags.AccessibilityModifier) { - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; - } - - // If a parameter has an initializer, a binding pattern or a dotDotDot token, then - // it is ES6 syntax and its container must emit default value assignments or parameter destructuring downlevel. - if ((node).initializer - || (node).dotDotDotToken - || isBindingPattern((node).name)) { - transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsDefaultValueAssignments; - } - - break; + return computeParameter(node, subtreeFlags); case SyntaxKind.ArrowFunction: - // An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction. - excludeFlags = TransformFlags.ArrowFunctionExcludes; - transformFlags = TransformFlags.AssertES6; - - // If an ArrowFunction contains a lexical this, its container must capture the lexical this. - if (subtreeFlags & TransformFlags.ContainsLexicalThis) { - transformFlags |= TransformFlags.ContainsCapturedLexicalThis; - } - - // An async arrow function is TypeScript syntax. - if (node.flags & NodeFlags.Async) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - break; + return computeArrowFunction(node, subtreeFlags); case SyntaxKind.FunctionExpression: - // A FunctionExpression excludes markers that should not escape the scope of a FunctionExpression. - excludeFlags = TransformFlags.FunctionExcludes; - - // 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 ((node).asteriskToken - || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis - || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { - transformFlags |= TransformFlags.AssertES6; - } - - // An async function expression is TypeScript syntax. - if (node.flags & NodeFlags.Async) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - break; + return computeFunctionExpression(node, subtreeFlags); case SyntaxKind.FunctionDeclaration: - // A FunctionDeclaration excludes markers that should not escape the scope of a FunctionDeclaration. - excludeFlags = TransformFlags.FunctionExcludes; - - // A FunctionDeclaration without a body is an overload and is TypeScript syntax. - if (!(node).body) { - transformFlags = TransformFlags.AssertTypeScript; - break; - } - - // 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 ((node).asteriskToken - || node.flags & NodeFlags.Export - || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis - || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { - transformFlags |= TransformFlags.AssertES6; - } - - // An async function declaration is TypeScript syntax. - if (node.flags & NodeFlags.Async) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - break; + return computeFunctionDeclaration(node, subtreeFlags); case SyntaxKind.VariableDeclaration: - // A VariableDeclaration with a binding pattern is ES6 syntax. - if (isBindingPattern((node).name)) { - transformFlags |= TransformFlags.AssertES6; - } - - break; + return computeVariableDeclaration(node, subtreeFlags); case SyntaxKind.VariableDeclarationList: // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. if (node.flags & NodeFlags.BlockScoped) { - transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBlockScopedBinding; + transformFlags = TransformFlags.AssertES6 | TransformFlags.ContainsBlockScopedBinding; } break; case SyntaxKind.VariableStatement: - // If a VariableStatement is exported, then it is either ES6 or TypeScript syntax. - if (node.flags & NodeFlags.Export) { - transformFlags |= TransformFlags.AssertES6 | TransformFlags.AssertTypeScript; - } - - break; + return computeVariableStatement(node, subtreeFlags); case SyntaxKind.LabeledStatement: - // A labeled statement containing a block scoped binding *may* need to be transformed from ES6. - if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding - && isIterationStatement(this, /*lookInLabeledStatements*/ true)) { - transformFlags |= TransformFlags.AssertES6; - } - - break; + return computeLabeledStatement(node, subtreeFlags); case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: @@ -2107,36 +1983,26 @@ namespace ts { case SyntaxKind.ForInStatement: // A loop containing a block scoped binding *may* need to be transformed from ES6. if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding) { - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; } break; case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - // A ClassDeclarations or ClassExpression is ES6 syntax. - excludeFlags = TransformFlags.ClassExcludes; - transformFlags = TransformFlags.AssertES6; - - // A class with a parameter property assignment, property initializer, or decorator is - // TypeScript syntax. - if (subtreeFlags & TransformFlags.ContainsParameterPropertyAssignments - || subtreeFlags & TransformFlags.ContainsPropertyInitializer - || subtreeFlags & TransformFlags.ContainsDecorators) { - transformFlags |= TransformFlags.AssertTypeScript; - } + return computeClassDeclaration(node, subtreeFlags); - break; + case SyntaxKind.ClassExpression: + return computeClassExpression(node, subtreeFlags); case SyntaxKind.HeritageClause: if ((node).token === SyntaxKind.ExtendsKeyword) { // An `extends` HeritageClause is ES6 syntax. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; } else { // An `implements` HeritageClause is TypeScript syntax. Debug.assert((node).token === SyntaxKind.ImplementsKeyword); - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; } break; @@ -2144,7 +2010,7 @@ namespace ts { case SyntaxKind.ExpressionWithTypeArguments: // An ExpressionWithTypeArguments is ES6 syntax, as it is used in the // extends clause of a class. - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; // If an ExpressionWithTypeArguments contains type arguments, then it // is TypeScript syntax. @@ -2157,7 +2023,7 @@ namespace ts { case SyntaxKind.Constructor: // A Constructor is ES6 syntax. excludeFlags = TransformFlags.ConstructorExcludes; - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; // An overload constructor is TypeScript syntax. if (!(node).body) { @@ -2168,7 +2034,7 @@ namespace ts { case SyntaxKind.PropertyDeclaration: // A PropertyDeclaration is TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; // If the PropertyDeclaration has an initializer, we need to inform its ancestor // so that it handle the transformation. @@ -2181,14 +2047,13 @@ namespace ts { case SyntaxKind.MethodDeclaration: // A MethodDeclaration is ES6 syntax. excludeFlags = TransformFlags.MethodOrAccessorExcludes; - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; // A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded, // generic, or has both a computed property name and a decorator. if ((node).body === undefined || (node).typeParameters !== undefined - || node.flags & NodeFlags.Async - || node.flags & NodeFlags.Abstract + || hasModifier(node, ModifierFlags.Async | ModifierFlags.Abstract) || (subtreeFlags & TransformFlags.ContainsDecorators && subtreeFlags & TransformFlags.ContainsComputedPropertyName)) { transformFlags |= TransformFlags.AssertTypeScript; @@ -2203,10 +2068,10 @@ namespace ts { // A GetAccessor or SetAccessor is TypeScript syntax if it is either abstract, // or has both a computed property name and a decorator. - if (node.flags & NodeFlags.Abstract || - subtreeFlags & TransformFlags.ContainsDecorators && - subtreeFlags & TransformFlags.ContainsComputedPropertyName) { - transformFlags |= TransformFlags.AssertTypeScript; + if (hasModifier(node, ModifierFlags.Abstract) + || (subtreeFlags & TransformFlags.ContainsDecorators + && subtreeFlags & TransformFlags.ContainsComputedPropertyName)) { + transformFlags = TransformFlags.AssertTypeScript; } break; @@ -2214,7 +2079,7 @@ namespace ts { case SyntaxKind.ImportEqualsDeclaration: // An ImportEqualsDeclaration with a namespace reference is TypeScript. if (!isExternalModuleImportEqualsDeclaration(node)) { - transformFlags |= TransformFlags.AssertTypeScript; + transformFlags = TransformFlags.AssertTypeScript; } break; @@ -2223,20 +2088,258 @@ namespace ts { // If a PropertyAccessExpression starts with a super keyword, then it is // ES6 syntax, and requires a lexical `this` binding. if ((node).expression.kind === SyntaxKind.SuperKeyword) { - transformFlags |= TransformFlags.ContainsLexicalThis; + transformFlags = TransformFlags.ContainsLexicalThis; } break; case SyntaxKind.SourceFile: if (subtreeFlags & TransformFlags.ContainsCapturedLexicalThis) { - transformFlags |= TransformFlags.AssertES6; + transformFlags = TransformFlags.AssertES6; } break; } - return (node.transformFlags = subtreeFlags | transformFlags) - & ~(node.excludeTransformFlags = excludeFlags | TransformFlags.NodeExcludes); + return updateTransformFlags(node, subtreeFlags, transformFlags, excludeFlags); + } + + function computeCallExpression(node: CallExpression, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression + || isSuperCall(node) + || isSuperPropertyCall(node)) { + // If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6 + // node. + transformFlags = TransformFlags.AssertES6; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ArrayLiteralOrCallOrNewExcludes); + } + + function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + if (isDestructuringAssignment(node)) { + // Destructuring assignments are ES6 syntax. + transformFlags = TransformFlags.AssertES6 | TransformFlags.DestructuringAssignment; + } + else if (isExponentiation(node)) { + // Exponentiation is ES7 syntax. + transformFlags = TransformFlags.AssertES7; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function isDestructuringAssignment(node: BinaryExpression) { + return node.operatorToken.kind === SyntaxKind.EqualsToken + && isObjectOrArrayLiteral(node.left); + } + + function isObjectOrArrayLiteral(node: Node) { + switch (node.kind) { + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ArrayLiteralExpression: + return true; + } + + return false; + } + + function isExponentiation(operatorToken: Node) { + switch (operatorToken.kind) { + case SyntaxKind.AsteriskAsteriskToken: + case SyntaxKind.AsteriskAsteriskEqualsToken: + return true; + } + + return false; + } + + function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + // If the parameter has a question token, then it is TypeScript syntax. + if (isDefined(node.questionToken)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + // If a parameter has an accessibility modifier, then it is TypeScript syntax. + if (hasModifier(node, ModifierFlags.AccessibilityModifier)) { + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; + } + + // If a parameter has an initializer, a binding pattern or a dotDotDot token, then + // it is ES6 syntax and its container must emit default value assignments or parameter destructuring downlevel. + if (isDefined(node.initializer) || isDefined(node.dotDotDotToken) || isBindingPattern(node.name)) { + transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsDefaultValueAssignments; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function computeParenthesizedExpression(node: ParenthesizedExpression, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + // If the node is synthesized, it means the emitter put the parentheses there, + // not the user. If we didn't want them, the emitter would not have put them + // there. + if (node.expression.kind === SyntaxKind.AsExpression + || node.expression.kind === SyntaxKind.TypeAssertionExpression) { + transformFlags = TransformFlags.AssertTypeScript; + } + + // If the expression of a ParenthesizedExpression is a destructuring assignment, + // then the ParenthesizedExpression is a destructuring assignment. + if (node.expression.transformFlags & TransformFlags.DestructuringAssignment) { + transformFlags |= TransformFlags.DestructuringAssignment; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) { + // An ambient declaration is TypeScript syntax. + if (hasModifier(node, ModifierFlags.Ambient)) { + return updateTransformFlags(node, TransformFlags.None, TransformFlags.TypeScript, TransformFlags.ClassExcludes); + } + + // A ClassDeclaration is ES6 syntax. + let transformFlags = TransformFlags.AssertES6; + + // A class with a parameter property assignment, property initializer, or decorator is + // TypeScript syntax. + // An exported declaration may be TypeScript syntax. + if (subtreeFlags + & (TransformFlags.ContainsParameterPropertyAssignments + | TransformFlags.ContainsPropertyInitializer + | TransformFlags.ContainsDecorators) + || hasModifier(node, ModifierFlags.Export)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ClassExcludes); + } + + function computeClassExpression(node: ClassExpression, subtreeFlags: TransformFlags) { + // A ClassExpression is ES6 syntax. + let transformFlags = TransformFlags.AssertES6; + + // A class with a parameter property assignment, property initializer, or decorator is + // TypeScript syntax. + if (subtreeFlags + & (TransformFlags.ContainsParameterPropertyAssignments + | TransformFlags.ContainsPropertyInitializer + | TransformFlags.ContainsDecorators)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ClassExcludes); + } + + function computeFunctionDeclaration(node: FunctionDeclaration, subtreeFlags: TransformFlags) { + const modifiers = getModifierFlags(node); + + // An ambient declaration is TypeScript syntax. + // A FunctionDeclaration without a body is an overload and is TypeScript syntax. + if (!node.body || modifiers & ModifierFlags.Ambient) { + return updateTransformFlags(node, TransformFlags.None, TransformFlags.AssertTypeScript, TransformFlags.FunctionExcludes); + } + + let transformFlags = TransformFlags.None; + + // If a FunctionDeclaration is exported, then it is either ES6 or TypeScript syntax. + if (modifiers & ModifierFlags.Export) { + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.AssertES6; + } + + // 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 (subtreeFlags & (TransformFlags.ContainsCapturedLexicalThis | TransformFlags.ContainsDefaultValueAssignments) + || node.asteriskToken) { + transformFlags |= TransformFlags.AssertES6; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.FunctionExcludes); + } + + function computeFunctionExpression(node: FunctionExpression, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + + // An async function expression is TypeScript syntax. + if (hasModifier(node, ModifierFlags.Async)) { + 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 (subtreeFlags & (TransformFlags.ContainsCapturedLexicalThis | TransformFlags.ContainsDefaultValueAssignments) + || node.asteriskToken) { + transformFlags |= TransformFlags.AssertES6; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.FunctionExcludes); + } + + function computeArrowFunction(node: ArrowFunction, subtreeFlags: TransformFlags) { + // An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction. + let transformFlags = TransformFlags.AssertES6; + + // An async arrow function is TypeScript syntax. + if (hasModifier(node, ModifierFlags.Async)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + // If an ArrowFunction contains a lexical this, its container must capture the lexical this. + if (subtreeFlags & TransformFlags.ContainsLexicalThis) { + transformFlags |= TransformFlags.ContainsCapturedLexicalThis; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ArrowFunctionExcludes); + } + + function computeVariableDeclaration(node: VariableDeclaration, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + + // A VariableDeclaration with a binding pattern is ES6 syntax. + if (isBindingPattern((node).name)) { + transformFlags = TransformFlags.AssertES6; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) { + const modifiers = getModifierFlags(node); + // An ambient declaration is TypeScript syntax. + if (modifiers & ModifierFlags.Ambient) { + return updateTransformFlags(node, TransformFlags.None, TransformFlags.AssertTypeScript, TransformFlags.None); + } + + let transformFlags = TransformFlags.None; + + // If a VariableStatement is exported, then it is either ES6 or TypeScript syntax. + if (modifiers & ModifierFlags.Export) { + transformFlags = TransformFlags.AssertES6 | TransformFlags.AssertTypeScript; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function computeLabeledStatement(node: LabeledStatement, subtreeFlags: TransformFlags) { + let transformFlags = TransformFlags.None; + + // A labeled statement containing a block scoped binding *may* need to be transformed from ES6. + if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding + && isIterationStatement(this, /*lookInLabeledStatements*/ true)) { + transformFlags = TransformFlags.AssertES6; + } + + return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.None); + } + + function updateTransformFlags(node: Node, subtreeFlags: TransformFlags, transformFlags: TransformFlags, excludeFlags: TransformFlags) { + node.transformFlags = transformFlags | subtreeFlags | TransformFlags.HasComputedFlags; + node.excludeTransformFlags = excludeFlags | TransformFlags.NodeExcludes; + return node.transformFlags & ~node.excludeTransformFlags; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 278159469e192..9b48b60b477a6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -535,7 +535,7 @@ namespace ts { const initializerOfNonStaticProperty = current.parent && current.parent.kind === SyntaxKind.PropertyDeclaration && - (current.parent.flags & NodeFlags.Static) === 0 && + (getModifierFlags(current.parent) & ModifierFlags.Static) === 0 && (current.parent).initializer === current; if (initializerOfNonStaticProperty) { @@ -653,7 +653,7 @@ namespace ts { // local variables of the constructor. This effectively means that entities from outer scopes // by the same name as a constructor parameter or local variable are inaccessible // in initializer expressions for instance member variables. - if (isClassLike(location.parent) && !(location.flags & NodeFlags.Static)) { + if (isClassLike(location.parent) && !(getModifierFlags(location) & ModifierFlags.Static)) { const ctor = findConstructorDeclaration(location.parent); if (ctor && ctor.locals) { if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) { @@ -667,7 +667,7 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { - if (lastLocation && lastLocation.flags & NodeFlags.Static) { + if (lastLocation && getModifierFlags(lastLocation) & ModifierFlags.Static) { // TypeScript 1.0 spec (April 2014): 3.4.1 // The scope of a type parameter extends over the entire declaration with which the type // parameter list is associated, with the exception of static member declarations in classes. @@ -824,7 +824,7 @@ namespace ts { // No static member is present. // Check if we're in an instance method and look for a relevant instance member. - if (location === container && !(location.flags & NodeFlags.Static)) { + if (location === container && !(getModifierFlags(location) & ModifierFlags.Static)) { const instanceType = (getDeclaredTypeOfSymbol(classSymbol)).thisType; if (getPropertyOfType(instanceType, name)) { error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg)); @@ -1617,7 +1617,7 @@ namespace ts { const anyImportSyntax = getAnyImportSyntax(declaration); if (anyImportSyntax && - !(anyImportSyntax.flags & NodeFlags.Export) && // import clause without export + !(getModifierFlags(anyImportSyntax) & ModifierFlags.Export) && // import clause without export isDeclarationVisible(anyImportSyntax.parent)) { getNodeLinks(declaration).isVisible = true; if (aliasesToMakeVisible) { @@ -2014,7 +2014,7 @@ namespace ts { function shouldWriteTypeOfFunctionSymbol() { const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method - forEach(symbol.declarations, declaration => declaration.flags & NodeFlags.Static)); + forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || // is exported function symbol forEach(symbol.declarations, declaration => @@ -2306,7 +2306,7 @@ namespace ts { } const parent = getDeclarationContainer(node); // If the node is not exported or it is not ambient module element (except import declaration) - if (!(getCombinedNodeFlags(node) & NodeFlags.Export) && + if (!(getCombinedModifierFlags(node) & ModifierFlags.Export) && !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) { return isGlobalSourceFile(parent); } @@ -2319,7 +2319,7 @@ namespace ts { case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - if (node.flags & (NodeFlags.Private | NodeFlags.Protected)) { + if (getModifierFlags(node) & (ModifierFlags.Private | ModifierFlags.Protected)) { // Private/protected properties/methods are not visible return false; } @@ -3867,7 +3867,7 @@ namespace ts { const type = getApparentType(current); if (type !== unknownType) { const prop = getPropertyOfType(type, name); - if (prop && !(getDeclarationFlagsFromSymbol(prop) & (NodeFlags.Private | NodeFlags.Protected))) { + if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))) { commonFlags &= prop.flags; if (!props) { props = [prop]; @@ -4309,7 +4309,7 @@ namespace ts { const declaration = getIndexDeclarationOfSymbol(symbol, kind); if (declaration) { return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, - (declaration.flags & NodeFlags.Readonly) !== 0, declaration); + (getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration); } return undefined; } @@ -4845,7 +4845,7 @@ namespace ts { const container = getThisContainer(node, /*includeArrowFunctions*/ false); const parent = container && container.parent; if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { - if (!(container.flags & NodeFlags.Static) && + if (!(getModifierFlags(container) & ModifierFlags.Static) && (container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (container).body))) { return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType; } @@ -5791,24 +5791,24 @@ namespace ts { } } else if (!(targetProp.flags & SymbolFlags.Prototype)) { - const sourcePropFlags = getDeclarationFlagsFromSymbol(sourceProp); - const targetPropFlags = getDeclarationFlagsFromSymbol(targetProp); - if (sourcePropFlags & NodeFlags.Private || targetPropFlags & NodeFlags.Private) { + const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); + const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); + if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) { if (reportErrors) { - if (sourcePropFlags & NodeFlags.Private && targetPropFlags & NodeFlags.Private) { + if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) { reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp)); } else { reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp), - typeToString(sourcePropFlags & NodeFlags.Private ? source : target), - typeToString(sourcePropFlags & NodeFlags.Private ? target : source)); + typeToString(sourcePropFlags & ModifierFlags.Private ? source : target), + typeToString(sourcePropFlags & ModifierFlags.Private ? target : source)); } } return Ternary.False; } } - else if (targetPropFlags & NodeFlags.Protected) { + else if (targetPropFlags & ModifierFlags.Protected) { const sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & SymbolFlags.Class; const sourceClass = sourceDeclaredInClass ? getDeclaredTypeOfSymbol(getParentOfSymbol(sourceProp)) : undefined; const targetClass = getDeclaredTypeOfSymbol(getParentOfSymbol(targetProp)); @@ -5820,7 +5820,7 @@ namespace ts { return Ternary.False; } } - else if (sourcePropFlags & NodeFlags.Protected) { + else if (sourcePropFlags & ModifierFlags.Protected) { if (reportErrors) { reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, symbolToString(targetProp), typeToString(source), typeToString(target)); @@ -6060,7 +6060,7 @@ namespace ts { const symbol = type.symbol; if (symbol && symbol.flags & SymbolFlags.Class) { const declaration = getClassLikeDeclarationOfSymbol(symbol); - if (declaration && declaration.flags & NodeFlags.Abstract) { + if (declaration && getModifierFlags(declaration) & ModifierFlags.Abstract) { return true; } } @@ -6100,8 +6100,8 @@ namespace ts { if (sourceProp === targetProp) { return Ternary.True; } - const sourcePropAccessibility = getDeclarationFlagsFromSymbol(sourceProp) & (NodeFlags.Private | NodeFlags.Protected); - const targetPropAccessibility = getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected); + const sourcePropAccessibility = getDeclarationModifierFlagsFromSymbol(sourceProp) & ModifierFlags.NonPublicAccessibilityModifier; + const targetPropAccessibility = getDeclarationModifierFlagsFromSymbol(targetProp) & ModifierFlags.NonPublicAccessibilityModifier; if (sourcePropAccessibility !== targetPropAccessibility) { return Ternary.False; } @@ -7323,7 +7323,7 @@ namespace ts { break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - if (container.flags & NodeFlags.Static) { + if (getModifierFlags(container) & ModifierFlags.Static) { error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks } @@ -7339,7 +7339,7 @@ namespace ts { if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); - return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; + return getModifierFlags(container) & ModifierFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; } if (isInJavaScriptFile(node)) { @@ -7429,7 +7429,7 @@ namespace ts { return unknownType; } - if ((container.flags & NodeFlags.Static) || isCallExpression) { + if ((getModifierFlags(container) & ModifierFlags.Static) || isCallExpression) { nodeCheckFlag = NodeCheckFlags.SuperStatic; } else { @@ -7494,8 +7494,8 @@ namespace ts { // 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 (isSuperPropertyOrElementAccess(node.parent) && isAssignmentTarget(node.parent)) { + if (container.kind === SyntaxKind.MethodDeclaration && getModifierFlags(container) & ModifierFlags.Async) { + if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } else { @@ -7560,7 +7560,7 @@ namespace ts { // topmost container must be something that is directly nested in the class declaration\object literal expression if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (container.flags & NodeFlags.Static) { + if (getModifierFlags(container) & ModifierFlags.Static) { return container.kind === SyntaxKind.MethodDeclaration || container.kind === SyntaxKind.MethodSignature || container.kind === SyntaxKind.GetAccessor || @@ -8785,8 +8785,12 @@ namespace ts { return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration; } - function getDeclarationFlagsFromSymbol(s: Symbol): NodeFlags { - return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : 0; + function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags { + return s.valueDeclaration ? getCombinedModifierFlags(s.valueDeclaration) : s.flags & SymbolFlags.Prototype ? ModifierFlags.Public | ModifierFlags.Static : 0; + } + + function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags { + return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0; } /** @@ -8798,7 +8802,7 @@ namespace ts { * @param prop The symbol for the right hand side of the property access. */ function checkClassPropertyAccess(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean { - const flags = getDeclarationFlagsFromSymbol(prop); + const flags = getDeclarationModifierFlagsFromSymbol(prop); const declaringClass = getDeclaredTypeOfSymbol(getParentOfSymbol(prop)); if (left.kind === SyntaxKind.SuperKeyword) { @@ -8821,7 +8825,7 @@ namespace ts { return false; } - if (flags & NodeFlags.Abstract) { + if (flags & ModifierFlags.Abstract) { // A method cannot be accessed in a super property access if the method is abstract. // This error could mask a private property access error. But, a member // cannot simultaneously be private and abstract, so this will trigger an @@ -8833,7 +8837,7 @@ namespace ts { } // Public properties are otherwise accessible. - if (!(flags & (NodeFlags.Private | NodeFlags.Protected))) { + if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) { return true; } @@ -8844,7 +8848,7 @@ namespace ts { const enclosingClass = enclosingClassDeclaration ? getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)) : undefined; // Private property is accessible if declaring and enclosing class are the same - if (flags & NodeFlags.Private) { + if (flags & ModifierFlags.Private) { if (declaringClass !== enclosingClass) { error(node, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(declaringClass)); return false; @@ -8864,7 +8868,7 @@ namespace ts { return false; } // No further restrictions for static properties - if (flags & NodeFlags.Static) { + if (flags & ModifierFlags.Static) { return true; } // An instance property must be accessed through an instance of the enclosing class @@ -10082,7 +10086,7 @@ namespace ts { // In the case of a merged class-module or class-interface declaration, // only the class declaration node will have the Abstract flag set. const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol); - if (valueDecl && valueDecl.flags & NodeFlags.Abstract) { + if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) { error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name)); return resolveErrorCall(node); } @@ -10700,8 +10704,8 @@ namespace ts { // Variables declared with 'const' // Get accessors without matching set accessors // Enum members - return symbol.flags & SymbolFlags.Property && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Readonly) !== 0 || - symbol.flags & SymbolFlags.Variable && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 || + return symbol.flags & SymbolFlags.Property && (getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly) !== 0 || + symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 || symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) || (symbol.flags & SymbolFlags.EnumMember) !== 0; } @@ -11516,7 +11520,7 @@ namespace ts { checkVariableLikeDeclaration(node); let func = getContainingFunction(node); - if (node.flags & NodeFlags.AccessibilityModifier) { + if (getModifierFlags(node) & ModifierFlags.AccessibilityModifier) { func = getContainingFunction(node); if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) { error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); @@ -11760,7 +11764,7 @@ namespace ts { // Abstract methods cannot have an implementation. // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. - if (node.flags & NodeFlags.Abstract && node.body) { + if (getModifierFlags(node) & ModifierFlags.Abstract && node.body) { error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name)); } } @@ -11822,7 +11826,7 @@ namespace ts { function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && - !(n.flags & NodeFlags.Static) && + !(getModifierFlags(n) & ModifierFlags.Static) && !!(n).initializer; } @@ -11847,7 +11851,7 @@ namespace ts { // or the containing class declares instance member variables with initializers. const superCallShouldBeFirst = forEach((node.parent).members, isInstancePropertyWithInitializer) || - forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected)); + forEach(node.parameters, p => getModifierFlags(p) & ModifierFlags.AccessibilityModifier); // Skip past any prologue directives to find the first statement // to ensure that it was a super call. @@ -11905,7 +11909,7 @@ namespace ts { const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const otherAccessor = getDeclarationOfKind(node.symbol, otherKind); if (otherAccessor) { - if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) { + if ((getModifierFlags(node) & ModifierFlags.AccessibilityModifier) !== (getModifierFlags(otherAccessor) & ModifierFlags.AccessibilityModifier)) { error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); } @@ -12006,11 +12010,11 @@ namespace ts { } function isPrivateWithinAmbient(node: Node): boolean { - return (node.flags & NodeFlags.Private) && isInAmbientContext(node); + return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node); } - function getEffectiveDeclarationFlags(n: Node, flagsToCheck: NodeFlags): NodeFlags { - let flags = getCombinedNodeFlags(n); + function getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags { + let flags = getCombinedModifierFlags(n); // children of classes (even ambient classes) should not be marked as ambient or export // because those flags have no useful semantics there. @@ -12018,11 +12022,11 @@ namespace ts { n.parent.kind !== SyntaxKind.ClassDeclaration && n.parent.kind !== SyntaxKind.ClassExpression && isInAmbientContext(n)) { - if (!(flags & NodeFlags.Ambient)) { + if (!(flags & ModifierFlags.Ambient)) { // It is nested in an ambient context, which means it is automatically exported - flags |= NodeFlags.Export; + flags |= ModifierFlags.Export; } - flags |= NodeFlags.Ambient; + flags |= ModifierFlags.Ambient; } return flags & flagsToCheck; @@ -12043,7 +12047,7 @@ namespace ts { return implementationSharesContainerWithFirstOverload ? implementation : overloads[0]; } - function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, flagsToCheck: NodeFlags, someOverloadFlags: NodeFlags, allOverloadFlags: NodeFlags): void { + function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void { // Error if some overloads have a flag that is not shared by all overloads. To find the // deviations, we XOR someOverloadFlags with allOverloadFlags const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags; @@ -12052,16 +12056,16 @@ namespace ts { forEach(overloads, o => { const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags; - if (deviation & NodeFlags.Export) { + if (deviation & ModifierFlags.Export) { error(o.name, Diagnostics.Overload_signatures_must_all_be_exported_or_not_exported); } - else if (deviation & NodeFlags.Ambient) { + else if (deviation & ModifierFlags.Ambient) { error(o.name, Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient); } - else if (deviation & (NodeFlags.Private | NodeFlags.Protected)) { + else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) { error(o.name, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected); } - else if (deviation & NodeFlags.Abstract) { + else if (deviation & ModifierFlags.Abstract) { error(o.name, Diagnostics.Overload_signatures_must_all_be_abstract_or_not_abstract); } }); @@ -12080,8 +12084,8 @@ namespace ts { } } - const flagsToCheck: NodeFlags = NodeFlags.Export | NodeFlags.Ambient | NodeFlags.Private | NodeFlags.Protected | NodeFlags.Abstract; - let someNodeFlags: NodeFlags = 0; + const flagsToCheck: ModifierFlags = ModifierFlags.Export | ModifierFlags.Ambient | ModifierFlags.Private | ModifierFlags.Protected | ModifierFlags.Abstract; + let someNodeFlags: ModifierFlags = ModifierFlags.None; let allNodeFlags = flagsToCheck; let someHaveQuestionToken = false; let allHaveQuestionToken = true; @@ -12116,13 +12120,13 @@ namespace ts { if (node.name && (subsequentNode).name && (node.name).text === ((subsequentNode).name).text) { const reportError = (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && - (node.flags & NodeFlags.Static) !== (subsequentNode.flags & NodeFlags.Static); + (getModifierFlags(node) & ModifierFlags.Static) !== (getModifierFlags(subsequentNode) & ModifierFlags.Static); // we can get here in two cases // 1. mixed static and instance class members // 2. something with the same name was defined before the set of overloads that prevents them from merging // here we'll report error only for the first case since for second we should already report error in binder if (reportError) { - const diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; + const diagnostic = getModifierFlags(node) & ModifierFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; error(errorNode, diagnostic); } return; @@ -12140,7 +12144,7 @@ namespace ts { else { // Report different errors regarding non-consecutive blocks of declarations depending on whether // the node in question is abstract. - if (node.flags & NodeFlags.Abstract) { + if (getModifierFlags(node) & ModifierFlags.Abstract) { error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive); } else { @@ -12219,7 +12223,7 @@ namespace ts { // Abstract methods can't have an implementation -- in particular, they don't need one. if (!isExportSymbolInsideModule && lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && - !(lastSeenNonAmbientDeclaration.flags & NodeFlags.Abstract)) { + !(getModifierFlags(lastSeenNonAmbientDeclaration) & ModifierFlags.Abstract)) { reportImplementationExpectedError(lastSeenNonAmbientDeclaration); } @@ -12269,10 +12273,10 @@ namespace ts { let defaultExportedDeclarationSpaces = SymbolFlags.None; for (const d of symbol.declarations) { const declarationSpaces = getDeclarationSpaces(d); - const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, NodeFlags.Export | NodeFlags.Default); + const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default); - if (effectiveDeclarationFlags & NodeFlags.Export) { - if (effectiveDeclarationFlags & NodeFlags.Default) { + if (effectiveDeclarationFlags & ModifierFlags.Export) { + if (effectiveDeclarationFlags & ModifierFlags.Default) { defaultExportedDeclarationSpaces |= declarationSpaces; } else { @@ -13012,7 +13016,7 @@ namespace ts { if (localDeclarationSymbol && localDeclarationSymbol !== symbol && localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) { - if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) { + if (getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) { const varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList); const container = varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent @@ -13788,7 +13792,7 @@ namespace ts { // Only process instance properties with computed names here. // Static properties cannot be in conflict with indexers, // and properties with literal names were already checked. - if (!(member.flags & NodeFlags.Static) && hasDynamicName(member)) { + if (!(getModifierFlags(member) & ModifierFlags.Static) && hasDynamicName(member)) { const propType = getTypeOfSymbol(member.symbol); checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); @@ -13899,7 +13903,7 @@ namespace ts { } function checkClassDeclaration(node: ClassDeclaration) { - if (!node.name && !(node.flags & NodeFlags.Default)) { + if (!node.name && !(getModifierFlags(node) & ModifierFlags.Default)) { grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name); } checkClassLikeDeclaration(node); @@ -14019,7 +14023,7 @@ namespace ts { } const derived = getTargetSymbol(getPropertyOfObjectType(type, base.name)); - const baseDeclarationFlags = getDeclarationFlagsFromSymbol(base); + const baseDeclarationFlags = getDeclarationModifierFlagsFromSymbol(base); Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration."); @@ -14035,7 +14039,7 @@ namespace ts { // It is an error to inherit an abstract member without implementing it or being declared abstract. // If there is no declaration for the derived class (as in the case of class expressions), // then the class cannot be declared abstract. - if (baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) { + if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !(getModifierFlags(derivedClassDecl) & ModifierFlags.Abstract))) { if (derivedClassDecl.kind === SyntaxKind.ClassExpression) { error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, symbolToString(baseProperty), typeToString(baseType)); @@ -14048,13 +14052,13 @@ namespace ts { } else { // derived overrides base. - const derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived); - if ((baseDeclarationFlags & NodeFlags.Private) || (derivedDeclarationFlags & NodeFlags.Private)) { + const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived); + if ((baseDeclarationFlags & ModifierFlags.Private) || (derivedDeclarationFlags & ModifierFlags.Private)) { // either base or derived property is private - not override, skip it continue; } - if ((baseDeclarationFlags & NodeFlags.Static) !== (derivedDeclarationFlags & NodeFlags.Static)) { + if ((baseDeclarationFlags & ModifierFlags.Static) !== (derivedDeclarationFlags & ModifierFlags.Static)) { // value of 'static' is not the same for properties - not override, skip it continue; } @@ -14727,7 +14731,7 @@ namespace ts { // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. return; } - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers); } if (checkExternalImportOrExportDeclaration(node)) { @@ -14757,7 +14761,7 @@ namespace ts { checkGrammarDecorators(node) || checkGrammarModifiers(node); if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) { checkImportBinding(node); - if (node.flags & NodeFlags.Export) { + if (getModifierFlags(node) & ModifierFlags.Export) { markExportAsReferenced(node); } if (isInternalModuleImportEqualsDeclaration(node)) { @@ -14790,7 +14794,7 @@ namespace ts { return; } - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers); } @@ -14849,7 +14853,7 @@ namespace ts { return; } // Grammar checking - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); } if (node.expression.kind === SyntaxKind.Identifier) { @@ -15177,7 +15181,7 @@ namespace ts { function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { const symbols: SymbolTable = {}; - let memberFlags: NodeFlags = 0; + let memberFlags: ModifierFlags = ModifierFlags.None; if (isInsideWithStatementBody(location)) { // We cannot answer semantic questions within a with block, do not proceed any further @@ -15218,7 +15222,7 @@ namespace ts { // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. - if (!(memberFlags & NodeFlags.Static)) { + if (!(memberFlags & ModifierFlags.Static)) { copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type); } break; @@ -15234,7 +15238,7 @@ namespace ts { copySymbol(argumentsSymbol, meaning); } - memberFlags = location.flags; + memberFlags = getModifierFlags(location); location = location.parent; } @@ -15592,7 +15596,7 @@ namespace ts { */ function getParentTypeOfClassElement(node: ClassElement) { const classSymbol = getSymbolOfNode(node.parent); - return node.flags & NodeFlags.Static + return getModifierFlags(node) & ModifierFlags.Static ? getTypeOfSymbol(classSymbol) : getDeclaredTypeOfSymbol(classSymbol); } @@ -16169,7 +16173,7 @@ namespace ts { } let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node; - let flags = 0; + let flags = ModifierFlags.None; for (const modifier of node.modifiers) { if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) { @@ -16201,22 +16205,22 @@ namespace ts { lastPrivate = modifier; } - if (flags & NodeFlags.AccessibilityModifier) { + if (flags & ModifierFlags.AccessibilityModifier) { return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen); } - else if (flags & NodeFlags.Static) { + else if (flags & ModifierFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static"); } - else if (flags & NodeFlags.Readonly) { + else if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly"); } - else if (flags & NodeFlags.Async) { + else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, text); } - else if (flags & NodeFlags.Abstract) { + else if (flags & ModifierFlags.Abstract) { if (modifier.kind === SyntaxKind.PrivateKeyword) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, text, "abstract"); } @@ -16228,13 +16232,13 @@ namespace ts { break; case SyntaxKind.StaticKeyword: - if (flags & NodeFlags.Static) { + if (flags & ModifierFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static"); } - else if (flags & NodeFlags.Readonly) { + else if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly"); } - else if (flags & NodeFlags.Async) { + else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { @@ -16243,35 +16247,35 @@ namespace ts { else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static"); } - else if (flags & NodeFlags.Abstract) { + else if (flags & ModifierFlags.Abstract) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); } - flags |= NodeFlags.Static; + flags |= ModifierFlags.Static; lastStatic = modifier; break; case SyntaxKind.ReadonlyKeyword: - if (flags & NodeFlags.Readonly) { + if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly"); } else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature) { return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature); } - flags |= NodeFlags.Readonly; + flags |= ModifierFlags.Readonly; lastReadonly = modifier; break; case SyntaxKind.ExportKeyword: - if (flags & NodeFlags.Export) { + if (flags & ModifierFlags.Export) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export"); } - else if (flags & NodeFlags.Ambient) { + else if (flags & ModifierFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare"); } - else if (flags & NodeFlags.Abstract) { + else if (flags & ModifierFlags.Abstract) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "abstract"); } - else if (flags & NodeFlags.Async) { + else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { @@ -16280,14 +16284,14 @@ namespace ts { else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export"); } - flags |= NodeFlags.Export; + flags |= ModifierFlags.Export; break; case SyntaxKind.DeclareKeyword: - if (flags & NodeFlags.Ambient) { + if (flags & ModifierFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare"); } - else if (flags & NodeFlags.Async) { + else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { @@ -16299,76 +16303,76 @@ namespace ts { else if (isInAmbientContext(node.parent) && node.parent.kind === SyntaxKind.ModuleBlock) { return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context); } - flags |= NodeFlags.Ambient; + flags |= ModifierFlags.Ambient; lastDeclare = modifier; break; case SyntaxKind.AbstractKeyword: - if (flags & NodeFlags.Abstract) { + if (flags & ModifierFlags.Abstract) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract"); } if (node.kind !== SyntaxKind.ClassDeclaration) { if (node.kind !== SyntaxKind.MethodDeclaration) { return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_or_method_declaration); } - if (!(node.parent.kind === SyntaxKind.ClassDeclaration && node.parent.flags & NodeFlags.Abstract)) { + if (!(node.parent.kind === SyntaxKind.ClassDeclaration && getModifierFlags(node.parent) & ModifierFlags.Abstract)) { return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class); } - if (flags & NodeFlags.Static) { + if (flags & ModifierFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); } - if (flags & NodeFlags.Private) { + if (flags & ModifierFlags.Private) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "private", "abstract"); } } - flags |= NodeFlags.Abstract; + flags |= ModifierFlags.Abstract; break; case SyntaxKind.AsyncKeyword: - if (flags & NodeFlags.Async) { + if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async"); } - else if (flags & NodeFlags.Ambient || isInAmbientContext(node.parent)) { + else if (flags & ModifierFlags.Ambient || isInAmbientContext(node.parent)) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "async"); } - flags |= NodeFlags.Async; + flags |= ModifierFlags.Async; lastAsync = modifier; break; } } if (node.kind === SyntaxKind.Constructor) { - if (flags & NodeFlags.Static) { + if (flags & ModifierFlags.Static) { return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static"); } - if (flags & NodeFlags.Abstract) { + if (flags & ModifierFlags.Abstract) { return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract"); } - else if (flags & NodeFlags.Protected) { + else if (flags & ModifierFlags.Protected) { return grammarErrorOnNode(lastProtected, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "protected"); } - else if (flags & NodeFlags.Private) { + else if (flags & ModifierFlags.Private) { return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private"); } - else if (flags & NodeFlags.Async) { + else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async"); } - else if (flags & NodeFlags.Readonly) { + else if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(lastReadonly, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly"); } return; } - else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) { + else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & ModifierFlags.Ambient) { return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare"); } - else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.AccessibilityModifier) && isBindingPattern((node).name)) { + else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.AccessibilityModifier) && isBindingPattern((node).name)) { return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_a_binding_pattern); } - if (flags & NodeFlags.Async) { + if (flags & ModifierFlags.Async) { return checkGrammarAsyncModifier(node, lastAsync); } } @@ -16485,7 +16489,7 @@ namespace ts { if (parameter.dotDotDotToken) { return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); } - if (parameter.flags & NodeFlags.Modifier) { + if (getModifierFlags(parameter) !== 0) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); } if (parameter.questionToken) { @@ -16672,11 +16676,13 @@ namespace ts { } // Modifiers are never allowed on properties except for 'async' on a method declaration - forEach(prop.modifiers, mod => { - if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) { - grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod)); + if (prop.modifiers) { + for (const mod of prop.modifiers) { + if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) { + grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod)); + } } - }); + } // ECMA-262 11.1.5 Object Initialiser // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true @@ -16830,7 +16836,7 @@ namespace ts { if (parameter.dotDotDotToken) { return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter); } - else if (parameter.flags & NodeFlags.Modifier) { + else if (getModifierFlags(parameter) !== 0) { return grammarErrorOnNode(accessor.name, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); } else if (parameter.questionToken) { @@ -17136,8 +17142,7 @@ namespace ts { node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || - (node.flags & NodeFlags.Ambient) || - (node.flags & (NodeFlags.Export | NodeFlags.Default))) { + getModifierFlags(node) & (ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) { return false; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a464bd8668240..e4bc42ee45e26 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -171,23 +171,116 @@ namespace ts { return result; } + /** + * Flattens an array containing a mix of array or non-array elements. + * + * @param array The array to flatten. + */ + export function flatten(array: (T | T[])[]): T[] { + let result: T[]; + if (array) { + result = []; + for (const v of array) { + if (v) { + if (isArray(v)) { + addRange(result, v); + } + else { + result.push(v); + } + } + } + } + + return result; + } + /** * Maps an array. If the mapped value is an array, it is spread into the result. + * + * @param array The array to map. + * @param mapfn The callback used to map the result into one or more values. */ - export function flatMap(array: T[], f: (x: T, i: number) => U | U[]): U[] { + export function flatMap(array: T[], mapfn: (x: T, i: number) => U | U[]): U[] { let result: U[]; if (array) { result = []; for (let i = 0; i < array.length; i++) { - const v = array[i]; - const ar = f(v, i); - if (ar) { - // We cast to here to leverage the behavior of Array#concat - // which will append a single value here. - result = result.concat(ar); + const v = mapfn(array[i], i); + if (v) { + if (isArray(v)) { + addRange(result, v); + } + else { + result.push(v); + } + } + } + } + return result; + } + + /** + * Computes the first matching span of elements and returns a tuple of the first span + * and the remaining elements. + */ + export function span(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] { + if (array) { + for (let i = 0; i < array.length; i++) { + if (!f(array[i], i)) { + return [array.slice(0, i), array.slice(i)]; + } + } + return [array.slice(0), []]; + } + + return undefined; + } + + /** + * Maps contiguous spans of values with the same key. + * + * @param array The array to map. + * @param keyfn A callback used to select the key for an element. + * @param mapfn A callback used to map a contiguous chunk of values to a single value. + */ + export function spanMap(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K) => U): U[] { + let result: U[]; + if (array) { + result = []; + const len = array.length; + let previousKey: K; + let key: K; + let start = 0; + let pos = 0; + while (start < len) { + while (pos < len) { + const value = array[pos]; + key = keyfn(value, pos); + if (pos === 0) { + previousKey = key; + } + else if (key !== previousKey) { + break; + } + + pos++; } + + if (start < pos) { + const v = mapfn(array.slice(start, pos), previousKey); + if (v) { + result.push(v); + } + + start = pos; + } + + previousKey = key; + pos++; } } + return result; } @@ -920,8 +1013,9 @@ namespace ts { this.pos = pos; this.end = end; this.flags = NodeFlags.None; - this.transformFlags = undefined; - this.excludeTransformFlags = undefined; + this.modifierFlagsCache = ModifierFlags.None; + this.transformFlags = TransformFlags.None; + this.excludeTransformFlags = TransformFlags.None; this.parent = undefined; this.original = undefined; } @@ -942,7 +1036,12 @@ namespace ts { } export namespace Debug { - const currentAssertionLevel = AssertionLevel.None; + declare var process: any; + declare var require: any; + + const currentAssertionLevel = getDevelopmentMode() === "development" + ? AssertionLevel.Normal + : AssertionLevel.None; export function shouldAssert(level: AssertionLevel): boolean { return currentAssertionLevel >= level; @@ -962,6 +1061,17 @@ namespace ts { export function fail(message?: string): void { Debug.assert(/*expression*/ false, message); } + + function getDevelopmentMode() { + return typeof require !== "undefined" + && typeof process !== "undefined" + && !process.browser + && process.nextTick + && process.env + && process.env.NODE_ENV + ? String(process.env.NODE_ENV).toLowerCase() + : undefined; + } } export function copyListRemovingItem(item: T, list: T[]) { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index c89b7579d5247..655169b88e919 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -623,12 +623,13 @@ namespace ts { function emitModuleElementDeclarationFlags(node: Node) { // If the node is parented in the current source file we need to emit export declare or just export if (node.parent.kind === SyntaxKind.SourceFile) { + const modifiers = getModifierFlags(node); // If the node is exported - if (node.flags & NodeFlags.Export) { + if (modifiers & ModifierFlags.Export) { write("export "); } - if (node.flags & NodeFlags.Default) { + if (modifiers & ModifierFlags.Default) { write("default "); } else if (node.kind !== SyntaxKind.InterfaceDeclaration && !noDeclare) { @@ -637,21 +638,21 @@ namespace ts { } } - function emitClassMemberDeclarationFlags(flags: NodeFlags) { - if (flags & NodeFlags.Private) { + function emitClassMemberDeclarationFlags(flags: ModifierFlags) { + if (flags & ModifierFlags.Private) { write("private "); } - else if (flags & NodeFlags.Protected) { + else if (flags & ModifierFlags.Protected) { write("protected "); } - if (flags & NodeFlags.Static) { + if (flags & ModifierFlags.Static) { write("static "); } - if (flags & NodeFlags.Readonly) { + if (flags & ModifierFlags.Readonly) { write("readonly "); } - if (flags & NodeFlags.Abstract) { + if (flags & ModifierFlags.Abstract) { write("abstract "); } } @@ -660,7 +661,7 @@ namespace ts { // note usage of writer. methods instead of aliases created, just to make sure we are using // correct writer especially to handle asynchronous alias writing emitJsDocComments(node); - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { write("export "); } write("import "); @@ -698,12 +699,12 @@ namespace ts { } function writeImportDeclaration(node: ImportDeclaration) { - if (!node.importClause && !(node.flags & NodeFlags.Export)) { + if (!node.importClause && !hasModifier(node, ModifierFlags.Export)) { // do not write non-exported import declarations that don't have import clauses return; } emitJsDocComments(node); - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { write("export "); } write("import "); @@ -893,7 +894,7 @@ namespace ts { } function isPrivateMethodTypeParameter(node: TypeParameterDeclaration) { - return node.parent.kind === SyntaxKind.MethodDeclaration && (node.parent.flags & NodeFlags.Private); + return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private); } function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) { @@ -943,7 +944,7 @@ namespace ts { case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - if (node.parent.flags & NodeFlags.Static) { + if (hasModifier(node.parent, ModifierFlags.Static)) { diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; } else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { @@ -1018,7 +1019,7 @@ namespace ts { function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) { if (constructorDeclaration) { forEach(constructorDeclaration.parameters, param => { - if (param.flags & NodeFlags.AccessibilityModifier) { + if (hasModifier(param, ModifierFlags.AccessibilityModifier)) { emitPropertyDeclaration(param); } }); @@ -1027,7 +1028,7 @@ namespace ts { emitJsDocComments(node); emitModuleElementDeclarationFlags(node); - if (node.flags & NodeFlags.Abstract) { + if (hasModifier(node, ModifierFlags.Abstract)) { write("abstract "); } @@ -1077,7 +1078,7 @@ namespace ts { } emitJsDocComments(node); - emitClassMemberDeclarationFlags(node.flags); + emitClassMemberDeclarationFlags(getModifierFlags(node)); emitVariableDeclaration(node); write(";"); writeLine(); @@ -1102,7 +1103,7 @@ namespace ts { if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { emitTypeOfVariableDeclarationFromTypeLiteral(node); } - else if (!(node.flags & NodeFlags.Private)) { + else if (!hasModifier(node, ModifierFlags.Private)) { writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError); } } @@ -1119,7 +1120,7 @@ namespace ts { // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) { // TODO(jfreeman): Deal with computed properties in error reporting. - if (node.flags & NodeFlags.Static) { + if (hasModifier(node, ModifierFlags.Static)) { return symbolAccesibilityResult.errorModuleName ? symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : @@ -1230,9 +1231,9 @@ namespace ts { if (node === accessors.firstAccessor) { emitJsDocComments(accessors.getAccessor); emitJsDocComments(accessors.setAccessor); - emitClassMemberDeclarationFlags(node.flags | (accessors.setAccessor ? 0 : NodeFlags.Readonly)); + emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly)); writeTextOfNode(currentText, node.name); - if (!(node.flags & NodeFlags.Private)) { + if (!hasModifier(node, ModifierFlags.Private)) { accessorWithTypeAnnotation = node; let type = getTypeAnnotationFromAccessor(node); if (!type) { @@ -1263,7 +1264,7 @@ namespace ts { let diagnosticMessage: DiagnosticMessage; if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) { // Setters have to have type named and cannot infer it so, the type should always be named - if (accessorWithTypeAnnotation.parent.flags & NodeFlags.Static) { + if (hasModifier(accessorWithTypeAnnotation.parent, ModifierFlags.Static)) { diagnosticMessage = symbolAccesibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_name_1; @@ -1281,7 +1282,7 @@ namespace ts { }; } else { - if (accessorWithTypeAnnotation.flags & NodeFlags.Static) { + if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) { diagnosticMessage = symbolAccesibilityResult.errorModuleName ? symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : @@ -1317,7 +1318,7 @@ namespace ts { emitModuleElementDeclarationFlags(node); } else if (node.kind === SyntaxKind.MethodDeclaration) { - emitClassMemberDeclarationFlags(node.flags); + emitClassMemberDeclarationFlags(getModifierFlags(node)); } if (node.kind === SyntaxKind.FunctionDeclaration) { write("function "); @@ -1347,7 +1348,7 @@ namespace ts { if (node.kind === SyntaxKind.IndexSignature) { // Index signature can have readonly modifier - emitClassMemberDeclarationFlags(node.flags); + emitClassMemberDeclarationFlags(getModifierFlags(node)); write("["); } else { @@ -1378,7 +1379,7 @@ namespace ts { emitType(node.type); } } - else if (node.kind !== SyntaxKind.Constructor && !(node.flags & NodeFlags.Private)) { + else if (node.kind !== SyntaxKind.Constructor && !hasModifier(node, ModifierFlags.Private)) { writeReturnTypeAtSignature(node, getReturnTypeVisibilityError); } @@ -1415,7 +1416,7 @@ namespace ts { case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - if (node.flags & NodeFlags.Static) { + if (hasModifier(node, ModifierFlags.Static)) { diagnosticMessage = symbolAccesibilityResult.errorModuleName ? symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : @@ -1481,7 +1482,7 @@ namespace ts { node.parent.parent.kind === SyntaxKind.TypeLiteral) { emitTypeOfVariableDeclarationFromTypeLiteral(node); } - else if (!(node.parent.flags & NodeFlags.Private)) { + else if (!hasModifier(node.parent, ModifierFlags.Private)) { writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError); } @@ -1517,7 +1518,7 @@ namespace ts { case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - if (node.parent.flags & NodeFlags.Static) { + if (hasModifier(node.parent, ModifierFlags.Static)) { return symbolAccesibilityResult.errorModuleName ? symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index bda2bd79506cc..ad942cdca5c10 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2339,7 +2339,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge superCall = true; } else { - superCall = isSuperPropertyOrElementAccess(expression); + superCall = isSuperProperty(expression); isAsyncMethodWithSuper = superCall && isInAsyncMethodWithSuperInES6(node); emit(expression); } @@ -2579,7 +2579,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge let current = getRootDeclaration(node).parent; while (current) { if (current.kind === SyntaxKind.SourceFile) { - return !isExported || ((getCombinedNodeFlags(node) & NodeFlags.Export) !== 0); + return !isExported || ((getCombinedModifierFlags(node) & ModifierFlags.Export) !== 0); } else if (isDeclaration(current)) { return false; @@ -3627,7 +3627,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitModuleMemberName(node: Declaration) { emitStart(node.name); - if (getCombinedNodeFlags(node) & NodeFlags.Export) { + if (getCombinedModifierFlags(node) & ModifierFlags.Export) { const container = getContainingModule(node); if (container) { write(getGeneratedNameForNode(container)); @@ -3651,7 +3651,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitEs6ExportDefaultCompat(node: Node) { if (node.parent.kind === SyntaxKind.SourceFile) { - Debug.assert(!!(node.flags & NodeFlags.Default) || node.kind === SyntaxKind.ExportAssignment); + Debug.assert(hasModifier(node, ModifierFlags.Default) || node.kind === SyntaxKind.ExportAssignment); // only allow export default at a source file level if (modulekind === ModuleKind.CommonJS || modulekind === ModuleKind.AMD || modulekind === ModuleKind.UMD) { if (!isEs6Module) { @@ -3670,7 +3670,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function emitExportMemberAssignment(node: FunctionLikeDeclaration | ClassDeclaration) { - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { writeLine(); emitStart(node); @@ -3679,7 +3679,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // emit export default as // export("default", ) write(`${exportFunctionForFile}("`); - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { write("default"); } else { @@ -3690,7 +3690,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(")"); } else { - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { emitEs6ExportDefaultCompat(node); if (languageVersion === ScriptTarget.ES3) { write("exports[\"default\"]"); @@ -3821,7 +3821,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // because actual variable declarations are hoisted let canDefineTempVariablesInPlace = false; if (root.kind === SyntaxKind.VariableDeclaration) { - const isExported = getCombinedNodeFlags(root) & NodeFlags.Export; + const isExported = getCombinedModifierFlags(root) & ModifierFlags.Export; const isSourceLevelForSystemModuleKind = shouldHoistDeclarationInSystemJsModule(root); canDefineTempVariablesInPlace = !isExported && !isSourceLevelForSystemModuleKind; } @@ -4163,7 +4163,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function isES6ExportedDeclaration(node: Node) { - return !!(node.flags & NodeFlags.Export) && + return hasModifier(node, ModifierFlags.Export) && modulekind === ModuleKind.ES6 && node.parent.kind === SyntaxKind.SourceFile; } @@ -4171,7 +4171,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitVariableStatement(node: VariableStatement) { let startIsEmitted = false; - if (node.flags & NodeFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { if (isES6ExportedDeclaration(node)) { // Exported ES6 module member write("export "); @@ -4200,7 +4200,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function shouldEmitLeadingAndTrailingCommentsForVariableStatement(node: VariableStatement) { // If we're not exporting the variables, there's nothing special here. // Always emit comments for these nodes. - if (!(node.flags & NodeFlags.Export)) { + if (!hasModifier(node, ModifierFlags.Export)) { return true; } @@ -4408,7 +4408,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (!shouldEmitAsArrowFunction(node)) { if (isES6ExportedDeclaration(node)) { write("export "); - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { write("default "); } } @@ -4661,7 +4661,7 @@ const _super = (function (geti, seti) { } function emitExpressionFunctionBody(node: FunctionLikeDeclaration, body: Expression) { - if (languageVersion < ScriptTarget.ES6 || node.flags & NodeFlags.Async) { + if (languageVersion < ScriptTarget.ES6 || hasModifier(node, ModifierFlags.Async)) { emitDownLevelExpressionFunctionBody(node, body); return; } @@ -4777,7 +4777,7 @@ const _super = (function (geti, seti) { function emitParameterPropertyAssignments(node: ConstructorDeclaration) { forEach(node.parameters, param => { - if (param.flags & NodeFlags.AccessibilityModifier) { + if (hasModifier(param, ModifierFlags.AccessibilityModifier)) { writeLine(); emitStart(param); emitStart(param.name); @@ -4813,7 +4813,7 @@ const _super = (function (geti, seti) { function getInitializedProperties(node: ClassLikeDeclaration, isStatic: boolean) { const properties: PropertyDeclaration[] = []; for (const member of node.members) { - if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === ((member.flags & NodeFlags.Static) !== 0) && (member).initializer) { + if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === hasModifier(member, ModifierFlags.Static) && (member).initializer) { properties.push(member); } } @@ -4836,7 +4836,7 @@ const _super = (function (geti, seti) { emit(receiver); } else { - if (property.flags & NodeFlags.Static) { + if (hasModifier(property, ModifierFlags.Static)) { emitDeclarationName(node); } else { @@ -4938,7 +4938,7 @@ const _super = (function (geti, seti) { writeLine(); emitLeadingComments(member); emitStart(member); - if (member.flags & NodeFlags.Static) { + if (hasModifier(member, ModifierFlags.Static)) { write("static "); } @@ -4996,7 +4996,7 @@ const _super = (function (geti, seti) { emitCommentsOnNotEmittedNode(member); } // Check if there is any non-static property assignment - if (member.kind === SyntaxKind.PropertyDeclaration && (member).initializer && (member.flags & NodeFlags.Static) === 0) { + if (member.kind === SyntaxKind.PropertyDeclaration && (member).initializer && !hasModifier(member, ModifierFlags.Static)) { hasInstancePropertyWithInitializer = true; } }); @@ -5210,7 +5210,7 @@ const _super = (function (geti, seti) { writeLine(); } - if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) { + if (isES6ExportedDeclaration(node) && !hasModifier(node, ModifierFlags.Default)) { write("export "); } @@ -5224,7 +5224,7 @@ const _super = (function (geti, seti) { } else if (isES6ExportedDeclaration(node)) { write("export "); - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { write("default "); } } @@ -5258,7 +5258,7 @@ const _super = (function (geti, seti) { // emit name if // - node has a name // - this is default export with static initializers - if (node.name || (node.flags & NodeFlags.Default && (staticProperties.length > 0 || modulekind !== ModuleKind.ES6) && !thisNodeIsDecorated)) { + if (node.name || (hasModifier(node, ModifierFlags.Default) && (staticProperties.length > 0 || modulekind !== ModuleKind.ES6) && !thisNodeIsDecorated)) { write(" "); emitDeclarationName(node); } @@ -5307,7 +5307,7 @@ const _super = (function (geti, seti) { emitDecoratorsOfClass(node, decoratedClassAlias); } - if (!(node.flags & NodeFlags.Export)) { + if (!hasModifier(node, ModifierFlags.Export)) { return; } if (modulekind !== ModuleKind.ES6) { @@ -5316,7 +5316,7 @@ const _super = (function (geti, seti) { else { // If this is an exported class, but not on the top level (i.e. on an internal // module), export it - if (node.flags & NodeFlags.Default) { + if (hasModifier(node, ModifierFlags.Default)) { // if this is a top level default export of decorated class, write the export after the declaration. if (thisNodeIsDecorated) { writeLine(); @@ -5439,14 +5439,14 @@ const _super = (function (geti, seti) { function emitClassMemberPrefix(node: ClassLikeDeclaration, member: Node) { emitDeclarationName(node); - if (!(member.flags & NodeFlags.Static)) { + if (!hasModifier(member, ModifierFlags.Static)) { write(".prototype"); } } function emitDecoratorsOfClass(node: ClassLikeDeclaration, decoratedClassAlias: string) { emitDecoratorsOfMembers(node, /*staticFlag*/ 0); - emitDecoratorsOfMembers(node, NodeFlags.Static); + emitDecoratorsOfMembers(node, ModifierFlags.Static); emitDecoratorsOfConstructor(node, decoratedClassAlias); } @@ -5500,10 +5500,10 @@ const _super = (function (geti, seti) { writeLine(); } - function emitDecoratorsOfMembers(node: ClassLikeDeclaration, staticFlag: NodeFlags) { + function emitDecoratorsOfMembers(node: ClassLikeDeclaration, staticFlag: ModifierFlags) { for (const member of node.members) { // only emit members in the correct group - if ((member.flags & NodeFlags.Static) !== staticFlag) { + if ((getModifierFlags(member) & ModifierFlags.Static) !== staticFlag) { continue; } @@ -5964,7 +5964,7 @@ const _super = (function (geti, seti) { // do not emit var if variable was already hoisted const isES6ExportedEnum = isES6ExportedDeclaration(node); - if (!(node.flags & NodeFlags.Export) || (isES6ExportedEnum && isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.EnumDeclaration))) { + if (!hasModifier(node, ModifierFlags.Export) || (isES6ExportedEnum && isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.EnumDeclaration))) { emitStart(node); if (isES6ExportedEnum) { write("export "); @@ -5993,7 +5993,7 @@ const _super = (function (geti, seti) { emitModuleMemberName(node); write(" = {}));"); emitEnd(node); - if (!isES6ExportedDeclaration(node) && node.flags & NodeFlags.Export && !shouldHoistDeclarationInSystemJsModule(node)) { + if (!isES6ExportedDeclaration(node) && hasModifier(node, ModifierFlags.Export) && !shouldHoistDeclarationInSystemJsModule(node)) { // do not emit var if variable was already hoisted writeLine(); emitStart(node); @@ -6005,7 +6005,7 @@ const _super = (function (geti, seti) { write(";"); } if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile) { - if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) { + if (modulekind === ModuleKind.System && hasModifier(node, ModifierFlags.Export)) { // write the call to exporter for enum writeLine(); write(`${exportFunctionForFile}("`); @@ -6127,7 +6127,7 @@ const _super = (function (geti, seti) { } write(")("); // write moduleDecl = containingModule.m only if it is not exported es6 module member - if ((node.flags & NodeFlags.Export) && !isES6ExportedDeclaration(node)) { + if (hasModifier(node, ModifierFlags.Export) && !isES6ExportedDeclaration(node)) { emit(node.name); write(" = "); } @@ -6137,7 +6137,7 @@ const _super = (function (geti, seti) { write(" = {}));"); emitEnd(node); if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && node.parent === currentSourceFile) { - if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) { + if (modulekind === ModuleKind.System && hasModifier(node, ModifierFlags.Export)) { writeLine(); write(`${exportFunctionForFile}("`); emitDeclarationName(node); @@ -6249,7 +6249,7 @@ const _super = (function (geti, seti) { function emitExternalImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration) { if (contains(externalImports, node)) { - const isExportedImport = node.kind === SyntaxKind.ImportEqualsDeclaration && (node.flags & NodeFlags.Export) !== 0; + const isExportedImport = node.kind === SyntaxKind.ImportEqualsDeclaration && hasModifier(node, ModifierFlags.Export); const namespaceDeclaration = getNamespaceDeclarationNode(node); const varOrConst = (languageVersion <= ScriptTarget.ES5) ? "var " : "const "; @@ -6339,7 +6339,7 @@ const _super = (function (geti, seti) { write("export "); write("var "); } - else if (!(node.flags & NodeFlags.Export)) { + else if (!hasModifier(node, ModifierFlags.Export)) { write("var "); } } @@ -6732,7 +6732,7 @@ const _super = (function (geti, seti) { function writeExportedName(node: Identifier | Declaration): void { // do not record default exports // they are local to module and never overwritten (explicitly skipped) by star export - if (node.kind !== SyntaxKind.Identifier && node.flags & NodeFlags.Default) { + if (node.kind !== SyntaxKind.Identifier && hasModifier(node, ModifierFlags.Default)) { return; } @@ -6804,8 +6804,8 @@ const _super = (function (geti, seti) { emit(local); } - const flags = getCombinedNodeFlags(local.kind === SyntaxKind.Identifier ? local.parent : local); - if (flags & NodeFlags.Export) { + const flags = getCombinedModifierFlags(local.kind === SyntaxKind.Identifier ? local.parent : local); + if (flags & ModifierFlags.Export) { if (!exportedDeclarations) { exportedDeclarations = []; } @@ -6820,7 +6820,7 @@ const _super = (function (geti, seti) { writeLine(); emit(f); - if (f.flags & NodeFlags.Export) { + if (hasModifier(f, ModifierFlags.Export)) { if (!exportedDeclarations) { exportedDeclarations = []; } @@ -6832,7 +6832,7 @@ const _super = (function (geti, seti) { return exportedDeclarations; function visit(node: Node): void { - if (node.flags & NodeFlags.Ambient) { + if (hasModifier(node, ModifierFlags.Ambient)) { return; } @@ -7596,7 +7596,7 @@ const _super = (function (geti, seti) { function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemap: (node: Node) => void): void { if (node) { - if (node.flags & NodeFlags.Ambient) { + if (hasModifier(node, ModifierFlags.Ambient)) { return emitCommentsOnNotEmittedNode(node); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0ad04c0a092f4..84eb1ac62ccd8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -46,55 +46,9 @@ namespace ts { array.hasTrailingComma = true; } - array.arrayKind = ArrayKind.NodeArray; return array; } - export function createModifiersArray(elements?: Modifier[], location?: TextRange): ModifiersArray { - let flags: NodeFlags; - if (elements) { - if (isModifiersArray(elements)) { - return elements; - } - - flags = 0; - for (const modifier of elements) { - flags |= modifierToFlag(modifier.kind); - } - } - else { - elements = []; - flags = 0; - } - - const array = elements; - if (location) { - array.pos = location.pos; - array.end = location.end; - } - else { - array.pos = -1; - array.end = -1; - } - - array.arrayKind = ArrayKind.ModifiersArray; - array.flags = flags; - return array; - } - - export function setModifiers(node: T, modifiers: Modifier[]) { - if (modifiers) { - const array = createModifiersArray(modifiers); - node.modifiers = array; - node.flags |= array.flags; - } - else { - node.modifiers = undefined; - } - - return node; - } - export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; @@ -105,26 +59,16 @@ namespace ts { return createNodeArray(elements, /*location*/ undefined); } - export function createSynthesizedModifiersArray(elements?: Modifier[]): ModifiersArray { - return createModifiersArray(elements, /*location*/ undefined); - } - /** - * Creates a shallow, memberwise clone of a node. The "kind", "pos", "end", "flags", and "parent" - * properties are excluded by default, and can be provided via the "location", "flags", and - * "parent" parameters. - * - * @param node The node to clone. - * @param location An optional TextRange to use to supply the new position. - * @param flags The NodeFlags to use for the cloned node. - * @param parent The parent for the new node. - * @param original An optional pointer to the original source tree node. + * Creates a shallow, memberwise clone of a node with no source map location. */ - export function cloneNode(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node, original?: Node): T { + export function getSynthesizedClone(node: T): T { // We don't use "clone" from core.ts here, as we need to preserve the prototype chain of // the original node. We also need to exclude specific properties and only include own- // properties (to skip members already defined on the shared prototype). - const clone = createNode(node.kind, location); + const clone = createSynthesizedNode(node.kind); + clone.flags = node.flags; + clone.original = node; for (const key in node) { if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) { @@ -134,46 +78,28 @@ namespace ts { (clone)[key] = (node)[key]; } - if (flags !== undefined) { - clone.flags = flags; - } - - if (parent !== undefined) { - clone.parent = parent; - } - - if (original !== undefined) { - clone.original = original; - } - return clone; } /** * Creates a shallow, memberwise clone of a node for mutation. */ - export function getMutableNode(node: T): T { - return cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); - } - - /** - * Creates a shallow, memberwise clone of a node with no source map location. - */ - export function getSynthesizedClone(node: T): T { - return nodeIsSynthesized(node) ? node : cloneNode(node, /*location*/ undefined, node.flags, /*parent*/ undefined, /*original*/ node); + export function getMutableClone(node: T): T { + const clone = getSynthesizedClone(node); + clone.pos = node.pos; + clone.end = node.end; + clone.parent = node.parent; + return clone; } /** * Creates a shallow, memberwise clone of a node at the specified source map location. */ export function getRelocatedClone(node: T, location: TextRange): T { - return cloneNode(node, location, node.flags, /*parent*/ undefined, /*original*/ node); - } - - export function createNodeArrayNode(elements: T[]): NodeArrayNode { - const node = >createSynthesizedNode(SyntaxKind.NodeArrayNode); - node.nodes = createNodeArray(elements); - return node; + const clone = getSynthesizedClone(node); + clone.pos = location.pos; + clone.end = location.end; + return clone; } // Literals @@ -265,7 +191,7 @@ namespace ts { export function createMethod(modifiers: Modifier[], name: string | PropertyName, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { const node = createNode(SyntaxKind.MethodDeclaration, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = typeof name === "string" ? createIdentifier(name) : name; node.typeParameters = undefined; node.parameters = createNodeArray(parameters); @@ -287,7 +213,7 @@ namespace ts { export function createGetAccessor(modifiers: Modifier[], name: string | PropertyName, body: Block, location?: TextRange) { const node = createNode(SyntaxKind.GetAccessor, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = typeof name === "string" ? createIdentifier(name) : name; node.typeParameters = undefined; node.parameters = createNodeArray(); @@ -298,7 +224,7 @@ namespace ts { export function createSetAccessor(modifiers: Modifier[], name: string | PropertyName, parameter: ParameterDeclaration, body: Block, location?: TextRange) { const node = createNode(SyntaxKind.SetAccessor, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = typeof name === "string" ? createIdentifier(name) : name; node.typeParameters = undefined; node.parameters = createNodeArray([parameter]); @@ -320,15 +246,21 @@ namespace ts { // Expression - export function createArrayLiteral(elements?: Expression[]) { - const node = createNode(SyntaxKind.ArrayLiteralExpression); + export function createArrayLiteral(elements?: Expression[], location?: TextRange, multiLine?: boolean) { + const node = createNode(SyntaxKind.ArrayLiteralExpression, location); node.elements = parenthesizeListElements(createNodeArray(elements)); + if (multiLine) { + node.multiLine = multiLine; + } return node; } - export function createObjectLiteral(properties?: ObjectLiteralElement[], location?: TextRange) { + export function createObjectLiteral(properties?: ObjectLiteralElement[], location?: TextRange, multiLine?: boolean) { const node = createNode(SyntaxKind.ObjectLiteralExpression, location); node.properties = createNodeArray(properties); + if (multiLine) { + node.multiLine = multiLine; + } return node; } @@ -356,7 +288,7 @@ namespace ts { export function createNew(expression: Expression, argumentsArray: Expression[], location?: TextRange) { const node = createNode(SyntaxKind.NewExpression, location); - node.expression = parenthesizeForAccess(expression); + node.expression = parenthesizeForNew(expression); node.arguments = argumentsArray ? parenthesizeListElements(createNodeArray(argumentsArray)) : undefined; @@ -369,7 +301,7 @@ namespace ts { return node; } - export function createFunctionExpression(asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + export function createFunctionExpression(asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, original?: Node) { const node = createNode(SyntaxKind.FunctionExpression, location); node.modifiers = undefined; node.asteriskToken = asteriskToken; @@ -378,6 +310,10 @@ namespace ts { node.parameters = createNodeArray(parameters); node.type = undefined; node.body = body; + if (original) { + node.original = original; + } + return node; } @@ -468,16 +404,19 @@ namespace ts { // Element - export function createBlock(statements: Statement[], location?: TextRange): Block { + export function createBlock(statements: Statement[], location?: TextRange, multiLine?: boolean): Block { const block = createNode(SyntaxKind.Block, location); block.statements = createNodeArray(statements); + if (multiLine) { + block.multiLine = true; + } return block; } export function createVariableStatement(modifiers: Modifier[], declarationList: VariableDeclarationList, location?: TextRange): VariableStatement { const node = createNode(SyntaxKind.VariableStatement, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.declarationList = declarationList; return node; } @@ -573,23 +512,26 @@ namespace ts { return node; } - export function createFunctionDeclaration(modifiers: Modifier[], asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + export function createFunctionDeclaration(modifiers: Modifier[], asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, original?: Node) { const node = createNode(SyntaxKind.FunctionDeclaration, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.asteriskToken = asteriskToken; node.name = typeof name === "string" ? createIdentifier(name) : name; node.typeParameters = undefined; node.parameters = createNodeArray(parameters); node.type = undefined; node.body = body; + if (original) { + node.original = original; + } return node; } export function createClassDeclaration(modifiers: Modifier[], name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { const node = createNode(SyntaxKind.ClassDeclaration, location); node.decorators = undefined; - setModifiers(node, modifiers); + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = name; node.typeParameters = undefined; node.heritageClauses = createNodeArray(heritageClauses); @@ -702,8 +644,8 @@ namespace ts { export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression { return isIdentifier(memberName) - ? createPropertyAccess(target, cloneNode(memberName), location) - : createElementAccess(target, cloneNode(isComputedPropertyName(memberName) ? memberName.expression : memberName), location); + ? createPropertyAccess(target, getSynthesizedClone(memberName), location) + : createElementAccess(target, getSynthesizedClone(isComputedPropertyName(memberName) ? memberName.expression : memberName), location); } export function createRestParameter(name: string | Identifier) { @@ -882,7 +824,7 @@ namespace ts { ); if (preferNewLine) { - property.startsOnNewLine = true; + startOnNewLine(property); } properties.push(property); @@ -1068,6 +1010,68 @@ namespace ts { ); } + export interface CallBinding { + target: LeftHandSideExpression; + thisArg: Expression; + } + + export function createCallBinding(expression: Expression, languageVersion?: ScriptTarget): CallBinding { + const callee = skipParentheses(expression); + let thisArg: Expression; + let target: LeftHandSideExpression; + if (isSuperProperty(callee)) { + thisArg = createThis(/*location*/ callee.expression); + target = callee; + } + else if (callee.kind === SyntaxKind.SuperKeyword) { + thisArg = createThis(/*location*/ callee); + target = languageVersion < ScriptTarget.ES6 ? createIdentifier("_super", /*location*/ callee) : callee; + } + else { + switch (callee.kind) { + case SyntaxKind.PropertyAccessExpression: { + // for `a.b()` target is `(_a = a).b` and thisArg is `_a` + thisArg = createTempVariable(); + target = createPropertyAccess( + createAssignment( + thisArg, + (callee).expression, + /*location*/ (callee).expression + ), + (callee).name, + /*location*/ callee + ); + break; + } + + case SyntaxKind.ElementAccessExpression: { + // for `a[b]()` target is `(_a = a)[b]` and thisArg is `_a` + thisArg = createTempVariable(); + target = createElementAccess( + createAssignment( + thisArg, + (callee).expression, + /*location*/ (callee).expression + ), + (callee).argumentExpression, + /*location*/ callee + ); + + break; + } + + default: { + // for `a()` target is `a` and thisArg is `void 0` + thisArg = createVoidZero(); + target = parenthesizeForAccess(expression); + break; + } + } + } + + return { target, thisArg }; + } + export function inlineExpressions(expressions: Expression[]) { return reduceLeft(expressions, createComma); } @@ -1076,18 +1080,17 @@ namespace ts { return isQualifiedName(node) ? createPropertyAccess( createExpressionFromEntityName(node.left), - cloneNode(node.right) + getSynthesizedClone(node.right) ) - : cloneNode(node); + : getSynthesizedClone(node); } export function createExpressionForPropertyName(memberName: PropertyName, location?: TextRange): Expression { return isIdentifier(memberName) ? createLiteral(memberName.text, location) - : isComputedPropertyName(memberName) ? cloneNode(memberName.expression, location) - : cloneNode(memberName, location); + : isComputedPropertyName(memberName) ? getRelocatedClone(memberName.expression, location) + : getRelocatedClone(memberName, location); } - // Utilities /** @@ -1207,6 +1210,23 @@ namespace ts { || binaryOperator === SyntaxKind.CaretToken; } + /** + * Wraps an expression in parentheses if it is needed in order to use the expression + * as the expression of a NewExpression node. + * + * @param expression The Expression node. + */ + export function parenthesizeForNew(expression: Expression): LeftHandSideExpression { + const lhs = parenthesizeForAccess(expression); + switch (lhs.kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + return createParen(lhs); + } + + return lhs; + } + /** * Wraps an expression in parentheses if it is needed in order to use the expression for * property or element access. @@ -1276,7 +1296,7 @@ namespace ts { const callee = expression.expression; if (callee.kind === SyntaxKind.FunctionExpression || callee.kind === SyntaxKind.ArrowFunction) { - const clone = cloneNode(expression, expression, expression.flags, expression.parent, expression); + const clone = getMutableClone(expression); clone.expression = createParen(callee, /*location*/ callee); return clone; } @@ -1354,4 +1374,14 @@ namespace ts { node.flags = flags; return node; } + + export function setMultiLine(node: T, multiLine: boolean): T { + node.multiLine = multiLine; + return node; + } + + export function setHasTrailingComma(nodes: NodeArray, hasTrailingComma: boolean): NodeArray { + nodes.hasTrailingComma = hasTrailingComma; + return nodes; + } } \ No newline at end of file diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c4813ac993217..14c701fb09e64 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -997,19 +997,6 @@ namespace ts { } array.pos = pos; array.end = pos; - array.arrayKind = ArrayKind.NodeArray; - return array; - } - - function createModifiersArray(elements?: Modifier[], pos?: number): ModifiersArray { - const array = (elements || []); - if (!(pos >= 0)) { - pos = getNodePos(); - } - array.pos = pos; - array.end = pos; - array.arrayKind = ArrayKind.ModifiersArray; - array.flags = 0; return array; } @@ -2019,17 +2006,10 @@ namespace ts { return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken; } - function setModifiers(node: Node, modifiers: ModifiersArray) { - if (modifiers) { - node.flags |= modifiers.flags; - node.modifiers = modifiers; - } - } - function parseParameter(): ParameterDeclaration { const node = createNode(SyntaxKind.Parameter); node.decorators = parseDecorators(); - setModifiers(node, parseModifiers()); + node.modifiers = parseModifiers(); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); // FormalParameter [Yield,Await]: @@ -2218,23 +2198,23 @@ namespace ts { return token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBracketToken; } - function parseIndexSignatureDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): IndexSignatureDeclaration { + function parseIndexSignatureDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): IndexSignatureDeclaration { const node = createNode(SyntaxKind.IndexSignature, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); node.type = parseTypeAnnotation(); parseTypeMemberSemicolon(); return finishNode(node); } - function parsePropertyOrMethodSignature(fullStart: number, modifiers: ModifiersArray): PropertySignature | MethodSignature { + function parsePropertyOrMethodSignature(fullStart: number, modifiers: NodeArray): PropertySignature | MethodSignature { const name = parsePropertyName(); const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { const method = createNode(SyntaxKind.MethodSignature, fullStart); - setModifiers(method, modifiers); + method.modifiers = modifiers; method.name = name; method.questionToken = questionToken; @@ -2246,7 +2226,7 @@ namespace ts { } else { const property = createNode(SyntaxKind.PropertySignature, fullStart); - setModifiers(property, modifiers); + property.modifiers = modifiers; property.name = name; property.questionToken = questionToken; property.type = parseTypeAnnotation(); @@ -2826,7 +2806,7 @@ namespace ts { return undefined; } - const isAsync = !!(arrowFunction.flags & NodeFlags.Async); + const isAsync = !!(getModifierFlags(arrowFunction) & ModifierFlags.Async); // If we have an arrow, then try to parse the body. Even if not, try to parse if we // have an opening brace, just in case we're in an error state. @@ -2971,8 +2951,8 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); - setModifiers(node, parseModifiersForArrowFunction()); - const isAsync = !!(node.flags & NodeFlags.Async); + node.modifiers = parseModifiersForArrowFunction(); + const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); // Arrow functions are never generators. // @@ -3942,7 +3922,7 @@ namespace ts { return finishNode(node); } - function tryParseAccessorDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): AccessorDeclaration { + function tryParseAccessorDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): AccessorDeclaration { if (parseContextualModifier(SyntaxKind.GetKeyword)) { return parseAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, decorators, modifiers); } @@ -4027,12 +4007,12 @@ namespace ts { } const node = createNode(SyntaxKind.FunctionExpression); - setModifiers(node, parseModifiers()); + node.modifiers = parseModifiers(); parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); const isGenerator = !!node.asteriskToken; - const isAsync = !!(node.flags & NodeFlags.Async); + const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : isGenerator ? doInYieldContext(parseOptionalIdentifier) : @@ -4615,7 +4595,7 @@ namespace ts { const node = createMissingNode(SyntaxKind.MissingDeclaration, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); node.pos = fullStart; node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; return finishNode(node); } } @@ -4750,57 +4730,57 @@ namespace ts { return nextTokenIsIdentifier() && nextToken() === SyntaxKind.CloseParenToken; } - function parseVariableStatement(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): VariableStatement { + function parseVariableStatement(fullStart: number, decorators: NodeArray, modifiers: NodeArray): VariableStatement { const node = createNode(SyntaxKind.VariableStatement, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; node.declarationList = parseVariableDeclarationList(/*inForStatementInitializer*/ false); parseSemicolon(); return addJSDocComment(finishNode(node)); } - function parseFunctionDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): FunctionDeclaration { + function parseFunctionDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): FunctionDeclaration { const node = createNode(SyntaxKind.FunctionDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - node.name = node.flags & NodeFlags.Default ? parseOptionalIdentifier() : parseIdentifier(); + node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier(); const isGenerator = !!node.asteriskToken; - const isAsync = !!(node.flags & NodeFlags.Async); + const isAsync = hasModifier(node, ModifierFlags.Async); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } - function parseConstructorDeclaration(pos: number, decorators: NodeArray, modifiers: ModifiersArray): ConstructorDeclaration { + function parseConstructorDeclaration(pos: number, decorators: NodeArray, modifiers: NodeArray): ConstructorDeclaration { const node = createNode(SyntaxKind.Constructor, pos); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.ConstructorKeyword); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } - function parseMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, asteriskToken: Node, name: PropertyName, questionToken: Node, diagnosticMessage?: DiagnosticMessage): MethodDeclaration { + function parseMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, asteriskToken: Node, name: PropertyName, questionToken: Node, diagnosticMessage?: DiagnosticMessage): MethodDeclaration { const method = createNode(SyntaxKind.MethodDeclaration, fullStart); method.decorators = decorators; - setModifiers(method, modifiers); + method.modifiers = modifiers; method.asteriskToken = asteriskToken; method.name = name; method.questionToken = questionToken; const isGenerator = !!asteriskToken; - const isAsync = !!(method.flags & NodeFlags.Async); + const isAsync = hasModifier(method, ModifierFlags.Async); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, method); method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage); return addJSDocComment(finishNode(method)); } - function parsePropertyDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, name: PropertyName, questionToken: Node): ClassElement { + function parsePropertyDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, name: PropertyName, questionToken: Node): ClassElement { const property = createNode(SyntaxKind.PropertyDeclaration, fullStart); property.decorators = decorators; - setModifiers(property, modifiers); + property.modifiers = modifiers; property.name = name; property.questionToken = questionToken; property.type = parseTypeAnnotation(); @@ -4814,7 +4794,7 @@ namespace ts { // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initialiser_opt[In, ?Yield]; // // The checker may still error in the static case to explicitly disallow the yield expression. - property.initializer = modifiers && modifiers.flags & NodeFlags.Static + property.initializer = hasModifier(property, ModifierFlags.Static) ? allowInAnd(parseNonParameterInitializer) : doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.DisallowInContext, parseNonParameterInitializer); @@ -4822,7 +4802,7 @@ namespace ts { return finishNode(property); } - function parsePropertyOrMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ClassElement { + function parsePropertyOrMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ClassElement { const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); const name = parsePropertyName(); @@ -4841,10 +4821,10 @@ namespace ts { return parseInitializer(/*inParameter*/ false); } - function parseAccessorDeclaration(kind: SyntaxKind, fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): AccessorDeclaration { + function parseAccessorDeclaration(kind: SyntaxKind, fullStart: number, decorators: NodeArray, modifiers: NodeArray): AccessorDeclaration { const node = createNode(kind, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; node.name = parsePropertyName(); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false); @@ -4963,9 +4943,8 @@ namespace ts { * * In such situations, 'permitInvalidConstAsModifier' should be set to true. */ - function parseModifiers(permitInvalidConstAsModifier?: boolean): ModifiersArray { - let flags = 0; - let modifiers: ModifiersArray; + function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray { + let modifiers: NodeArray; while (true) { const modifierStart = scanner.getStartPos(); const modifierKind = token; @@ -4983,31 +4962,28 @@ namespace ts { } } - flags |= modifierToFlag(modifierKind); const modifier = finishNode(createNode(modifierKind, modifierStart)); if (!modifiers) { - modifiers = createModifiersArray([modifier], modifierStart); + modifiers = createNodeArray([modifier], modifierStart); } else { modifiers.push(modifier); } } if (modifiers) { - modifiers.flags = flags; modifiers.end = scanner.getStartPos(); } return modifiers; } - function parseModifiersForArrowFunction(): ModifiersArray { - let modifiers: ModifiersArray; + function parseModifiersForArrowFunction(): NodeArray { + let modifiers: NodeArray; if (token === SyntaxKind.AsyncKeyword) { const modifierStart = scanner.getStartPos(); const modifierKind = token; nextToken(); const modifier = finishNode(createNode(modifierKind, modifierStart)); - modifiers = createModifiersArray([modifier], modifierStart); - modifiers.flags = modifierToFlag(modifierKind); + modifiers = createNodeArray([modifier], modifierStart); modifiers.end = scanner.getStartPos(); } @@ -5067,14 +5043,14 @@ namespace ts { SyntaxKind.ClassExpression); } - function parseClassDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ClassDeclaration { + function parseClassDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ClassDeclaration { return parseClassDeclarationOrExpression(fullStart, decorators, modifiers, SyntaxKind.ClassDeclaration); } - function parseClassDeclarationOrExpression(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, kind: SyntaxKind): ClassLikeDeclaration { + function parseClassDeclarationOrExpression(fullStart: number, decorators: NodeArray, modifiers: NodeArray, kind: SyntaxKind): ClassLikeDeclaration { const node = createNode(kind, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.ClassKeyword); node.name = parseNameOfClassDeclarationOrExpression(); node.typeParameters = parseTypeParameters(); @@ -5149,10 +5125,10 @@ namespace ts { return parseList(ParsingContext.ClassMembers, parseClassElement); } - function parseInterfaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): InterfaceDeclaration { + function parseInterfaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): InterfaceDeclaration { const node = createNode(SyntaxKind.InterfaceDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.InterfaceKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); @@ -5161,10 +5137,10 @@ namespace ts { return finishNode(node); } - function parseTypeAliasDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): TypeAliasDeclaration { + function parseTypeAliasDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): TypeAliasDeclaration { const node = createNode(SyntaxKind.TypeAliasDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.TypeKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); @@ -5185,10 +5161,10 @@ namespace ts { return finishNode(node); } - function parseEnumDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): EnumDeclaration { + function parseEnumDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): EnumDeclaration { const node = createNode(SyntaxKind.EnumDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -5213,25 +5189,25 @@ namespace ts { return finishNode(node); } - function parseModuleOrNamespaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, flags: NodeFlags): ModuleDeclaration { + function parseModuleOrNamespaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, flags: NodeFlags): ModuleDeclaration { const node = createNode(SyntaxKind.ModuleDeclaration, fullStart); // If we are parsing a dotted namespace name, we want to // propagate the 'Namespace' flag across the names if set. const namespaceFlag = flags & NodeFlags.Namespace; node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; node.flags |= flags; node.name = parseIdentifier(); node.body = parseOptional(SyntaxKind.DotToken) - ? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.Export | namespaceFlag) + ? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.NestedNamespace | namespaceFlag) : parseModuleBlock(); return finishNode(node); } - function parseAmbientExternalModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ModuleDeclaration { + function parseAmbientExternalModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ModuleDeclaration { const node = createNode(SyntaxKind.ModuleDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; if (token === SyntaxKind.GlobalKeyword) { // parse 'global' as name of global scope augmentation node.name = parseIdentifier(); @@ -5244,8 +5220,8 @@ namespace ts { return finishNode(node); } - function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ModuleDeclaration { - let flags = modifiers ? modifiers.flags : 0; + function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ModuleDeclaration { + let flags: NodeFlags = 0; if (token === SyntaxKind.GlobalKeyword) { // global augmentation return parseAmbientExternalModuleDeclaration(fullStart, decorators, modifiers); @@ -5275,7 +5251,7 @@ namespace ts { return nextToken() === SyntaxKind.SlashToken; } - function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ImportEqualsDeclaration | ImportDeclaration { + function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ImportEqualsDeclaration | ImportDeclaration { parseExpected(SyntaxKind.ImportKeyword); const afterImportPos = scanner.getStartPos(); @@ -5288,7 +5264,7 @@ namespace ts { // import x = M.x; const importEqualsDeclaration = createNode(SyntaxKind.ImportEqualsDeclaration, fullStart); importEqualsDeclaration.decorators = decorators; - setModifiers(importEqualsDeclaration, modifiers); + importEqualsDeclaration.modifiers = modifiers; importEqualsDeclaration.name = identifier; parseExpected(SyntaxKind.EqualsToken); importEqualsDeclaration.moduleReference = parseModuleReference(); @@ -5300,7 +5276,7 @@ namespace ts { // Import statement const importDeclaration = createNode(SyntaxKind.ImportDeclaration, fullStart); importDeclaration.decorators = decorators; - setModifiers(importDeclaration, modifiers); + importDeclaration.modifiers = modifiers; // ImportDeclaration: // import ImportClause from ModuleSpecifier ; @@ -5436,10 +5412,10 @@ namespace ts { return finishNode(node); } - function parseExportDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ExportDeclaration { + function parseExportDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ExportDeclaration { const node = createNode(SyntaxKind.ExportDeclaration, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; if (parseOptional(SyntaxKind.AsteriskToken)) { parseExpected(SyntaxKind.FromKeyword); node.moduleSpecifier = parseModuleSpecifier(); @@ -5459,10 +5435,10 @@ namespace ts { return finishNode(node); } - function parseExportAssignment(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ExportAssignment { + function parseExportAssignment(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ExportAssignment { const node = createNode(SyntaxKind.ExportAssignment, fullStart); node.decorators = decorators; - setModifiers(node, modifiers); + node.modifiers = modifiers; if (parseOptional(SyntaxKind.EqualsToken)) { node.isExportEquals = true; } @@ -5541,7 +5517,7 @@ namespace ts { function setExternalModuleIndicator(sourceFile: SourceFile) { sourceFile.externalModuleIndicator = forEach(sourceFile.statements, node => - node.flags & NodeFlags.Export + hasModifier(node, ModifierFlags.Export) || node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference || node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ExportAssignment diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index f997e23d9122b..8b1c73abae544 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -6,9 +6,6 @@ /* @internal */ namespace ts { - const delimiters = createDelimiterMap(); - const brackets = createBracketsMap(); - // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { Auto = 0x00000000, // No preferred name @@ -18,6 +15,9 @@ namespace ts { // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function printFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { + const delimiters = createDelimiterMap(); + const brackets = createBracketsMap(); + // emit output for the __extends helper function const extendsHelper = ` var __extends = (this && this.__extends) || function (d, b) { @@ -147,6 +147,7 @@ const _super = (function (geti, seti) { let startLexicalEnvironment: () => void; let endLexicalEnvironment: () => Statement[]; let getNodeEmitFlags: (node: Node) => NodeEmitFlags; + let setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void; let isExpressionSubstitutionEnabled: (node: Node) => boolean; let isEmitNotificationEnabled: (node: Node) => boolean; let expressionSubstitution: (node: Expression) => Expression; @@ -208,6 +209,7 @@ const _super = (function (geti, seti) { startLexicalEnvironment = undefined; endLexicalEnvironment = undefined; getNodeEmitFlags = undefined; + setNodeEmitFlags = undefined; isExpressionSubstitutionEnabled = undefined; isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; @@ -228,6 +230,7 @@ const _super = (function (geti, seti) { startLexicalEnvironment = context.startLexicalEnvironment; endLexicalEnvironment = context.endLexicalEnvironment; getNodeEmitFlags = context.getNodeEmitFlags; + setNodeEmitFlags = context.setNodeEmitFlags; isExpressionSubstitutionEnabled = context.isExpressionSubstitutionEnabled; isEmitNotificationEnabled = context.isEmitNotificationEnabled; expressionSubstitution = context.expressionSubstitution; @@ -672,7 +675,7 @@ const _super = (function (geti, seti) { // function emitIdentifier(node: Identifier) { - if (getNodeEmitFlags(node) && NodeEmitFlags.UMDDefine) { + if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { writeLines(umdHelper); } else { @@ -1408,6 +1411,10 @@ const _super = (function (geti, seti) { } function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { + if (body.multiLine) { + return false; + } + const originalNode = getOriginalNode(parentNode); if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode)) { const body = originalNode.body; @@ -1951,7 +1958,7 @@ const _super = (function (geti, seti) { } } - function emitModifiers(node: Node, modifiers: ModifiersArray) { + function emitModifiers(node: Node, modifiers: NodeArray) { if (modifiers && modifiers.length) { emitList(node, modifiers, ListFormat.SingleLine); write(" "); @@ -1981,10 +1988,13 @@ const _super = (function (geti, seti) { } function tryEmitSubstitute(node: Node, substitution: (node: Node) => Node) { - const substitute = substitution ? substitution(node) : node; - if (substitute && substitute !== node) { - emitWorker(substitute); - return true; + if (substitution && (getNodeEmitFlags(node) & NodeEmitFlags.NoSubstitution) === 0) { + const substitute = substitution(node); + if (substitute !== node) { + setNodeEmitFlags(substitute, NodeEmitFlags.NoSubstitution | getNodeEmitFlags(substitute)); + emitWorker(substitute); + return true; + } } return false; @@ -2510,36 +2520,36 @@ const _super = (function (geti, seti) { return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateIdentifier(node))); } } - } - function createDelimiterMap() { - const delimiters: string[] = []; - delimiters[ListFormat.None] = ""; - delimiters[ListFormat.CommaDelimited] = ","; - delimiters[ListFormat.BarDelimited] = " |"; - delimiters[ListFormat.AmpersandDelimited] = " &"; - return delimiters; - } + function createDelimiterMap() { + const delimiters: string[] = []; + delimiters[ListFormat.None] = ""; + delimiters[ListFormat.CommaDelimited] = ","; + delimiters[ListFormat.BarDelimited] = " |"; + delimiters[ListFormat.AmpersandDelimited] = " &"; + return delimiters; + } - function getDelimiter(format: ListFormat) { - return delimiters[format & ListFormat.DelimitersMask]; - } + function getDelimiter(format: ListFormat) { + return delimiters[format & ListFormat.DelimitersMask]; + } - function createBracketsMap() { - const brackets: string[][] = []; - brackets[ListFormat.Braces] = ["{", "}"]; - brackets[ListFormat.Parenthesis] = ["(", ")"]; - brackets[ListFormat.AngleBrackets] = ["<", ">"]; - brackets[ListFormat.SquareBrackets] = ["[", "]"]; - return brackets; - } + function createBracketsMap() { + const brackets: string[][] = []; + brackets[ListFormat.Braces] = ["{", "}"]; + brackets[ListFormat.Parenthesis] = ["(", ")"]; + brackets[ListFormat.AngleBrackets] = ["<", ">"]; + brackets[ListFormat.SquareBrackets] = ["[", "]"]; + return brackets; + } - function getOpeningBracket(format: ListFormat) { - return brackets[format & ListFormat.BracketsMask][0]; - } + function getOpeningBracket(format: ListFormat) { + return brackets[format & ListFormat.BracketsMask][0]; + } - function getClosingBracket(format: ListFormat) { - return brackets[format & ListFormat.BracketsMask][1]; + function getClosingBracket(format: ListFormat) { + return brackets[format & ListFormat.BracketsMask][1]; + } } const enum ListFormat { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 87e8775d1f0d9..d5472907edb23 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1196,7 +1196,7 @@ namespace ts { return false; } - function checkModifiers(modifiers: ModifiersArray): boolean { + function checkModifiers(modifiers: NodeArray): boolean { if (modifiers) { for (const modifier of modifiers) { switch (modifier.kind) { @@ -1311,7 +1311,7 @@ namespace ts { } break; case SyntaxKind.ModuleDeclaration: - if (isAmbientModule(node) && (inAmbientModule || node.flags & NodeFlags.Ambient || isDeclarationFile(file))) { + if (isAmbientModule(node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || isDeclarationFile(file))) { const moduleName = (node).name; // Ambient module declarations can be interpreted as augmentations for some existing external modules. // This will happen in two cases: diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 68c8f5bc79485..2ce54b9b9ed4d 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -433,9 +433,7 @@ namespace ts { /* @internal */ export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number { - // Using ! with a greater than test is a fast way of testing the following conditions: - // pos === undefined || pos === null || isNaN(pos) || pos < 0; - if (!(pos >= 0)) { + if (positionIsSynthesized(pos)) { return pos; } @@ -642,6 +640,7 @@ namespace ts { pos++; } } + if (collecting) { if (!result) { result = []; diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 9c3972b27566a..f3973e852c6bb 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -8,6 +8,7 @@ namespace ts { args: string[]; newLine: string; useCaseSensitiveFileNames: boolean; + /*@internal*/ developmentMode?: boolean; write(s: string): void; readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; @@ -22,6 +23,7 @@ namespace ts { readDirectory(path: string, extension?: string, exclude?: string[]): string[]; getMemoryUsage?(): number; exit(exitCode?: number): void; + /*@internal*/ tryEnableSourceMapsForHost?(): void; } interface WatchedFile { @@ -494,6 +496,7 @@ namespace ts { args: process.argv.slice(2), newLine: _os.EOL, useCaseSensitiveFileNames: useCaseSensitiveFileNames, + developmentMode: /^development$/i.test(String(process.env.NODE_ENV)), write(s: string): void { process.stdout.write(s); }, @@ -564,6 +567,14 @@ namespace ts { }, exit(exitCode?: number): void { process.exit(exitCode); + }, + tryEnableSourceMapsForHost() { + try { + require("source-map-support").install(); + } + catch (e) { + // Could not enable source maps. + } } }; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 442201a1f7d1e..59dbe0307bc15 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -16,7 +16,7 @@ namespace ts { node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void, - visitor?: (node: Node) => Node) { + visitor?: (node: Node) => VisitResult) { if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { return node.right; @@ -79,7 +79,7 @@ namespace ts { * @param value The rhs value for the binding pattern. * @param visitor An optional visitor to use to visit expressions. */ - export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression, visitor?: (node: Node) => Node) { + export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression, visitor?: (node: Node) => VisitResult) { const declarations: VariableDeclaration[] = []; flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); @@ -110,7 +110,7 @@ namespace ts { * @param value An optional rhs value for the binding pattern. * @param visitor An optional visitor to use to visit expressions. */ - export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression, visitor?: (node: Node) => Node) { + export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression, visitor?: (node: Node) => VisitResult) { const declarations: VariableDeclaration[] = []; flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); @@ -151,7 +151,7 @@ namespace ts { node: VariableDeclaration, recordTempVariable: (name: Identifier) => void, nameSubstitution?: (name: Identifier) => Expression, - visitor?: (node: Node) => Node) { + visitor?: (node: Node) => VisitResult) { const pendingAssignments: Expression[] = []; @@ -191,7 +191,7 @@ namespace ts { location: TextRange, emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - visitor?: (node: Node) => Node) { + visitor?: (node: Node) => VisitResult) { if (value && visitor) { value = visitNode(value, visitor, isExpression); } @@ -236,7 +236,7 @@ namespace ts { emitArrayLiteralAssignment(target, value, location); } else { - const name = cloneNode(target, /*location*/ target, /*flags*/ undefined, /*parent*/ undefined, /*original*/ target); + const name = getRelocatedClone(target, /*location*/ target); emitAssignment(name, value, location, /*original*/ undefined); } } @@ -326,7 +326,7 @@ namespace ts { } } else { - const clonedName = cloneNode(name, /*location*/ undefined, /*flags*/ undefined, /*parent*/ undefined, /*original*/ name); + const clonedName = getSynthesizedClone(name); emitAssignment(clonedName, value, target, target); } } @@ -365,7 +365,7 @@ namespace ts { // otherwise occur when the identifier is emitted. return createElementAccess( expression, - cloneNode(propertyName) + getSynthesizedClone(propertyName) ); } } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index d5c5f9566db79..05c001a711411 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -3,17 +3,80 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): ES6->ES5 transformer + + const enum ES6SubstitutionFlags { + /** Enables substitutions for captured `this` */ + CapturedThis = 1 << 0, + /** Enables substitutions for block-scoped bindings. */ + BlockScopedBindings = 1 << 1, + } + export function transformES6(context: TransformationContext) { + const { + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + getNodeEmitFlags, + setNodeEmitFlags, + } = context; + + const resolver = context.getEmitResolver(); + const previousIdentifierSubstitution = context.identifierSubstitution; + const previousExpressionSubstitution = context.expressionSubstitution; + const previousOnEmitNode = context.onEmitNode; + context.onEmitNode = onEmitNode; + context.identifierSubstitution = substituteIdentifier; + context.expressionSubstitution = substituteExpression; + + let currentSourceFile: SourceFile; + let currentParent: Node; + let currentNode: Node; + let enclosingBlockScopeContainer: Node; + let enclosingBlockScopeContainerParent: Node; + let containingNonArrowFunction: FunctionLikeDeclaration; + + /** + * Keeps track of whether substitutions have been enabled for specific cases. + * They are persisted between each SourceFile transformation and should not + * be reset. + */ + let enabledSubstitutions: ES6SubstitutionFlags; + + /** + * This is used to determine whether we need to emit `_this` instead of `this`. + */ + let useCapturedThis: boolean; + return transformSourceFile; function transformSourceFile(node: SourceFile) { + currentSourceFile = node; + enclosingBlockScopeContainer = node; return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): VisitResult { + const savedContainingNonArrowFunction = containingNonArrowFunction; + const savedCurrentParent = currentParent; + const savedCurrentNode = currentNode; + const savedEnclosingBlockScopeContainer = enclosingBlockScopeContainer; + const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent; + + onBeforeVisitNode(node); + + const visited = visitorWorker(node); + + containingNonArrowFunction = savedContainingNonArrowFunction; + currentParent = savedCurrentParent; + currentNode = savedCurrentNode; + enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer; + enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent; + return visited; + } + + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES6) { - return visitorWorker(node); + return visitJavaScript(node); } else if (node.transformFlags & TransformFlags.ContainsES6) { return visitEachChild(node, visitor, context); @@ -23,8 +86,1797 @@ namespace ts { } } - function visitorWorker(node: Node): Node { + function visitJavaScript(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node); + + case SyntaxKind.ClassExpression: + return visitClassExpression(node); + + case SyntaxKind.Parameter: + return visitParameter(node); + + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node); + + case SyntaxKind.ArrowFunction: + return visitArrowFunction(node); + + case SyntaxKind.FunctionExpression: + return visitFunctionExpression(node); + + case SyntaxKind.VariableDeclaration: + return visitVariableDeclaration(node); + + case SyntaxKind.VariableDeclarationList: + return visitVariableDeclarationList(node); + + case SyntaxKind.LabeledStatement: + return visitLabeledStatement(node); + + case SyntaxKind.DoStatement: + return visitDoStatement(node); + + case SyntaxKind.WhileStatement: + return visitWhileStatement(node); + + case SyntaxKind.ForStatement: + return visitForStatement(node); + + case SyntaxKind.ForInStatement: + return visitForInStatement(node); + + case SyntaxKind.ForOfStatement: + return visitForOfStatement(node); + + case SyntaxKind.ExpressionStatement: + return visitExpressionStatement(node); + + case SyntaxKind.ObjectLiteralExpression: + return visitObjectLiteralExpression(node); + + case SyntaxKind.ShorthandPropertyAssignment: + return visitShorthandPropertyAssignment(node); + + case SyntaxKind.ArrayLiteralExpression: + return visitArrayLiteralExpression(node); + + case SyntaxKind.CallExpression: + return visitCallExpression(node); + + case SyntaxKind.NewExpression: + return visitNewExpression(node); + + case SyntaxKind.ParenthesizedExpression: + return visitParenthesizedExpression(node, /*needsDestructuringValue*/ true); + + case SyntaxKind.BinaryExpression: + return visitBinaryExpression(node, /*needsDestructuringValue*/ true); + + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return visitTemplateLiteral(node); + + case SyntaxKind.TaggedTemplateExpression: + return visitTaggedTemplateExpression(node); + + case SyntaxKind.TemplateExpression: + return visitTemplateExpression(node); + + case SyntaxKind.SuperKeyword: + return visitSuperKeyword(node); + + case SyntaxKind.MethodDeclaration: + return visitMethodDeclaration(node); + + case SyntaxKind.SourceFile: + return visitSourceFileNode(node); + + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } + + } + + function onBeforeVisitNode(node: Node) { + const currentGrandparent = currentParent; + currentParent = currentNode; + currentNode = node; + + if (currentParent) { + if (isBlockScope(currentParent, currentGrandparent)) { + enclosingBlockScopeContainer = currentParent; + enclosingBlockScopeContainerParent = currentGrandparent; + } + + switch (currentParent.kind) { + case SyntaxKind.Constructor: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + containingNonArrowFunction = currentParent; + break; + } + } + } + + /** + * Visits a ClassDeclaration and transforms it into a variable statement. + * + * @param node A ClassDeclaration node. + */ + function visitClassDeclaration(node: ClassDeclaration): Statement { + // [source] + // class C { } + // + // [output] + // var C = (function () { + // function C() { + // } + // return C; + // }()); + + return startOnNewLine( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getDeclarationName(node), + transformClassLikeDeclarationToExpression(node) + ) + ]), + node + ) + ); + } + + /** + * Visits a ClassExpression and transforms it into an expression. + * + * @param node A ClassExpression node. + */ + function visitClassExpression(node: ClassExpression): Expression { + // [source] + // C = class { } + // + // [output] + // C = (function () { + // function class_1() { + // } + // return class_1; + // }()) + + return transformClassLikeDeclarationToExpression(node); + } + + /** + * Transforms a ClassExpression or ClassDeclaration into an expression. + * + * @param node A ClassExpression or ClassDeclaration node. + */ + function transformClassLikeDeclarationToExpression(node: ClassExpression | ClassDeclaration): Expression { + // [source] + // class C extends D { + // constructor() {} + // method() {} + // get prop() {} + // set prop(v) {} + // } + // + // [output] + // (function (_super) { + // __extends(C, _super); + // function C() { + // } + // C.prototype.method = function () {} + // Object.defineProperty(C.prototype, "prop", { + // get: function() {}, + // set: function() {}, + // enumerable: true, + // configurable: true + // }); + // return C; + // }(D)) + + const baseTypeNode = getClassExtendsHeritageClauseElement(node); + return createParen( + createCall( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + baseTypeNode ? [createParameter("_super")] : [], + transformClassBody(node, baseTypeNode !== undefined) + ), + baseTypeNode + ? [visitNode(baseTypeNode.expression, visitor, isExpression)] + : [] + ) + ); + } + + /** + * Transforms a ClassExpression or ClassDeclaration into a function body. + * + * @param node A ClassExpression or ClassDeclaration node. + * @param hasExtendsClause A value indicating whether the class has an `extends` clause. + */ + function transformClassBody(node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): Block { + const statements: Statement[] = []; + startLexicalEnvironment(); + addExtendsHelperIfNeeded(statements, node, hasExtendsClause); + addConstructor(statements, node, hasExtendsClause); + addClassMembers(statements, node); + statements.push(createReturn(getDeclarationName(node))); + addRange(statements, endLexicalEnvironment()); + return createBlock(statements, /*location*/ undefined, /*multiLine*/ true); + } + + /** + * Adds a call to the `__extends` helper if needed for a class. + * + * @param statements The statements of the class body function. + * @param node The ClassExpression or ClassDeclaration node. + * @param hasExtendsClause A value indicating whether the class has an `extends` clause. + */ + function addExtendsHelperIfNeeded(statements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + if (hasExtendsClause) { + statements.push( + createStatement( + createExtendsHelper(getDeclarationName(node)) + ) + ); + } + } + + /** + * Adds the constructor of the class to a class body function. + * + * @param statements The statements of the class body function. + * @param node The ClassExpression or ClassDeclaration node. + * @param hasExtendsClause A value indicating whether the class has an `extends` clause. + */ + function addConstructor(statements: Statement[], node: ClassExpression | ClassDeclaration, hasExtendsClause: boolean): void { + const constructor = getFirstConstructorWithBody(node); + const hasSynthesizedSuper = hasSynthesizedDefaultSuperCall(constructor, hasExtendsClause); + statements.push( + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + getDeclarationName(node), + transformConstructorParameters(constructor, hasSynthesizedSuper), + transformConstructorBody(constructor, hasExtendsClause, hasSynthesizedSuper), + /*location*/ constructor + ) + ); + } + + /** + * Transforms the parameters of the constructor declaration of a class. + * + * @param constructor The constructor for the class. + * @param hasSynthesizedSuper A value indicating whether the constructor starts with a + * synthesized `super` call. + */ + function transformConstructorParameters(constructor: ConstructorDeclaration, hasSynthesizedSuper: boolean): ParameterDeclaration[] { + // If the TypeScript transformer needed to synthesize a constructor for property + // initializers, it would have also added a synthetic `...args` parameter and + // `super` call. + // If this is the case, we do not include the synthetic `...args` parameter and + // will instead use the `arguments` object in ES5/3. + if (constructor && !hasSynthesizedSuper) { + return visitNodes(constructor.parameters, visitor, isParameter); + } + + return []; + } + + /** + * Transforms the body of a constructor declaration of a class. + * + * @param constructor The constructor for the class. + * @param hasExtendsClause A value indicating whether the class has an `extends` clause. + * @param hasSynthesizedSuper A value indicating whether the constructor starts with a + * synthesized `super` call. + */ + function transformConstructorBody(constructor: ConstructorDeclaration, hasExtendsClause: boolean, hasSynthesizedSuper: boolean) { + const statements: Statement[] = []; + startLexicalEnvironment(); + if (constructor) { + addCaptureThisForNodeIfNeeded(statements, constructor); + addDefaultValueAssignmentsIfNeeded(statements, constructor); + addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); + } + + addDefaultSuperCallIfNeeded(statements, constructor, hasExtendsClause, hasSynthesizedSuper); + + if (constructor) { + addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, hasSynthesizedSuper ? 1 : 0)); + } + + addRange(statements, endLexicalEnvironment()); + return createBlock(statements, /*location*/ constructor && constructor.body, /*multiLine*/ true); + } + + /** + * Adds a synthesized call to `_super` if it is needed. + * + * @param statements The statements for the new constructor body. + * @param constructor The constructor for the class. + * @param hasExtendsClause A value indicating whether the class has an `extends` clause. + * @param hasSynthesizedSuper A value indicating whether the constructor starts with a + * synthesized `super` call. + */ + function addDefaultSuperCallIfNeeded(statements: Statement[], constructor: ConstructorDeclaration, hasExtendsClause: boolean, hasSynthesizedSuper: boolean) { + // If the TypeScript transformer needed to synthesize a constructor for property + // initializers, it would have also added a synthetic `...args` parameter and + // `super` call. + // If this is the case, or if the class has an `extends` clause but no + // constructor, we emit a synthesized call to `_super`. + if (constructor ? hasSynthesizedSuper : hasExtendsClause) { + statements.push( + createStatement( + createFunctionApply( + createIdentifier("_super"), + createThis(), + createIdentifier("arguments") + ) + ) + ); + } + } + + /** + * Visits a parameter declaration. + * + * @param node A ParameterDeclaration node. + */ + function visitParameter(node: ParameterDeclaration): ParameterDeclaration { + if (isBindingPattern(node.name)) { + // Binding patterns are converted into a generated name and are + // evaluated inside the function body. + return createParameter( + getGeneratedNameForNode(node), + /*initializer*/ undefined, + /*location*/ node + ); + } + else if (node.initializer) { + // Initializers are elided + return createParameter( + node.name, + /*initializer*/ undefined, + /*location*/ node + ); + } + else if (node.dotDotDotToken) { + // rest parameters are elided + return undefined; + } + else { + return node; + } + } + + /** + * Gets a value indicating whether we need to add default value assignments for a + * function-like node. + * + * @param node A function-like node. + */ + function shouldAddDefaultValueAssignments(node: FunctionLikeDeclaration): boolean { + return (node.transformFlags & TransformFlags.ContainsDefaultValueAssignments) !== 0; + } + + /** + * Adds statements to the body of a function-like node if it contains parameters with + * binding patterns or initializers. + * + * @param statements The statements for the new function body. + * @param node A function-like node. + */ + function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): void { + if (!shouldAddDefaultValueAssignments(node)) { + return; + } + + for (const parameter of node.parameters) { + const { name, initializer, dotDotDotToken } = parameter; + + // A rest parameter cannot have a binding pattern or an initializer, + // so let's just ignore it. + if (dotDotDotToken) { + continue; + } + + if (isBindingPattern(name)) { + addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer); + } + else if (initializer) { + addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer); + } + } + } + + /** + * Adds statements to the body of a function-like node for parameters with binding patterns + * + * @param statements The statements for the new function body. + * @param parameter The parameter for the function. + * @param name The name of the parameter. + * @param initializer The initializer for the parameter. + */ + function addDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression): void { + const temp = getGeneratedNameForNode(parameter); + + // In cases where a binding pattern is simply '[]' or '{}', + // we usually don't want to emit a var declaration; however, in the presence + // of an initializer, we must emit that expression to preserve side effects. + if (name.elements.length > 0) { + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + flattenParameterDestructuring(parameter, temp, visitor) + ) + ) + ); + } + else if (initializer) { + statements.push( + createStatement( + createAssignment( + temp, + visitNode(initializer, visitor, isExpression) + ) + ) + ); + } + } + + /** + * Adds statements to the body of a function-like node for parameters with initializers. + * + * @param statements The statements for the new function body. + * @param parameter The parameter for the function. + * @param name The name of the parameter. + * @param initializer The initializer for the parameter. + */ + function addDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void { + statements.push( + createIf( + createStrictEquality( + getSynthesizedClone(name), + createVoidZero() + ), + setNodeEmitFlags( + createBlock([ + createStatement( + createAssignment( + getSynthesizedClone(name), + visitNode(initializer, visitor, isExpression) + ) + ) + ]), + NodeEmitFlags.SingleLine + ) + ) + ); + } + + /** + * Gets a value indicating whether we need to add statements to handle a rest parameter. + * + * @param node A ParameterDeclaration node. + * @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is + * part of a constructor declaration with a + * synthesized call to `super` + */ + function shouldAddRestParameter(node: ParameterDeclaration, inConstructorWithSynthesizedSuper: boolean) { + return node && node.dotDotDotToken && !inConstructorWithSynthesizedSuper; + } + + /** + * Adds statements to the body of a function-like node if it contains a rest parameter. + * + * @param statements The statements for the new function body. + * @param node A function-like node. + * @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is + * part of a constructor declaration with a + * synthesized call to `super` + */ + function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void { + const parameter = lastOrUndefined(node.parameters); + if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) { + return; + } + + const name = getSynthesizedClone(parameter.name); + const restIndex = node.parameters.length - 1; + const temp = createLoopVariable(); + + // var param = []; + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + name, + createArrayLiteral([]) + ) + ]) + ) + ); + + // for (var _i = restIndex; _i < arguments.length; _i++) { + // param[_i - restIndex] = arguments[_i]; + // } + statements.push( + createFor( + createVariableDeclarationList([ + createVariableDeclaration(temp, createLiteral(restIndex)) + ]), + createLessThan( + temp, + createPropertyAccess(createIdentifier("arguments"), "length") + ), + createPostfixIncrement(temp), + createBlock([ + startOnNewLine( + createStatement( + createAssignment( + createElementAccess( + name, + createSubtract(temp, createLiteral(restIndex)) + ), + createElementAccess(createIdentifier("arguments"), temp) + ) + ) + ) + ]) + ) + ); + } + + /** + * Adds a statement to capture the `this` of a function declaration if it is needed. + * + * @param statements The statements for the new function body. + * @param node A node. + */ + function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): void { + if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) { + enableSubstitutionsForCapturedThis(); + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + "_this", + createThis() + ) + ]) + ) + ); + } + } + + /** + * Adds statements to the class body function for a class to define the members of the + * class. + * + * @param statements The statements for the class body function. + * @param node The ClassExpression or ClassDeclaration node. + */ + function addClassMembers(statements: Statement[], node: ClassExpression | ClassDeclaration): void { + for (const member of node.members) { + switch (member.kind) { + case SyntaxKind.SemicolonClassElement: + statements.push(transformSemicolonClassElementToStatement(member)); + break; + + case SyntaxKind.MethodDeclaration: + statements.push(transformClassMethodDeclarationToStatement(getClassMemberPrefix(node, member), member)); + break; + + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + const accessors = getAllAccessorDeclarations(node.members, member); + if (member === accessors.firstAccessor) { + statements.push(transformAccessorsToStatement(getClassMemberPrefix(node, member), accessors)); + } + + break; + + case SyntaxKind.Constructor: + // Constructors are handled in visitClassExpression/visitClassDeclaration + break; + + default: + Debug.failBadSyntaxKind(node); + break; + } + } + } + + /** + * Transforms a SemicolonClassElement into a statement for a class body function. + * + * @param member The SemicolonClassElement node. + */ + function transformSemicolonClassElementToStatement(member: SemicolonClassElement) { + return createEmptyStatement(/*location*/ member); + } + + /** + * Transforms a MethodDeclaration into a statement for a class body function. + * + * @param receiver The receiver for the member. + * @param member The MethodDeclaration node. + */ + function transformClassMethodDeclarationToStatement(receiver: LeftHandSideExpression, member: MethodDeclaration) { + return createStatement( + createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(member.name, visitor, isPropertyName) + ), + transformFunctionLikeToExpression(member, /*location*/ undefined, /*name*/ undefined) + ), + /*location*/ member + ); + } + + /** + * Transforms a set of related of get/set accessors into a statement for a class body function. + * + * @param receiver The receiver for the member. + * @param accessors The set of related get/set accessors. + */ + function transformAccessorsToStatement(receiver: LeftHandSideExpression, accessors: AllAccessorDeclarations): Statement { + return createStatement( + transformAccessorsToExpression(receiver, accessors) + ); + } + + /** + * Transforms a set of related get/set accessors into an expression for either a class + * body function or an ObjectLiteralExpression with computed properties. + * + * @param receiver The receiver for the member. + */ + function transformAccessorsToExpression(receiver: LeftHandSideExpression, { firstAccessor, getAccessor, setAccessor }: AllAccessorDeclarations): Expression { + return createObjectDefineProperty( + receiver, + createExpressionForPropertyName( + visitNode(firstAccessor.name, visitor, isPropertyName), + /*location*/ firstAccessor.name + ), + { + get: getAccessor && transformFunctionLikeToExpression(getAccessor, /*location*/ getAccessor, /*name*/ undefined), + set: setAccessor && transformFunctionLikeToExpression(setAccessor, /*location*/ setAccessor, /*name*/ undefined), + enumerable: true, + configurable: true + }, + /*preferNewLine*/ true, + /*location*/ firstAccessor + ); + } + + /** + * Visits an ArrowFunction and transforms it into a FunctionExpression. + * + * @param node An ArrowFunction node. + */ + function visitArrowFunction(node: ArrowFunction) { + if (node.transformFlags & TransformFlags.ContainsLexicalThis) { + enableSubstitutionsForCapturedThis(); + } + + const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); + setNodeEmitFlags(func, NodeEmitFlags.CapturesThis); + return func; + } + + /** + * Visits a FunctionExpression node. + * + * @param node a FunctionExpression node. + */ + function visitFunctionExpression(node: FunctionExpression): Expression { + return transformFunctionLikeToExpression(node, /*location*/ node, node.name); + } + + /** + * Visits a FunctionDeclaration node. + * + * @param node a FunctionDeclaration node. + */ + function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { + return createFunctionDeclaration( + /*modifiers*/ undefined, + node.asteriskToken, + node.name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + /*location*/ node, + /*original*/ node + ); + } + + /** + * Transforms a function-like node into a FunctionExpression. + * + * @param node The function-like node to transform. + * @param location The source-map location for the new FunctionExpression. + * @param name The name of the new FunctionExpression. + */ + function transformFunctionLikeToExpression(node: FunctionLikeDeclaration, location: TextRange, name: Identifier): FunctionExpression { + const savedContainingNonArrowFunction = containingNonArrowFunction; + if (node.kind !== SyntaxKind.ArrowFunction) { + containingNonArrowFunction = node; + } + + const expression = createFunctionExpression( + /*asteriskToken*/ undefined, + name, + visitNodes(node.parameters, visitor, isParameter), + transformFunctionBody(node), + location, + /*original*/ node + ); + + containingNonArrowFunction = savedContainingNonArrowFunction; + return expression; + } + + /** + * Transforms the body of a function-like node. + * + * @param node A function-like node. + */ + function transformFunctionBody(node: FunctionLikeDeclaration) { + const statements: Statement[] = []; + startLexicalEnvironment(); + addCaptureThisForNodeIfNeeded(statements, node); + addDefaultValueAssignmentsIfNeeded(statements, node); + addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); + + const body = node.body; + if (isBlock(body)) { + addRange(statements, visitNodes(body.statements, visitor, isStatement)); + } + else { + const expression = visitNode(body, visitor, isExpression); + if (expression) { + statements.push(createReturn(expression, /*location*/ body)); + } + } + + addRange(statements, endLexicalEnvironment()); + return createBlock(statements, node.body); + } + + /** + * Visits an ExpressionStatement that contains a destructuring assignment. + * + * @param node An ExpressionStatement node. + */ + function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { + // If we are here it is most likely because our expression is a destructuring assignment. + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return createStatement( + visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false), + /*location*/ node + ); + + case SyntaxKind.BinaryExpression: + return createStatement( + visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false), + /*location*/ node + ); + } + + return visitEachChild(node, visitor, context); + } + + /** + * Visits a ParenthesizedExpression that may contain a destructuring assignment. + * + * @param node A ParenthesizedExpression node. + * @param needsDestructuringValue A value indicating whether we need to hold onto the rhs + * of a destructuring assignment. + */ + function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { + // If we are here it is most likely because our expression is a destructuring assignment. + if (needsDestructuringValue) { + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return createParen( + visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ true), + /*location*/ node + ); + + case SyntaxKind.BinaryExpression: + return createParen( + visitBinaryExpression(node.expression, /*needsDestructuringValue*/ true), + /*location*/ node + ); + } + } + + return visitEachChild(node, visitor, context); + } + + /** + * Visits a BinaryExpression that contains a destructuring assignment. + * + * @param node A BinaryExpression node. + * @param needsDestructuringValue A value indicating whether we need to hold onto the rhs + * of a destructuring assignment. + */ + function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { + // If we are here it is because this is a destructuring assignment. + Debug.assert(isDestructuringAssignment(node)); + return flattenDestructuringAssignment(node, needsDestructuringValue, hoistVariableDeclaration, visitor); + } + + /** + * Visits a VariableDeclarationList that is block scoped (e.g. `let` or `const`). + * + * @param node A VariableDeclarationList node. + */ + function visitVariableDeclarationList(node: VariableDeclarationList): VariableDeclarationList { + // If we are here it is because the list is defined as `let` or `const`. + Debug.assert((node.flags & NodeFlags.BlockScoped) !== 0); + + enableSubstitutionsForBlockScopedBindings(); + return setOriginalNode( + createVariableDeclarationList( + flatten(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), + /*location*/ node + ), + node + ); + } + + /** + * Gets a value indicating whether we should emit an explicit initializer for a variable + * declaration in a `let` declaration list. + * + * @param node A VariableDeclaration node. + */ + function shouldEmitExplicitInitializerForLetDeclaration(node: VariableDeclaration) { + // Nested let bindings might need to be initialized explicitly to preserve + // ES6 semantic: + // + // { let x = 1; } + // { let x; } // x here should be undefined. not 1 + // + // Top level bindings never collide with anything and thus don't require + // explicit initialization. As for nested let bindings there are two cases: + // + // - Nested let bindings that were not renamed definitely should be + // initialized explicitly: + // + // { let x = 1; } + // { let x; if (some-condition) { x = 1}; if (x) { /*1*/ } } + // + // Without explicit initialization code in /*1*/ can be executed even if + // some-condition is evaluated to false. + // + // - Renaming introduces fresh name that should not collide with any + // existing names, however renamed bindings sometimes also should be + // explicitly initialized. One particular case: non-captured binding + // declared inside loop body (but not in loop initializer): + // + // let x; + // for (;;) { + // let x; + // } + // + // In downlevel codegen inner 'x' will be renamed so it won't collide + // with outer 'x' however it will should be reset on every iteration as + // if it was declared anew. + // + // * Why non-captured binding? + // - Because if loop contains block scoped binding captured in some + // function then loop body will be rewritten to have a fresh scope + // on every iteration so everything will just work. + // + // * Why loop initializer is excluded? + // - Since we've introduced a fresh name it already will be undefined. + + const original = getOriginalNode(node); + Debug.assert(isVariableDeclaration(original)); + + const flags = resolver.getNodeCheckFlags(original); + const isCapturedInFunction = flags & NodeCheckFlags.CapturedBlockScopedBinding; + const isDeclaredInLoop = flags & NodeCheckFlags.BlockScopedBindingInLoop; + const emittedAsTopLevel = + isBlockScopedContainerTopLevel(enclosingBlockScopeContainer) + || (isCapturedInFunction + && isDeclaredInLoop + && isBlock(enclosingBlockScopeContainer) + && isIterationStatement(enclosingBlockScopeContainerParent, /*lookInLabeledStatements*/ false)); + + const emitExplicitInitializer = + !emittedAsTopLevel + && enclosingBlockScopeContainer.kind !== SyntaxKind.ForInStatement + && enclosingBlockScopeContainer.kind !== SyntaxKind.ForOfStatement + && (!resolver.isDeclarationWithCollidingName(original) + || (isDeclaredInLoop + && !isCapturedInFunction + && !isIterationStatement(enclosingBlockScopeContainer, /*lookInLabeledStatements*/ false))); + + return emitExplicitInitializer; + } + + /** + * Visits a VariableDeclaration in a `let` declaration list. + * + * @param node A VariableDeclaration node. + */ + function visitVariableDeclarationInLetDeclarationList(node: VariableDeclaration) { + // For binding pattern names that lack initializers there is no point to emit + // explicit initializer since downlevel codegen for destructuring will fail + // in the absence of initializer so all binding elements will say uninitialized + const name = node.name; + if (isBindingPattern(name)) { + return visitVariableDeclaration(node); + } + + if (!node.initializer && shouldEmitExplicitInitializerForLetDeclaration(node)) { + const clone = getMutableClone(node); + clone.initializer = createVoidZero(); + return clone; + } + + return visitEachChild(node, visitor, context); + } + + /** + * Visits a VariableDeclaration node with a binding pattern. + * + * @param node A VariableDeclaration node. + */ + function visitVariableDeclaration(node: VariableDeclaration): VisitResult { + // If we are here it is because the name contains a binding pattern. + Debug.assert(isBindingPattern(node.name)); + + return flattenVariableDestructuring(node, /*value*/ undefined, visitor); + } + + function visitLabeledStatement(node: LabeledStatement) { + // TODO: Convert loop body for block scoped bindings. + return visitEachChild(node, visitor, context); + } + + function visitDoStatement(node: DoStatement) { + // TODO: Convert loop body for block scoped bindings. + return visitEachChild(node, visitor, context); + } + + function visitWhileStatement(node: WhileStatement) { + // TODO: Convert loop body for block scoped bindings. + return visitEachChild(node, visitor, context); + } + + function visitForStatement(node: ForStatement) { + // TODO: Convert loop body for block scoped bindings. + return visitEachChild(node, visitor, context); + } + + function visitForInStatement(node: ForInStatement) { + // TODO: Convert loop body for block scoped bindings. + return visitEachChild(node, visitor, context); + } + + /** + * Visits a ForOfStatement and converts it into a compatible ForStatement. + * + * @param node A ForOfStatement. + */ + function visitForOfStatement(node: ForOfStatement): Statement { + // TODO: Convert loop body for block scoped bindings. + + // The following ES6 code: + // + // for (let v of expr) { } + // + // should be emitted as + // + // for (var _i = 0, _a = expr; _i < _a.length; _i++) { + // var v = _a[_i]; + // } + // + // where _a and _i are temps emitted to capture the RHS and the counter, + // respectively. + // When the left hand side is an expression instead of a let declaration, + // the "let v" is not emitted. + // When the left hand side is a let/const, the v is renamed if there is + // another v in scope. + // Note that all assignments to the LHS are emitted in the body, including + // all destructuring. + // Note also that because an extra statement is needed to assign to the LHS, + // for-of bodies are always emitted as blocks. + + const expression = visitNode(node.expression, visitor, isExpression); + const initializer = node.initializer; + const statements: Statement[] = []; + + // In the case where the user wrote an identifier as the RHS, like this: + // + // for (let v of arr) { } + // + // we don't want to emit a temporary variable for the RHS, just use it directly. + const counter = createLoopVariable(); + const rhsReference = expression.kind === SyntaxKind.Identifier + ? createUniqueName((expression).text) + : createTempVariable(); + + // Initialize LHS + // var v = _a[_i]; + if (isVariableDeclarationList(initializer)) { + const firstDeclaration = firstOrUndefined(initializer.declarations); + if (firstDeclaration && isBindingPattern(firstDeclaration.name)) { + // This works whether the declaration is a var, let, or const. + // It will use rhsIterationValue _a[_i] as the initializer. + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + flattenVariableDestructuring( + firstDeclaration, + createElementAccess(rhsReference, counter), + visitor + ) + ), + /*location*/ initializer + ) + ); + } + else { + // The following call does not include the initializer, so we have + // to emit it separately. + statements.push( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + firstDeclaration ? firstDeclaration.name : createTempVariable(), + createElementAccess(rhsReference, counter) + ) + ]), + /*location*/ initializer + ) + ); + } + } + else { + // Initializer is an expression. Emit the expression in the body, so that it's + // evaluated on every iteration. + const assignment = createAssignment(initializer, createElementAccess(rhsReference, counter)); + if (isDestructuringAssignment(assignment)) { + // This is a destructuring pattern, so we flatten the destructuring instead. + statements.push( + createStatement( + flattenDestructuringAssignment( + assignment, + /*needsValue*/ false, + hoistVariableDeclaration, + visitor + ) + ) + ); + } + else { + statements.push(createStatement(assignment, /*location*/ node.initializer)); + } + } + + const statement = visitNode(node.statement, visitor, isStatement); + if (isBlock(statement)) { + addRange(statements, statement.statements); + } + else { + statements.push(statement); + } + + return createFor( + createVariableDeclarationList( + [ + createVariableDeclaration(counter, createLiteral(0), /*location*/ node.expression), + createVariableDeclaration(rhsReference, expression, /*location*/ node.expression) + ], + /*location*/ node.expression + ), + createLessThan( + counter, + createPropertyAccess(rhsReference, "length"), + /*location*/ initializer + ), + createPostfixIncrement(counter, /*location*/ initializer), + createBlock( + statements + ), + /*location*/ node + ); + } + + /** + * Visits an ObjectLiteralExpression with computed propety names. + * + * @param node An ObjectLiteralExpression node. + */ + function visitObjectLiteralExpression(node: ObjectLiteralExpression): LeftHandSideExpression { + // We are here because a ComputedPropertyName was used somewhere in the expression. + const properties = node.properties; + const numProperties = properties.length; + + // Find the first computed property. + // Everything until that point can be emitted as part of the initial object literal. + let numInitialNonComputedProperties = numProperties; + for (let i = 0, n = properties.length; i < n; i++) { + if (properties[i].name.kind === SyntaxKind.ComputedPropertyName) { + numInitialNonComputedProperties = i; + break; + } + } + + Debug.assert(numInitialNonComputedProperties !== numProperties); + + // For computed properties, we need to create a unique handle to the object + // literal so we can modify it without risking internal assignments tainting the object. + const temp = createTempVariable(); + hoistVariableDeclaration(temp); + + // Write out the first non-computed properties, then emit the rest through indexing on the temp variable. + const expressions: Expression[] = []; + addNode(expressions, + createAssignment( + temp, + createObjectLiteral( + visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialNonComputedProperties), + /*location*/ undefined, + node.multiLine + ) + ), + node.multiLine + ); + + addObjectLiteralMembers(expressions, node, temp, numInitialNonComputedProperties); + + // We need to clone the temporary identifier so that we can write it on a + // new line + addNode(expressions, getMutableClone(temp), node.multiLine); + return createParen(inlineExpressions(expressions)); + } + + /** + * Adds the members of an object literal to an array of expressions. + * + * @param expressions An array of expressions. + * @param node An ObjectLiteralExpression node. + * @param receiver The receiver for members of the ObjectLiteralExpression. + * @param numInitialNonComputedProperties The number of initial properties without + * computed property names. + */ + function addObjectLiteralMembers(expressions: Expression[], node: ObjectLiteralExpression, receiver: Identifier, numInitialNonComputedProperties: number) { + const properties = node.properties; + const numProperties = properties.length; + for (let i = numInitialNonComputedProperties; i < numProperties; i++) { + const property = properties[i]; + switch (property.kind) { + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + const accessors = getAllAccessorDeclarations(node.properties, property); + if (property === accessors.firstAccessor) { + addNode(expressions, transformAccessorsToExpression(receiver, accessors), node.multiLine); + } + + break; + + case SyntaxKind.PropertyAssignment: + addNode(expressions, transformPropertyAssignmentToExpression(node, property, receiver), node.multiLine); + break; + + case SyntaxKind.ShorthandPropertyAssignment: + addNode(expressions, transformShorthandPropertyAssignmentToExpression(node, property, receiver), node.multiLine); + break; + + case SyntaxKind.MethodDeclaration: + addNode(expressions, transformObjectLiteralMethodDeclarationToExpression(node, property, receiver), node.multiLine); + break; + + default: + Debug.failBadSyntaxKind(node); + break; + } + } + } + + /** + * Transforms a PropertyAssignment node into an expression. + * + * @param node The ObjectLiteralExpression that contains the PropertyAssignment. + * @param property The PropertyAssignment node. + * @param receiver The receiver for the assignment. + */ + function transformPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: PropertyAssignment, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(property.name, visitor, isPropertyName) + ), + visitNode(property.initializer, visitor, isExpression), + /*location*/ property + ); + } + + /** + * Transforms a ShorthandPropertyAssignment node into an expression. + * + * @param node The ObjectLiteralExpression that contains the ShorthandPropertyAssignment. + * @param property The ShorthandPropertyAssignment node. + * @param receiver The receiver for the assignment. + */ + function transformShorthandPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: ShorthandPropertyAssignment, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(property.name, visitor, isPropertyName) + ), + getSynthesizedClone(property.name), + /*location*/ property + ); + } + + /** + * Transforms a MethodDeclaration of an ObjectLiteralExpression into an expression. + * + * @param node The ObjectLiteralExpression that contains the MethodDeclaration. + * @param method The MethodDeclaration node. + * @param receiver The receiver for the assignment. + */ + function transformObjectLiteralMethodDeclarationToExpression(node: ObjectLiteralExpression, method: MethodDeclaration, receiver: Expression) { + return createAssignment( + createMemberAccessForPropertyName( + receiver, + visitNode(method.name, visitor, isPropertyName) + ), + transformFunctionLikeToExpression(method, /*location*/ method, /*name*/ undefined), + /*location*/ method + ); + } + + /** + * Visits a MethodDeclaration of an ObjectLiteralExpression and transforms it into a + * PropertyAssignment. + * + * @param node A MethodDeclaration node. + */ + function visitMethodDeclaration(node: MethodDeclaration): ObjectLiteralElement { + // We should only get here for methods on an object literal with regular identifier names. + // Methods on classes are handled in visitClassDeclaration/visitClassExpression. + // Methods with computed property names are handled in visitObjectLiteralExpression. + Debug.assert(!isComputedPropertyName(node.name)); + return createPropertyAssignment( + node.name, + transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined), + /*location*/ node + ); + } + + /** + * Visits a ShorthandPropertyAssignment and transforms it into a PropertyAssignment. + * + * @param node A ShorthandPropertyAssignment node. + */ + function visitShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElement { + return createPropertyAssignment( + node.name, + getSynthesizedClone(node.name), + /*location*/ node + ); + } + + /** + * Visits an ArrayLiteralExpression that contains a spread element. + * + * @param node An ArrayLiteralExpression node. + */ + function visitArrayLiteralExpression(node: ArrayLiteralExpression): Expression { + // We are here because we contain a SpreadElementExpression. + return transformAndSpreadElements(node.elements, /*needsUniqueCopy*/ true, node.multiLine); + } + + /** + * Visits a CallExpression that contains either a spread element or `super`. + * + * @param node a CallExpression. + */ + function visitCallExpression(node: CallExpression): LeftHandSideExpression { + // We are here either because SuperKeyword was used somewhere in the expression, or + // because we contain a SpreadElementExpression. + + const { target, thisArg } = createCallBinding(node.expression); + if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { + // [source] + // f(...a, b) + // x.m(...a, b) + // super(...a, b) + // super.m(...a, b) // in static + // super.m(...a, b) // in instance + // + // [output] + // f.apply(void 0, a.concat([b])) + // (_a = x).m.apply(_a, a.concat([b])) + // _super.apply(this, a.concat([b])) + // _super.m.apply(this, a.concat([b])) + // _super.prototype.m.apply(this, a.concat([b])) + + return createFunctionApply( + visitNode(target, visitor, isExpression), + visitNode(thisArg, visitor, isExpression), + transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false) + ); + } + else { + // [source] + // super(a) + // super.m(a) // in static + // super.m(a) // in instance + // + // [output] + // _super.call(this, a) + // _super.m.call(this, a) + // _super.prototype.m.call(this, a) + + return createFunctionCall( + visitNode(target, visitor, isExpression), + visitNode(thisArg, visitor, isExpression), + visitNodes(node.arguments, visitor, isExpression), + /*location*/ node + ); + } + } + + /** + * Visits a NewExpression that contains a spread element. + * + * @param node A NewExpression node. + */ + function visitNewExpression(node: NewExpression): LeftHandSideExpression { + // We are here because we contain a SpreadElementExpression. + Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadElementExpression) !== 0); + + // [source] + // new C(...a) + // + // [output] + // new ((_a = C).bind.apply(_a, [void 0].concat(a)))() + + const { target, thisArg } = createCallBinding(createPropertyAccess(node.expression, "bind")); + return createNew( + createFunctionApply( + visitNode(target, visitor, isExpression), + thisArg, + transformAndSpreadElements(createNodeArray([createVoidZero(), ...node.arguments]), /*needsUniqueCopy*/ false, /*multiLine*/ false) + ), + [] + ); + } + + /** + * Transforms an array of Expression nodes that contains a SpreadElementExpression. + * + * @param elements The array of Expression nodes. + * @param needsUniqueCopy A value indicating whether to ensure that the result is a fresh array. + * @param multiLine A value indicating whether the result should be emitted on multiple lines. + */ + function transformAndSpreadElements(elements: NodeArray, needsUniqueCopy: boolean, multiLine: boolean): Expression { + // [source] + // [a, ...b, c] + // + // [output] + // [a].concat(b, [c]) + + // Map spans of spread expressions into their expressions and spans of other + // expressions into an array literal. + const segments = flatten( + spanMap(elements, isSpreadElementExpression, (chunk: Expression[], isSpread: boolean) => isSpread + ? map(chunk, visitExpressionOfSpreadElement) + : createArrayLiteral(visitNodes(createNodeArray(chunk), visitor, isExpression), /*location*/ undefined, multiLine))); + + if (segments.length === 1) { + return needsUniqueCopy && isSpreadElementExpression(elements[0]) + ? createArraySlice(segments[0]) + : segments[0]; + } + + // Rewrite using the pattern .concat(, , ...) + return createArrayConcat(segments.shift(), segments); + } + + /** + * Transforms the expression of a SpreadElementExpression node. + * + * @param node A SpreadElementExpression node. + */ + function visitExpressionOfSpreadElement(node: SpreadElementExpression) { + return visitNode(node.expression, visitor, isExpression); + } + + /** + * Visits a template literal. + * + * @param node A template literal. + */ + function visitTemplateLiteral(node: LiteralExpression): LeftHandSideExpression { + return createLiteral(node.text, /*location*/ node); + } + + /** + * Visits a TaggedTemplateExpression node. + * + * @param node A TaggedTemplateExpression node. + */ + function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { + // Visit the tag expression + const tag = visitNode(node.tag, visitor, isExpression); + + // Allocate storage for the template site object + const temp = createTempVariable(); + hoistVariableDeclaration(temp); + + // Build up the template arguments and the raw and cooked strings for the template. + const templateArguments: Expression[] = [temp]; + const cookedStrings: Expression[] = []; + const rawStrings: Expression[] = []; + const template = node.template; + if (isNoSubstitutionTemplateLiteral(template)) { + cookedStrings.push(createLiteral(template.text)); + rawStrings.push(getRawLiteral(template)); + } + else { + cookedStrings.push(createLiteral(template.head.text)); + rawStrings.push(getRawLiteral(template.head)); + for (const templateSpan of template.templateSpans) { + cookedStrings.push(createLiteral(templateSpan.literal.text)); + rawStrings.push(getRawLiteral(templateSpan.literal)); + templateArguments.push(visitNode(templateSpan.expression, visitor, isExpression)); + } + } + + return inlineExpressions([ + createAssignment(temp, createArrayLiteral(cookedStrings)), + createAssignment(createPropertyAccess(temp, "raw"), createArrayLiteral(rawStrings)), + createCall(tag, templateArguments) + ]); + } + + /** + * Creates an ES5 compatible literal from an ES6 template literal. + * + * @param node The ES6 template literal. + */ + function getRawLiteral(node: LiteralLikeNode) { + // Find original source text, since we need to emit the raw strings of the tagged template. + // The raw strings contain the (escaped) strings of what the user wrote. + // Examples: `\n` is converted to "\\n", a template string with a newline to "\n". + let text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node); + + // text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"), + // thus we need to remove those characters. + // First template piece starts with "`", others with "}" + // Last template piece ends with "`", others with "${" + const isLast = node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail; + text = text.substring(1, text.length - (isLast ? 1 : 2)); + + // Newline normalization: + // ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's + // and LineTerminatorSequences are normalized to for both TV and TRV. + text = text.replace(/\r\n?/g, "\n"); + text = escapeString(text); + return createLiteral(text, /*location*/ node); + } + + /** + * Visits a TemplateExpression node. + * + * @param node A TemplateExpression node. + */ + function visitTemplateExpression(node: TemplateExpression): Expression { + const expressions: Expression[] = []; + addTemplateHead(expressions, node); + addTemplateSpans(expressions, node); + + // createAdd will check if each expression binds less closely than binary '+'. + // If it does, it wraps the expression in parentheses. Otherwise, something like + // `abc${ 1 << 2 }` + // becomes + // "abc" + 1 << 2 + "" + // which is really + // ("abc" + 1) << (2 + "") + // rather than + // "abc" + (1 << 2) + "" + const expression = reduceLeft(expressions, createAdd); + if (nodeIsSynthesized(expression)) { + setTextRange(expression, node); + } + + return expression; + } + + /** + * Gets a value indicating whether we need to include the head of a TemplateExpression. + * + * @param node A TemplateExpression node. + */ + function shouldAddTemplateHead(node: TemplateExpression) { + // If this expression has an empty head literal and the first template span has a non-empty + // literal, then emitting the empty head literal is not necessary. + // `${ foo } and ${ bar }` + // can be emitted as + // foo + " and " + bar + // This is because it is only required that one of the first two operands in the emit + // output must be a string literal, so that the other operand and all following operands + // are forced into strings. + // + // If the first template span has an empty literal, then the head must still be emitted. + // `${ foo }${ bar }` + // must still be emitted as + // "" + foo + bar + + // There is always atleast one templateSpan in this code path, since + // NoSubstitutionTemplateLiterals are directly emitted via emitLiteral() + Debug.assert(node.templateSpans.length !== 0); + + return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0; + } + + /** + * Adds the head of a TemplateExpression to an array of expressions. + * + * @param expressions An array of expressions. + * @param node A TemplateExpression node. + */ + function addTemplateHead(expressions: Expression[], node: TemplateExpression): void { + if (!shouldAddTemplateHead(node)) { + return; + } + + expressions.push(createLiteral(node.head.text)); + } + + /** + * Visits and adds the template spans of a TemplateExpression to an array of expressions. + * + * @param expressions An array of expressions. + * @param node A TemplateExpression node. + */ + function addTemplateSpans(expressions: Expression[], node: TemplateExpression): void { + for (const span of node.templateSpans) { + expressions.push(visitNode(span.expression, visitor, isExpression)); + + // Only emit if the literal is non-empty. + // The binary '+' operator is left-associative, so the first string concatenation + // with the head will force the result up to this point to be a string. + // Emitting a '+ ""' has no semantic effect for middles and tails. + if (span.literal.text.length !== 0) { + expressions.push(createLiteral(span.literal.text)); + } + } + } + + /** + * Visits the `super` keyword + */ + function visitSuperKeyword(node: PrimaryExpression): LeftHandSideExpression { + return containingNonArrowFunction + && isClassElement(containingNonArrowFunction) + && !hasModifier(containingNonArrowFunction, ModifierFlags.Static) + ? createPropertyAccess(createIdentifier("_super"), "prototype") + : createIdentifier("_super"); + } + + function visitSourceFileNode(node: SourceFile): SourceFile { + const [prologue, remaining] = span(node.statements, isPrologueDirective); + const statements: Statement[] = []; + startLexicalEnvironment(); + addRange(statements, prologue); + addCaptureThisForNodeIfNeeded(statements, node); + addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement)); + addRange(statements, endLexicalEnvironment()); + const clone = getMutableClone(node); + clone.statements = createNodeArray(statements, /*location*/ node.statements); + return clone; + } + + /** + * Called by the printer just before a node is printed. + * + * @param node The node to be printed. + */ + function onEmitNode(node: Node, emit: (node: Node) => void) { + const savedUseCapturedThis = useCapturedThis; + + if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) { + // If we are tracking a captured `this`, push a bit that indicates whether the + // containing function is an arrow function. + useCapturedThis = (getNodeEmitFlags(node) & NodeEmitFlags.CapturesThis) !== 0; + } + + previousOnEmitNode(node, emit); + + useCapturedThis = savedUseCapturedThis; + } + + /** + * Enables a more costly code path for substitutions when we determine a source file + * contains block-scoped bindings (e.g. `let` or `const`). + */ + function enableSubstitutionsForBlockScopedBindings() { + if ((enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) === 0) { + enabledSubstitutions |= ES6SubstitutionFlags.BlockScopedBindings; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + } + } + + /** + * Enables a more costly code path for substitutions when we determine a source file + * contains a captured `this`. + */ + function enableSubstitutionsForCapturedThis() { + if ((enabledSubstitutions & ES6SubstitutionFlags.CapturedThis) === 0) { + enabledSubstitutions |= ES6SubstitutionFlags.CapturedThis; + context.enableExpressionSubstitution(SyntaxKind.ThisKeyword); + context.enableEmitNotification(SyntaxKind.Constructor); + context.enableEmitNotification(SyntaxKind.MethodDeclaration); + context.enableEmitNotification(SyntaxKind.GetAccessor); + context.enableEmitNotification(SyntaxKind.SetAccessor); + context.enableEmitNotification(SyntaxKind.ArrowFunction); + context.enableEmitNotification(SyntaxKind.FunctionExpression); + context.enableEmitNotification(SyntaxKind.FunctionDeclaration); + } + } + + /** + * Hooks substitutions for non-expression identifiers. + */ + function substituteIdentifier(node: Identifier) { + node = previousIdentifierSubstitution(node); + + // Only substitute the identifier if we have enabled substitutions for block-scoped + // bindings. + if (enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) { + const original = getOriginalNode(node); + if (isIdentifier(original) && !nodeIsSynthesized(original) && original.parent && isNameOfDeclarationWithCollidingName(original)) { + return getGeneratedNameForNode(original); + } + } + + return node; + } + + /** + * Determines whether a name is the name of a declaration with a colliding name. + * NOTE: This function expects to be called with an original source tree node. + * + * @param node An original source tree node. + */ + function isNameOfDeclarationWithCollidingName(node: Identifier) { + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.BindingElement: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.VariableDeclaration: + return (parent).name === node + && resolver.isDeclarationWithCollidingName(parent); + } + + return false; + } + + /** + * Substitutes an expression. + * + * @param node An Expression node. + */ + function substituteExpression(node: Expression): Expression { + node = previousExpressionSubstitution(node); + switch (node.kind) { + case SyntaxKind.Identifier: + return substituteExpressionIdentifier(node); + + case SyntaxKind.ThisKeyword: + return substituteThisKeyword(node); + } + return node; } + + /** + * Substitutes an expression identifier. + * + * @param node An Identifier node. + */ + function substituteExpressionIdentifier(node: Identifier): Identifier { + if (enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) { + const original = getOriginalNode(node); + if (isIdentifier(original)) { + const declaration = resolver.getReferencedDeclarationWithCollidingName(original); + if (declaration) { + return getGeneratedNameForNode(declaration.name); + } + } + } + + return node; + } + + /** + * Substitutes `this` when contained within an arrow function. + * + * @param node The ThisKeyword node. + */ + function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression { + if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && useCapturedThis) { + return createIdentifier("_this", /*location*/ node); + } + + return node; + } + + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { + return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); + } + + function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { + const expression = getDeclarationName(node); + return hasModifier(member, ModifierFlags.Static) ? expression : createPropertyAccess(expression, "prototype"); + } + + function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration, hasExtendsClause: boolean) { + if (!constructor || !hasExtendsClause) { + return false; + } + + const parameter = singleOrUndefined(constructor.parameters); + if (!parameter || !nodeIsSynthesized(parameter) || !parameter.dotDotDotToken) { + return false; + } + + const statement = firstOrUndefined(constructor.body.statements); + if (!statement || !nodeIsSynthesized(statement) || statement.kind !== SyntaxKind.ExpressionStatement) { + return false; + } + + const statementExpression = (statement).expression; + if (!nodeIsSynthesized(statementExpression) || statementExpression.kind !== SyntaxKind.CallExpression) { + return false; + } + + const callTarget = (statementExpression).expression; + if (!nodeIsSynthesized(callTarget) || callTarget.kind !== SyntaxKind.SuperKeyword) { + return false; + } + + const callArgument = singleOrUndefined((statementExpression).arguments); + if (!callArgument || !nodeIsSynthesized(callArgument) || callArgument.kind !== SyntaxKind.SpreadElementExpression) { + return false; + } + + const expression = (callArgument).expression; + return isIdentifier(expression) && expression === parameter.name; + } } } \ No newline at end of file diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index c207940508121..d04ca55db4b31 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -3,15 +3,16 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): ES7->ES6 transformer export function transformES7(context: TransformationContext) { + const { hoistVariableDeclaration } = context; + return transformSourceFile; function transformSourceFile(node: SourceFile) { return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES7) { return visitorWorker(node); } @@ -23,8 +24,77 @@ namespace ts { } } - function visitorWorker(node: Node) { - return node; + function visitorWorker(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.BinaryExpression: + return visitBinaryExpression(node); + + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } + } + + function visitBinaryExpression(node: BinaryExpression): Expression { + // We are here because ES7 adds support for the exponentiation operator. + const left = visitNode(node.left, visitor, isExpression); + const right = visitNode(node.right, visitor, isExpression); + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { + let target: Expression; + let value: Expression; + if (isElementAccessExpression(left)) { + // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` + const expressionTemp = createTempVariable(); + hoistVariableDeclaration(expressionTemp); + + const argumentExpressionTemp = createTempVariable(); + hoistVariableDeclaration(argumentExpressionTemp); + + target = createElementAccess( + createAssignment(expressionTemp, left.expression, /*location*/ left.expression), + createAssignment(argumentExpressionTemp, left.argumentExpression, /*location*/ left.argumentExpression), + /*location*/ left + ); + + value = createElementAccess( + expressionTemp, + argumentExpressionTemp, + /*location*/ left + ); + } + else if (isPropertyAccessExpression(left)) { + // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` + const expressionTemp = createTempVariable(); + hoistVariableDeclaration(expressionTemp); + + target = createPropertyAccess( + createAssignment(expressionTemp, left.expression, /*location*/ left.expression), + left.name, + /*location*/ left + ); + + value = createPropertyAccess( + expressionTemp, + left.name, + /*location*/ left + ); + } + else { + // Transforms `a **= b` into `a = Math.pow(a, b)` + target = left; + value = left; + } + + return createAssignment(target, createMathPow(value, right, /*location*/ node), /*location*/ node); + } + else if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken) { + // Transforms `a ** b` into `Math.pow(a, b)` + return createMathPow(left, right, /*location*/ node); + } + else { + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } } } } \ No newline at end of file diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index e9470b32eea8f..dc423e8da7b15 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -3,15 +3,22 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): JSX->React transformer + const entities: Map = createEntitiesMap(); + export function transformJsx(context: TransformationContext) { + const compilerOptions = context.getCompilerOptions(); return transformSourceFile; + /** + * Transform JSX-specific syntax in a SourceFile. + * + * @param node A SourceFile node. + */ function transformSourceFile(node: SourceFile) { return visitEachChild(node, visitor, context); } - function visitor(node: Node): Node { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.Jsx) { return visitorWorker(node); } @@ -23,8 +30,461 @@ namespace ts { } } - function visitorWorker(node: Node): Node { - return node; + function visitorWorker(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.JsxElement: + return visitJsxElement(node); + + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(node); + + default: + Debug.failBadSyntaxKind(node); + return undefined; + } + } + + function transformJsxChildToExpression(node: JsxChild): Expression { + switch (node.kind) { + case SyntaxKind.JsxText: + return visitJsxText(node); + + case SyntaxKind.JsxExpression: + return visitJsxExpression(node); + + case SyntaxKind.JsxElement: + return visitJsxElement(node); + + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(node); + + default: + Debug.failBadSyntaxKind(node); + return undefined; + } + } + + function visitJsxElement(node: JsxElement) { + return visitJsxOpeningLikeElement(node.openingElement, node.children); + } + + function visitJsxSelfClosingElement(node: JsxSelfClosingElement) { + return visitJsxOpeningLikeElement(node, /*children*/ undefined); } + + function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[]) { + const tagName = getTagName(node); + let objectProperties: Expression; + const attrs = node.attributes; + if (attrs.length === 0) { + // When there are no attributes, React wants "null" + objectProperties = createNull(); + } + else { + // Map spans of JsxAttribute nodes into object literals and spans + // of JsxSpreadAttribute nodes into expressions. + const segments = flatten( + spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread + ? map(attrs, transformJsxSpreadAttributeToExpression) + : createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement)) + ) + ); + + if (isJsxSpreadAttribute(attrs[0])) { + // We must always emit at least one object literal before a spread + // argument. + segments.unshift(createObjectLiteral()); + } + + // Either emit one big object literal (no spread attribs), or + // a call to React.__spread + objectProperties = singleOrUndefined(segments) + || createJsxSpread(compilerOptions.reactNamespace, segments); + } + + return createJsxCreateElement( + compilerOptions.reactNamespace, + tagName, + objectProperties, + filter(map(children, transformJsxChildToExpression), isDefined) + ); + } + + function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) { + return visitNode(node.expression, visitor, isExpression); + } + + function transformJsxAttributeToObjectLiteralElement(node: JsxAttribute) { + const name = getAttributeName(node); + const expression = node.initializer + ? visitNode(node.initializer, visitor, isExpression) + : createLiteral(true); + return createPropertyAssignment(name, expression); + } + + function visitJsxText(node: JsxText) { + const text = getTextOfNode(node, /*includeTrivia*/ true); + let parts: Expression[]; + let firstNonWhitespace = 0; + let lastNonWhitespace = -1; + + // JSX trims whitespace at the end and beginning of lines, except that the + // start/end of a tag is considered a start/end of a line only if that line is + // on the same line as the closing tag. See examples in + // tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx + for (let i = 0; i < text.length; i++) { + const c = text.charCodeAt(i); + if (isLineBreak(c)) { + if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) { + const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); + if (!parts) { + parts = []; + } + + // We do not escape the string here as that is handled by the printer + // when it emits the literal. We do, however, need to decode JSX entities. + parts.push(createLiteral(decodeEntities(part))); + } + + firstNonWhitespace = -1; + } + else if (!isWhiteSpace(c)) { + lastNonWhitespace = i; + if (firstNonWhitespace === -1) { + firstNonWhitespace = i; + } + } + } + + if (firstNonWhitespace !== -1) { + const part = text.substr(firstNonWhitespace); + if (!parts) { + parts = []; + } + + // We do not escape the string here as that is handled by the printer + // when it emits the literal. We do, however, need to decode JSX entities. + parts.push(createLiteral(decodeEntities(part))); + } + + if (parts) { + return reduceLeft(parts, aggregateJsxTextParts); + } + + return undefined; + } + + /** + * Aggregates two expressions by interpolating them with a whitespace literal. + */ + function aggregateJsxTextParts(left: Expression, right: Expression) { + return createAdd(createAdd(left, createLiteral(" ")), right); + } + + /** + * Decodes JSX entities. + */ + function decodeEntities(text: string) { + return text.replace(/&(\w+);/g, function(s: any, m: string) { + if (entities[m] !== undefined) { + return String.fromCharCode(entities[m]); + } + else { + return s; + } + }); + } + + function getTagName(node: JsxElement | JsxOpeningLikeElement): Expression { + if (node.kind === SyntaxKind.JsxElement) { + return getTagName((node).openingElement); + } + else { + const name = (node).tagName; + if (isIdentifier(name) && isIntrinsicJsxName(name.text)) { + return createLiteral(name.text); + } + else { + return createExpressionFromEntityName(name); + } + } + } + + /** + * Emit an attribute name, which is quoted if it needs to be quoted. Because + * these emit into an object literal property name, we don't need to be worried + * about keywords, just non-identifier characters + */ + function getAttributeName(node: JsxAttribute): StringLiteral | Identifier { + const name = node.name; + if (/^[A-Za-z_]\w*$/.test(name.text)) { + return createLiteral(name.text); + } + else { + return name; + } + } + + function visitJsxExpression(node: JsxExpression) { + return visitNode(node.expression, visitor, isExpression); + } + } + + function createEntitiesMap(): Map { + return { + "quot": 0x0022, + "amp": 0x0026, + "apos": 0x0027, + "lt": 0x003C, + "gt": 0x003E, + "nbsp": 0x00A0, + "iexcl": 0x00A1, + "cent": 0x00A2, + "pound": 0x00A3, + "curren": 0x00A4, + "yen": 0x00A5, + "brvbar": 0x00A6, + "sect": 0x00A7, + "uml": 0x00A8, + "copy": 0x00A9, + "ordf": 0x00AA, + "laquo": 0x00AB, + "not": 0x00AC, + "shy": 0x00AD, + "reg": 0x00AE, + "macr": 0x00AF, + "deg": 0x00B0, + "plusmn": 0x00B1, + "sup2": 0x00B2, + "sup3": 0x00B3, + "acute": 0x00B4, + "micro": 0x00B5, + "para": 0x00B6, + "middot": 0x00B7, + "cedil": 0x00B8, + "sup1": 0x00B9, + "ordm": 0x00BA, + "raquo": 0x00BB, + "frac14": 0x00BC, + "frac12": 0x00BD, + "frac34": 0x00BE, + "iquest": 0x00BF, + "Agrave": 0x00C0, + "Aacute": 0x00C1, + "Acirc": 0x00C2, + "Atilde": 0x00C3, + "Auml": 0x00C4, + "Aring": 0x00C5, + "AElig": 0x00C6, + "Ccedil": 0x00C7, + "Egrave": 0x00C8, + "Eacute": 0x00C9, + "Ecirc": 0x00CA, + "Euml": 0x00CB, + "Igrave": 0x00CC, + "Iacute": 0x00CD, + "Icirc": 0x00CE, + "Iuml": 0x00CF, + "ETH": 0x00D0, + "Ntilde": 0x00D1, + "Ograve": 0x00D2, + "Oacute": 0x00D3, + "Ocirc": 0x00D4, + "Otilde": 0x00D5, + "Ouml": 0x00D6, + "times": 0x00D7, + "Oslash": 0x00D8, + "Ugrave": 0x00D9, + "Uacute": 0x00DA, + "Ucirc": 0x00DB, + "Uuml": 0x00DC, + "Yacute": 0x00DD, + "THORN": 0x00DE, + "szlig": 0x00DF, + "agrave": 0x00E0, + "aacute": 0x00E1, + "acirc": 0x00E2, + "atilde": 0x00E3, + "auml": 0x00E4, + "aring": 0x00E5, + "aelig": 0x00E6, + "ccedil": 0x00E7, + "egrave": 0x00E8, + "eacute": 0x00E9, + "ecirc": 0x00EA, + "euml": 0x00EB, + "igrave": 0x00EC, + "iacute": 0x00ED, + "icirc": 0x00EE, + "iuml": 0x00EF, + "eth": 0x00F0, + "ntilde": 0x00F1, + "ograve": 0x00F2, + "oacute": 0x00F3, + "ocirc": 0x00F4, + "otilde": 0x00F5, + "ouml": 0x00F6, + "divide": 0x00F7, + "oslash": 0x00F8, + "ugrave": 0x00F9, + "uacute": 0x00FA, + "ucirc": 0x00FB, + "uuml": 0x00FC, + "yacute": 0x00FD, + "thorn": 0x00FE, + "yuml": 0x00FF, + "OElig": 0x0152, + "oelig": 0x0153, + "Scaron": 0x0160, + "scaron": 0x0161, + "Yuml": 0x0178, + "fnof": 0x0192, + "circ": 0x02C6, + "tilde": 0x02DC, + "Alpha": 0x0391, + "Beta": 0x0392, + "Gamma": 0x0393, + "Delta": 0x0394, + "Epsilon": 0x0395, + "Zeta": 0x0396, + "Eta": 0x0397, + "Theta": 0x0398, + "Iota": 0x0399, + "Kappa": 0x039A, + "Lambda": 0x039B, + "Mu": 0x039C, + "Nu": 0x039D, + "Xi": 0x039E, + "Omicron": 0x039F, + "Pi": 0x03A0, + "Rho": 0x03A1, + "Sigma": 0x03A3, + "Tau": 0x03A4, + "Upsilon": 0x03A5, + "Phi": 0x03A6, + "Chi": 0x03A7, + "Psi": 0x03A8, + "Omega": 0x03A9, + "alpha": 0x03B1, + "beta": 0x03B2, + "gamma": 0x03B3, + "delta": 0x03B4, + "epsilon": 0x03B5, + "zeta": 0x03B6, + "eta": 0x03B7, + "theta": 0x03B8, + "iota": 0x03B9, + "kappa": 0x03BA, + "lambda": 0x03BB, + "mu": 0x03BC, + "nu": 0x03BD, + "xi": 0x03BE, + "omicron": 0x03BF, + "pi": 0x03C0, + "rho": 0x03C1, + "sigmaf": 0x03C2, + "sigma": 0x03C3, + "tau": 0x03C4, + "upsilon": 0x03C5, + "phi": 0x03C6, + "chi": 0x03C7, + "psi": 0x03C8, + "omega": 0x03C9, + "thetasym": 0x03D1, + "upsih": 0x03D2, + "piv": 0x03D6, + "ensp": 0x2002, + "emsp": 0x2003, + "thinsp": 0x2009, + "zwnj": 0x200C, + "zwj": 0x200D, + "lrm": 0x200E, + "rlm": 0x200F, + "ndash": 0x2013, + "mdash": 0x2014, + "lsquo": 0x2018, + "rsquo": 0x2019, + "sbquo": 0x201A, + "ldquo": 0x201C, + "rdquo": 0x201D, + "bdquo": 0x201E, + "dagger": 0x2020, + "Dagger": 0x2021, + "bull": 0x2022, + "hellip": 0x2026, + "permil": 0x2030, + "prime": 0x2032, + "Prime": 0x2033, + "lsaquo": 0x2039, + "rsaquo": 0x203A, + "oline": 0x203E, + "frasl": 0x2044, + "euro": 0x20AC, + "image": 0x2111, + "weierp": 0x2118, + "real": 0x211C, + "trade": 0x2122, + "alefsym": 0x2135, + "larr": 0x2190, + "uarr": 0x2191, + "rarr": 0x2192, + "darr": 0x2193, + "harr": 0x2194, + "crarr": 0x21B5, + "lArr": 0x21D0, + "uArr": 0x21D1, + "rArr": 0x21D2, + "dArr": 0x21D3, + "hArr": 0x21D4, + "forall": 0x2200, + "part": 0x2202, + "exist": 0x2203, + "empty": 0x2205, + "nabla": 0x2207, + "isin": 0x2208, + "notin": 0x2209, + "ni": 0x220B, + "prod": 0x220F, + "sum": 0x2211, + "minus": 0x2212, + "lowast": 0x2217, + "radic": 0x221A, + "prop": 0x221D, + "infin": 0x221E, + "ang": 0x2220, + "and": 0x2227, + "or": 0x2228, + "cap": 0x2229, + "cup": 0x222A, + "int": 0x222B, + "there4": 0x2234, + "sim": 0x223C, + "cong": 0x2245, + "asymp": 0x2248, + "ne": 0x2260, + "equiv": 0x2261, + "le": 0x2264, + "ge": 0x2265, + "sub": 0x2282, + "sup": 0x2283, + "nsub": 0x2284, + "sube": 0x2286, + "supe": 0x2287, + "oplus": 0x2295, + "otimes": 0x2297, + "perp": 0x22A5, + "sdot": 0x22C5, + "lceil": 0x2308, + "rceil": 0x2309, + "lfloor": 0x230A, + "rfloor": 0x230B, + "lang": 0x2329, + "rang": 0x232A, + "loz": 0x25CA, + "spades": 0x2660, + "clubs": 0x2663, + "hearts": 0x2665, + "diams": 0x2666 + }; } } \ No newline at end of file diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts index b49d0a5c83b02..63b147b6e602d 100644 --- a/src/compiler/transformers/module/es6.ts +++ b/src/compiler/transformers/module/es6.ts @@ -7,10 +7,6 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); - } - - function visitor(node: Node): Node { return node; } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 61c4a2cd8807c..d6a89c4ed77c5 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -3,16 +3,757 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): CommonJS/AMD/UMD transformer export function transformModule(context: TransformationContext) { + const transformModuleDelegates: Map<(node: SourceFile) => SourceFile> = { + [ModuleKind.None]: transformCommonJSModule, + [ModuleKind.CommonJS]: transformCommonJSModule, + [ModuleKind.AMD]: transformAMDModule, + [ModuleKind.UMD]: transformUMDModule, + }; + + const { + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + setNodeEmitFlags + } = context; + + const compilerOptions = context.getCompilerOptions(); + const resolver = context.getEmitResolver(); + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); + const previousExpressionSubstitution = context.expressionSubstitution; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + context.expressionSubstitution = substituteExpression; + + let currentSourceFile: SourceFile; + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + let exportSpecifiers: Map; + let exportEquals: ExportAssignment; + let hasExportStars: boolean; + return transformSourceFile; + /** + * Transforms the module aspects of a SourceFile. + * + * @param node The SourceFile node. + */ function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); + if (isExternalModule(node) || compilerOptions.isolatedModules) { + currentSourceFile = node; + + // Collect information about the external module. + ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); + + // Perform the transformation. + const updated = transformModuleDelegates[moduleKind](node); + + currentSourceFile = undefined; + externalImports = undefined; + exportSpecifiers = undefined; + exportEquals = undefined; + hasExportStars = false; + return updated; + } + + return node; + } + + /** + * Transforms a SourceFile into a CommonJS module. + * + * @param node The SourceFile node. + */ + function transformCommonJSModule(node: SourceFile) { + startLexicalEnvironment(); + return setNodeEmitFlags( + updateSourceFile( + node, + [ + ...visitNodes(node.statements, visitor, isStatement), + ...(endLexicalEnvironment() || []), + tryCreateExportEquals(/*emitAsReturn*/ false) + ] + ), + hasExportStars ? NodeEmitFlags.EmitExportStar : 0 + ); + } + + /** + * Transforms a SourceFile into an AMD module. + * + * @param node The SourceFile node. + */ + function transformAMDModule(node: SourceFile) { + const define = createIdentifier("define"); + const moduleName = node.moduleName ? createLiteral(node.moduleName) : undefined; + return transformAsynchronousModule(node, define, moduleName, /*includeNonAmdDependencies*/ true); + } + + /** + * Transforms a SourceFile into a UMD module. + * + * @param node The SourceFile node. + */ + function transformUMDModule(node: SourceFile) { + const define = createIdentifier("define"); + setNodeEmitFlags(define, NodeEmitFlags.UMDDefine); + return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); + } + + /** + * Transforms a SourceFile into an AMD or UMD module. + * + * @param node The SourceFile node. + * @param define The expression used to define the module. + * @param moduleName An expression for the module name, if available. + * @param includeNonAmdDependencies A value indicating whether to incldue any non-AMD dependencies. + */ + function transformAsynchronousModule(node: SourceFile, define: Expression, moduleName: Expression, includeNonAmdDependencies: boolean) { + // Start the lexical environment for the module body. + startLexicalEnvironment(); + + const { importModuleNames, importAliasNames } = collectAsynchronousDependencies(node, includeNonAmdDependencies); + + // Create an updated SourceFile: + // + // define(moduleName?, ["module1", "module2"], function ... + return updateSourceFile(node, [ + createStatement( + createCall( + define, + flatten([ + // Add the module name (if provided). + moduleName, + + // Add the dependency array argument: + // + // ["module1", "module2", ...] + createArrayLiteral(importModuleNames), + + // Add the module body function argument: + // + // function (module1, module2) ... + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + importAliasNames, + setNodeEmitFlags( + setMultiLine( + createBlock( + flatten([ + // Visit each statement of the module body. + ...visitNodes(node.statements, visitor, isStatement), + + // End the lexical environment for the module body + // and merge any new lexical declarations. + ...(endLexicalEnvironment() || []), + + // Append the 'export =' statement if provided. + tryCreateExportEquals(/*emitAsReturn*/ true) + ]) + ), + /*multiLine*/ true + ), + + // If we have any `export * from ...` declarations + // we need to inform the emitter to add the __export helper. + hasExportStars ? NodeEmitFlags.EmitExportStar : 0 + ) + ) + ]) + ) + ) + ]); + } + + function tryCreateExportEquals(emitAsReturn: boolean) { + if (exportEquals && resolver.isValueAliasDeclaration(exportEquals)) { + if (emitAsReturn) { + return createReturn(exportEquals.expression); + } + else { + return createStatement( + createAssignment( + createPropertyAccess( + createIdentifier("module"), + "exports" + ), + exportEquals.expression + ) + ); + } + } + return undefined; + } + + /** + * Visits a node at the top level of the source file. + * + * @param node The node. + */ + function visitor(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node); + + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(node); + + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(node); + + case SyntaxKind.ExportAssignment: + return visitExportAssignment(node); + + case SyntaxKind.VariableStatement: + return visitVariableStatement(node); + + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node); + + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node); + + default: + // This visitor does not descend into the tree, as export/import statements + // are only transformed at the top level of a file. + return node; + } + } + + /** + * Visits an ImportDeclaration node. + * + * @param node The ImportDeclaration node. + */ + function visitImportDeclaration(node: ImportDeclaration): VisitResult { + if (!contains(externalImports, node)) { + return undefined; + } + + const statements: Statement[] = []; + const namespaceDeclaration = getNamespaceDeclarationNode(node); + if (moduleKind !== ModuleKind.AMD) { + if (!node.importClause) { + // import "mod"; + addNode(statements, + createStatement( + createRequireCall(node), + /*location*/ node + ) + ); + } + else { + const variables: VariableDeclaration[] = []; + if (namespaceDeclaration && !isDefaultImport(node)) { + // import * as n from "mod"; + addNode(variables, + createVariableDeclaration( + getSynthesizedClone(namespaceDeclaration.name), + createRequireCall(node) + ) + ); + } + else { + // import d from "mod"; + // import { x, y } from "mod"; + // import d, { x, y } from "mod"; + // import d, * as n from "mod"; + addNode(variables, + createVariableDeclaration( + getGeneratedNameForNode(node), + createRequireCall(node) + ) + ); + + if (namespaceDeclaration && isDefaultImport(node)) { + addNode(variables, + createVariableDeclaration( + getSynthesizedClone(namespaceDeclaration.name), + getGeneratedNameForNode(node) + ) + ); + } + } + + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList(variables), + /*location*/ node + ) + ); + } + } + else if (namespaceDeclaration && isDefaultImport(node)) { + // import d, * as n from "mod"; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getSynthesizedClone(namespaceDeclaration.name), + getGeneratedNameForNode(node), + /*location*/ node + ) + ]) + ) + ); + } + + addExportImportAssignments(statements, node); + return statements; + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { + if (!contains(externalImports, node)) { + return undefined; + } + + const statements: Statement[] = []; + if (moduleKind !== ModuleKind.AMD) { + if (hasModifier(node, ModifierFlags.Export)) { + addNode(statements, + createStatement( + createExportAssignment( + node.name, + createRequireCall(node) + ), + /*location*/ node + ) + ); + } + else { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getSynthesizedClone(node.name), + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); + } + } + else { + if (hasModifier(node, ModifierFlags.Export)) { + addNode(statements, + createStatement( + createExportAssignment(node.name, node.name), + /*location*/ node + ) + ); + } + } + + addExportImportAssignments(statements, node); + return statements; + } + + function visitExportDeclaration(node: ExportDeclaration): VisitResult { + if (!contains(externalImports, node)) { + return undefined; + } + + const generatedName = getGeneratedNameForNode(node); + if (node.exportClause) { + const statements: Statement[] = []; + // export { x, y } from "mod"; + if (moduleKind !== ModuleKind.AMD) { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + generatedName, + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); + } + for (const specifier of node.exportClause.elements) { + if (resolver.isValueAliasDeclaration(specifier)) { + const exportedValue = createPropertyAccess( + generatedName, + specifier.propertyName || specifier.name + ); + addNode(statements, + createStatement( + createExportAssignment(specifier.name, exportedValue), + /*location*/ specifier + ) + ); + } + } + + return statements; + } + else { + // export * from "mod"; + return createStatement( + createCall( + createIdentifier("__export"), + [ + moduleKind !== ModuleKind.AMD + ? createRequireCall(node) + : generatedName + ] + ), + /*location*/ node + ); + } + } + + function visitExportAssignment(node: ExportAssignment): VisitResult { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + const statements: Statement[] = []; + addExportDefault(statements, node.expression, /*location*/ node); + return statements; + } + + return undefined; } - function visitor(node: Node): Node { + function addExportDefault(statements: Statement[], expression: Expression, location: TextRange): void { + addNode(statements, tryCreateExportDefaultCompat()); + addNode(statements, + createStatement( + createExportAssignment( + createIdentifier("default"), + expression + ), + location + ) + ); + } + + function tryCreateExportDefaultCompat(): Statement { + const original = getOriginalNode(currentSourceFile); + Debug.assert(original.kind === SyntaxKind.SourceFile); + + if (!(original).symbol.exports["___esModule"]) { + if (languageVersion === ScriptTarget.ES3) { + return createStatement( + createExportAssignment( + createIdentifier("__esModule"), + createLiteral(true) + ) + ); + } + else { + return createStatement( + createObjectDefineProperty( + createIdentifier("exports"), + createLiteral("_esModule"), + { value: createLiteral(true) } + ) + ); + } + } + } + + function addExportImportAssignments(statements: Statement[], node: Node) { + const names = reduceEachChild(node, collectExportMembers, []); + for (const name of names) { + addExportMemberAssignments(statements, name); + } + } + + function collectExportMembers(names: Identifier[], node: Node): Identifier[] { + if (isAliasSymbolDeclaration(node) && resolver.isValueAliasDeclaration(node) && isDeclaration(node)) { + const name = node.name; + if (isIdentifier(name)) { + names.push(name); + } + } + + return reduceEachChild(node, collectExportMembers, names); + } + + function addExportMemberAssignments(statements: Statement[], name: Identifier): void { + if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + for (const specifier of exportSpecifiers[name.text]) { + addNode(statements, + createStatement( + createExportAssignment(specifier.name, name), + /*location*/ specifier.name + ) + ); + } + } + } + + function visitVariableStatement(node: VariableStatement): VisitResult { + const variables = getInitializedVariables(node.declarationList); + if (variables.length === 0) { + // elide statement if there are no initialized variables + return undefined; + } + + return createStatement( + inlineExpressions( + map(variables, transformInitializedVariable) + ) + ); + } + + function transformInitializedVariable(node: VariableDeclaration): Expression { + const name = node.name; + if (isBindingPattern(name)) { + return flattenVariableDestructuringToExpression( + node, + hoistVariableDeclaration, + getModuleMemberName, + visitor + ); + } + else { + return createAssignment( + getModuleMemberName(name), + visitNode(node.initializer, visitor, isExpression) + ); + } + } + + function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { + const statements: Statement[] = []; + if (node.name) { + if (hasModifier(node, ModifierFlags.Export)) { + addNode(statements, + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + node.name, + node.parameters, + node.body, + /*location*/ node + ) + ); + + if (hasModifier(node, ModifierFlags.Default)) { + addExportDefault(statements, getSynthesizedClone(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert(hasModifier(node, ModifierFlags.Default)); + addExportDefault(statements, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + node.parameters, + node.body, + /*location*/ node + ), + /*location*/ node + ); + } + return statements; + } + + function visitClassDeclaration(node: ClassDeclaration): VisitResult { + const statements: Statement[] = []; + if (node.name) { + if (hasModifier(node, ModifierFlags.Export)) { + addNode(statements, + createClassDeclaration( + /*modifiers*/ undefined, + node.name, + node.heritageClauses, + node.members, + /*location*/ node + ) + ); + + if (hasModifier(node, ModifierFlags.Default)) { + addExportDefault(statements, getSynthesizedClone(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert(hasModifier(node, ModifierFlags.Default)); + addExportDefault(statements, + createClassExpression( + /*name*/ undefined, + node.heritageClauses, + node.members, + /*location*/ node + ), + /*location*/ node + ); + } + + return statements; + } + + function substituteExpression(node: Expression) { + node = previousExpressionSubstitution(node); + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node); + } + return node; } + + function substituteExpressionIdentifier(node: Identifier): Expression { + const container = resolver.getReferencedExportContainer(node); + if (container && container.kind === SyntaxKind.SourceFile) { + return createPropertyAccess( + createIdentifier("exports"), + getSynthesizedClone(node), + /*location*/ node + ); + } + + return node; + } + + function getModuleMemberName(name: Identifier) { + return createPropertyAccess( + createIdentifier("exports"), + name, + /*location*/ name + ); + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + const moduleName = getExternalModuleName(importNode); + if (moduleName.kind === SyntaxKind.StringLiteral) { + return tryRenameExternalModule(moduleName) + || getSynthesizedClone(moduleName); + } + + return undefined; + } + + /** + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. + */ + function tryRenameExternalModule(moduleName: LiteralExpression) { + if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { + return createLiteral(currentSourceFile.renamedDependencies[moduleName.text]); + } + return undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + const namespaceDeclaration = getNamespaceDeclarationNode(node); + if (namespaceDeclaration && !isDefaultImport(node)) { + return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); + } + if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) { + return getGeneratedNameForNode(node); + } + if (node.kind === SyntaxKind.ExportDeclaration && (node).moduleSpecifier) { + return getGeneratedNameForNode(node); + } + } + + function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + const moduleName = getExternalModuleNameLiteral(importNode); + Debug.assert(isDefined(moduleName)); + return createCall( + createIdentifier("require"), + [moduleName] + ); + } + + function createExportAssignment(name: Identifier, value: Expression) { + return createAssignment( + name.originalKeywordKind && languageVersion === ScriptTarget.ES3 + ? createElementAccess( + createIdentifier("exports"), + createLiteral(name.text) + ) + : createPropertyAccess( + createIdentifier("exports"), + getSynthesizedClone(name) + ), + value + ); + } + + function collectAsynchronousDependencies(node: SourceFile, includeNonAmdDependencies: boolean) { + // An AMD define function has the following shape: + // + // define(id?, dependencies?, factory); + // + // This has the shape of the following: + // + // define(name, ["module1", "module2"], function (module1Alias) { ... } + // + // The location of the alias in the parameter list in the factory function needs to + // match the position of the module name in the dependency list. + // + // To ensure this is true in cases of modules with no aliases, e.g.: + // + // import "module" + // + // or + // + // /// + // + // we need to add modules without alias names to the end of the dependencies list + + const unaliasedModuleNames: Expression[] = []; + const aliasedModuleNames: Expression[] = [createLiteral("require"), createLiteral("exports") ]; + const importAliasNames = [createParameter("require"), createParameter("exports")]; + + for (const amdDependency of node.amdDependencies) { + if (amdDependency.name) { + aliasedModuleNames.push(createLiteral(amdDependency.name)); + importAliasNames.push(createParameter(createIdentifier(amdDependency.name))); + } + else { + unaliasedModuleNames.push(createLiteral(amdDependency.path)); + } + } + + for (const importNode of externalImports) { + // Find the name of the external module + const externalModuleName = getExternalModuleNameLiteral(importNode); + // Find the name of the module alias, if there is one + const importAliasName = getLocalNameForExternalImport(importNode); + if (includeNonAmdDependencies && importAliasName) { + aliasedModuleNames.push(externalModuleName); + importAliasNames.push(createParameter(importAliasName)); + } + else { + unaliasedModuleNames.push(externalModuleName); + } + } + + return { + importModuleNames: [ + ...unaliasedModuleNames, + ...aliasedModuleNames + ], + importAliasNames + }; + } + + function updateSourceFile(node: SourceFile, statements: Statement[]) { + const updated = getMutableClone(node); + updated.statements = createNodeArray(statements, node.statements); + return updated; + } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index e4c03d32b45d8..aab231dd2f414 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -3,16 +3,1277 @@ /*@internal*/ namespace ts { - // TODO(rbuckton): System module transformer export function transformSystemModule(context: TransformationContext) { + interface DependencyGroup { + name: Identifier; + externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + } + + const { + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + hoistFunctionDeclaration, + setNodeEmitFlags + } = context; + + const compilerOptions = context.getCompilerOptions(); + const resolver = context.getEmitResolver(); + const languageVersion = getEmitScriptTarget(compilerOptions); + const previousExpressionSubstitution = context.expressionSubstitution; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + context.enableExpressionSubstitution(SyntaxKind.BinaryExpression); + context.enableExpressionSubstitution(SyntaxKind.PostfixUnaryExpression); + context.expressionSubstitution = substituteExpression; + + let currentSourceFile: SourceFile; + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + let exportSpecifiers: Map; + let exportEquals: ExportAssignment; + let hasExportStars: boolean; + let exportFunctionForFile: Identifier; + let contextObjectForFile: Identifier; + let exportedLocalNames: Identifier[]; + let exportedFunctionDeclarations: ExpressionStatement[]; + return transformSourceFile; function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); + if (isExternalModule(node) || compilerOptions.isolatedModules) { + currentSourceFile = node; + + // Perform the transformation. + const updated = transformSystemModuleWorker(node); + + currentSourceFile = undefined; + externalImports = undefined; + exportSpecifiers = undefined; + exportEquals = undefined; + hasExportStars = false; + exportFunctionForFile = undefined; + contextObjectForFile = undefined; + exportedLocalNames = undefined; + exportedFunctionDeclarations = undefined; + return updated; + } + + return node; + } + + function transformSystemModuleWorker(node: SourceFile) { + // System modules have the following shape: + // + // System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */}) + // + // The parameter 'exports' here is a callback '(name: string, value: T) => T' that + // is used to publish exported values. 'exports' returns its 'value' argument so in + // most cases expressions that mutate exported values can be rewritten as: + // + // expr -> exports('name', expr) + // + // The only exception in this rule is postfix unary operators, + // see comment to 'substitutePostfixUnaryExpression' for more details + Debug.assert(!exportFunctionForFile); + + // Collect information about the external module and dependency groups. + ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); + + // Make sure that the name of the 'exports' function does not conflict with + // existing identifiers. + exportFunctionForFile = createUniqueName("exports"); + contextObjectForFile = createUniqueName("context"); + + const dependencyGroups = collectDependencyGroups(externalImports); + + const statements: Statement[] = []; + + // Add any prologue directives. + const statementOffset = copyPrologueDirectives(node.statements, statements); + + // var __moduleName = context_1 && context_1.id; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + "__moduleName", + createLogicalAnd( + contextObjectForFile, + createPropertyAccess(contextObjectForFile, "id") + ) + ) + ]) + ) + ); + + // Add the body of the module. + addSystemModuleBody(statements, node, dependencyGroups, statementOffset); + + // Write the call to `System.register` + return updateSourceFile(node, [ + createStatement( + createCall( + createPropertyAccess(createIdentifier("System"), "register"), + [ + node.moduleName ? createLiteral(node.moduleName) : undefined, + createArrayLiteral(map(dependencyGroups, getNameOfDependencyGroup)), + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [ + createParameter(exportFunctionForFile), + createParameter(contextObjectForFile) + ], + setNodeEmitFlags( + setMultiLine( + createBlock(statements), + /*multiLine*/ true + ), + NodeEmitFlags.EmitEmitHelpers + ) + ) + ] + ) + ) + ]); + } + + /** + * Adds the statements for the module body function for the source file. + * + * @param statements The output statements for the module body. + * @param node The source file for the module. + * @param statementOffset The offset at which to begin visiting the statements of the SourceFile. + */ + function addSystemModuleBody(statements: Statement[], node: SourceFile, dependencyGroups: DependencyGroup[], statementOffset: number) { + // Shape of the body in system modules: + // + // function (exports) { + // + // + // + // return { + // setters: [ + // + // ], + // execute: function() { + // + // } + // } + // + // } + // + // i.e: + // + // import {x} from 'file1' + // var y = 1; + // export function foo() { return y + x(); } + // console.log(y); + // + // Will be transformed to: + // + // function(exports) { + // var file_1; // local alias + // var y; + // function foo() { return y + file_1.x(); } + // exports("foo", foo); + // return { + // setters: [ + // function(v) { file_1 = v } + // ], + // execute(): function() { + // y = 1; + // console.log(y); + // } + // }; + // } + + // We start a new lexical environment in this function body, but *not* in the + // body of the execute function. This allows us to emit temporary declarations + // only in the outer module body and not in the inner one. + startLexicalEnvironment(); + + // Visit the statements of the source file, emitting any transformations into + // the `executeStatements` array. We do this *before* we fill the `setters` array + // as we both emit transformations as well as aggregate some data used when creating + // setters. This allows us to reduce the number of times we need to loop through the + // statements of the source file. + const executeStatements = visitNodes(node.statements, visitSourceElement, isStatement, statementOffset); + + // We emit the lexical environment (hoisted variables and function declarations) + // early to align roughly with our previous emit output. + // Two key differences in this approach are: + // - Temporary variables will appear at the top rather than at the bottom of the file + // - Calls to the exporter for exported function declarations are grouped after + // the declarations. + addNodes(statements, endLexicalEnvironment()); + + // Emit early exports for function declarations. + addNodes(statements, exportedFunctionDeclarations); + + const exportStarFunction = addExportStarIfNeeded(statements); + + addNode(statements, + createReturn( + setMultiLine( + createObjectLiteral([ + createPropertyAssignment("setters", + generateSetters(exportStarFunction, dependencyGroups) + ), + createPropertyAssignment("execute", + createFunctionExpression( + /*asteriskToken*/ node, + /*name*/ undefined, + [], + createBlock( + executeStatements + ) + ) + ) + ]), + /*multiLine*/ true + ) + ) + ); + } + + function addExportStarIfNeeded(statements: Statement[]) { + // when resolving exports local exported entries/indirect exported entries in the module + // should always win over entries with similar names that were added via star exports + // to support this we store names of local/indirect exported entries in a set. + // this set is used to filter names brought by star expors. + + // local names set should only be added if we have anything exported + if (!exportedLocalNames && isEmpty(exportSpecifiers)) { + // no exported declarations (export var ...) or export specifiers (export {x}) + // check if we have any non star export declarations. + let hasExportDeclarationWithExportClause = false; + for (const externalImport of externalImports) { + if (externalImport.kind === SyntaxKind.ExportDeclaration && (externalImport).exportClause) { + hasExportDeclarationWithExportClause = true; + break; + } + } + + if (!hasExportDeclarationWithExportClause) { + // we still need to emit exportStar helper + return addExportStarFunction(statements, /*localNames*/ undefined); + } + } + + const exportedNames: ObjectLiteralElement[] = []; + if (exportedLocalNames) { + for (const exportedLocalName of exportedLocalNames) { + // write name of exported declaration, i.e 'export var x...' + addNode(exportedNames, + createPropertyAssignment( + createLiteral(exportedLocalName.text), + createLiteral(true) + ) + ); + } + } + + for (const externalImport of externalImports) { + if (externalImport.kind !== SyntaxKind.ExportDeclaration) { + continue; + } + + const exportDecl = externalImport; + if (!exportDecl.exportClause) { + // export * from ... + continue; + } + + for (const element of exportDecl.exportClause.elements) { + // write name of indirectly exported entry, i.e. 'export {x} from ...' + addNode(exportedNames, + createPropertyAssignment( + createLiteral((element.name || element.propertyName).text), + createLiteral(true) + ) + ); + } + } + + const exportedNamesStorageRef = createUniqueName("exportedNames"); + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + exportedNamesStorageRef, + createObjectLiteral(exportedNames) + ) + ]) + ) + ); + + return addExportStarFunction(statements, exportedNamesStorageRef); + } + + /** + * Emits a setter callback for each dependency group. + * @param write The callback used to write each callback. + */ + function generateSetters(exportStarFunction: Identifier, dependencyGroups: DependencyGroup[]) { + const setters: Expression[] = []; + for (const group of dependencyGroups) { + // derive a unique name for parameter from the first named entry in the group + const parameterName = createUniqueName(forEach(group.externalImports, getLocalNameTextForExternalImport) || ""); + const statements: Statement[] = []; + for (const entry of group.externalImports) { + const importVariableName = getLocalNameForExternalImport(entry); + switch (entry.kind) { + case SyntaxKind.ImportDeclaration: + if (!(entry).importClause) { + // 'import "..."' case + // module is imported only for side-effects, no emit required + break; + } + + // fall-through + case SyntaxKind.ImportEqualsDeclaration: + Debug.assert(importVariableName !== undefined); + // save import into the local + addNode(statements, + createStatement( + createAssignment(importVariableName, parameterName) + ) + ); + break; + + case SyntaxKind.ExportDeclaration: + Debug.assert(importVariableName !== undefined); + if ((entry).exportClause) { + // export {a, b as c} from 'foo' + // + // emit as: + // + // exports_({ + // "a": _["a"], + // "c": _["b"] + // }); + const properties: PropertyAssignment[] = []; + for (const e of (entry).exportClause.elements) { + properties.push( + createPropertyAssignment( + createLiteral(e.name.text), + createElementAccess( + parameterName, + createLiteral((e.propertyName || e.name).text) + ) + ) + ); + } + + addNode(statements, + createStatement( + createCall( + exportFunctionForFile, + [createObjectLiteral(properties)] + ) + ) + ); + } + else { + // export * from 'foo' + // + // emit as: + // + // exportStar(foo_1_1); + addNode(statements, + createStatement( + createCall( + exportStarFunction, + [parameterName] + ) + ) + ); + } + break; + } + } + + addNode(setters, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [createParameter(parameterName)], + createBlock(statements) + ) + ); + } + + return createArrayLiteral(setters); + } + + function visitSourceElement(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node); + + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(node); + + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(node); + + case SyntaxKind.ExportAssignment: + return visitExportAssignment(node); + + default: + return visitNestedNode(node); + } + } + + function visitNestedNode(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.VariableStatement: + return visitVariableStatement(node); + + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node); + + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node); + + case SyntaxKind.ForStatement: + return visitForStatement(node); + + case SyntaxKind.ForInStatement: + return visitForInStatement(node); + + case SyntaxKind.ForOfStatement: + return visitForOfStatement(node); + + case SyntaxKind.DoStatement: + return visitDoStatement(node); + + case SyntaxKind.WhileStatement: + return visitWhileStatement(node); + + case SyntaxKind.LabeledStatement: + return visitLabeledStatement(node); + + case SyntaxKind.WithStatement: + return visitWithStatement(node); + + case SyntaxKind.SwitchStatement: + return visitSwitchStatement(node); + + case SyntaxKind.CaseBlock: + return visitCaseBlock(node); + + case SyntaxKind.CaseClause: + return visitCaseClause(node); + + case SyntaxKind.DefaultClause: + return visitDefaultClause(node); + + case SyntaxKind.TryStatement: + return visitTryStatement(node); + + case SyntaxKind.CatchClause: + return visitCatchClause(node); + + case SyntaxKind.Block: + return visitBlock(node); + + default: + return node; + } + } + + function visitImportDeclaration(node: ImportDeclaration): Node { + if (node.importClause && contains(externalImports, node)) { + hoistVariableDeclaration(getLocalNameForExternalImport(node)); + } + + return undefined; + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): Node { + if (contains(externalImports, node)) { + hoistVariableDeclaration(getLocalNameForExternalImport(node)); + } + + // NOTE(rbuckton): Do we support export import = require('') in System? + return undefined; + } + + function visitExportDeclaration(node: ExportDeclaration): VisitResult { + if (!node.moduleSpecifier) { + const statements: Statement[] = []; + addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); + return statements; + } + + return undefined; + } + + function visitExportSpecifier(specifier: ExportSpecifier): Statement { + if (resolver.getReferencedValueDeclaration(specifier.propertyName || specifier.name) + || resolver.isValueAliasDeclaration(specifier)) { + recordExportName(specifier.name); + return createExportStatement( + specifier.name, + specifier.propertyName || specifier.name + ); + } + return undefined; + } + + function visitExportAssignment(node: ExportAssignment): Statement { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + return createExportStatement( + createLiteral("default"), + node.expression + ); + } + + return undefined; + } + + /** + * Visits a variable statement, hoisting declared names to the top-level module body. + * Each declaration is rewritten into an assignment expression. + * + * @param node The variable statement to visit. + */ + function visitVariableStatement(node: VariableStatement): VisitResult { + const isExported = hasModifier(node, ModifierFlags.Export); + const expressions: Expression[] = []; + for (const variable of node.declarationList.declarations) { + addNode(expressions, transformVariable(variable, isExported)); + } + + if (expressions.length) { + return createStatement(inlineExpressions(expressions)); + } + + return undefined; + } + + /** + * Transforms a VariableDeclaration into one or more assignment expressions. + * + * @param node The VariableDeclaration to transform. + * @param isExported A value used to indicate whether the containing statement was exported. + */ + function transformVariable(node: VariableDeclaration, isExported: boolean): Expression { + // Hoist any bound names within the declaration. + hoistBindingElement(node, isExported); + + if (!node.initializer) { + // If the variable has no initializer, ignore it. + return; + } + + const name = node.name; + if (isIdentifier(name)) { + // If the variable has an IdentifierName, write out an assignment expression in its place. + return createAssignment(name, node.initializer); + } + else { + // If the variable has a BindingPattern, flatten the variable into multiple assignment expressions. + return flattenVariableDestructuringToExpression(node, hoistVariableDeclaration); + } + } + + /** + * Visits a FunctionDeclaration, hoisting it to the outer module body function. + * + * @param node The function declaration to visit. + */ + function visitFunctionDeclaration(node: FunctionDeclaration): Node { + if (hasModifier(node, ModifierFlags.Export)) { + // If the function is exported, ensure it has a name and rewrite the function without any export flags. + const name = node.name || getGeneratedNameForNode(node); + node = createFunctionDeclaration( + /*modifiers*/ undefined, + node.asteriskToken, + name, + node.parameters, + node.body, + /*location*/ node); + + // Record a declaration export in the outer module body function. + recordExportedFunctionDeclaration(node); + + if (!hasModifier(node, ModifierFlags.Default)) { + recordExportName(name); + } + } + + // Hoist the function declaration to the outer module body function. + hoistFunctionDeclaration(node); + return undefined; + } + + /** + * Visits a ClassDeclaration, hoisting its name to the outer module body function. + * + * @param node The class declaration to visit. + */ + function visitClassDeclaration(node: ClassDeclaration): VisitResult { + // Hoist the name of the class declaration to the outer module body function. + const name = getDeclarationName(node); + hoistVariableDeclaration(name); + + const statements: Statement[] = []; + + // Rewrite the class declaration into an assignment of a class expression. + addNode(statements, + createStatement( + createAssignment( + name, + createClassExpression( + node.name, + node.heritageClauses, + node.members, + /*location*/ node + ) + ), + /*location*/ node + ) + ); + + // If the class was exported, write a declaration export to the inner module body function. + if (hasModifier(node, ModifierFlags.Export)) { + if (!hasModifier(node, ModifierFlags.Default)) { + recordExportName(name); + } + + addNode(statements, createDeclarationExport(node)); + } + + return statements; + } + + /** + * Visits the body of a ForStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitForStatement(node: ForStatement): ForStatement { + const initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + const expressions: Expression[] = []; + for (const variable of initializer.declarations) { + addNode(expressions, transformVariable(variable, /*isExported*/ false)); + }; + + return createFor( + expressions.length + ? inlineExpressions(expressions) + : createSynthesizedNode(SyntaxKind.OmittedExpression), + node.condition, + node.incrementor, + visitNode(node.statement, visitNestedNode, isStatement), + /*location*/ node + ); + } + else { + return visitEachChild(node, visitNestedNode, context); + } + } + + /** + * Transforms and hoists the declaration list of a ForInStatement or ForOfStatement into an expression. + * + * @param node The decalaration list to transform. + */ + function transformForBinding(node: VariableDeclarationList): Expression { + const firstDeclaration = firstOrUndefined(node.declarations); + hoistBindingElement(firstDeclaration, /*isExported*/ false); + + const name = firstDeclaration.name; + return isIdentifier(name) + ? name + : flattenVariableDestructuringToExpression(firstDeclaration, hoistVariableDeclaration); + } + + /** + * Visits the body of a ForInStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitForInStatement(node: ForInStatement): ForInStatement { + const initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + const updated = getMutableClone(node); + updated.initializer = transformForBinding(initializer); + updated.statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + return updated; + } + else { + return visitEachChild(node, visitNestedNode, context); + } + } + + /** + * Visits the body of a ForOfStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitForOfStatement(node: ForOfStatement): ForOfStatement { + const initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + const updated = getMutableClone(node); + updated.initializer = transformForBinding(initializer); + updated.statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + return updated; + } + else { + return visitEachChild(node, visitNestedNode, context); + } } - function visitor(node: Node): Node { + /** + * Visits the body of a DoStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitDoStatement(node: DoStatement) { + const statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + if (statement !== node.statement) { + const updated = getMutableClone(node); + updated.statement = statement; + return updated; + } return node; } + + /** + * Visits the body of a WhileStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitWhileStatement(node: WhileStatement) { + const statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + if (statement !== node.statement) { + const updated = getMutableClone(node); + updated.statement = statement; + return updated; + } + return node; + } + + /** + * Visits the body of a LabeledStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitLabeledStatement(node: LabeledStatement) { + const statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + if (statement !== node.statement) { + const updated = getMutableClone(node); + updated.statement = statement; + return updated; + } + return node; + } + + /** + * Visits the body of a WithStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitWithStatement(node: WithStatement) { + const statement = visitNode(node.statement, visitNestedNode, isStatement, /*optional*/ false, liftToBlock); + if (statement !== node.statement) { + const updated = getMutableClone(node); + updated.statement = statement; + return updated; + } + return node; + } + + /** + * Visits the body of a SwitchStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitSwitchStatement(node: SwitchStatement) { + const caseBlock = visitNode(node.caseBlock, visitNestedNode, isCaseBlock); + if (caseBlock !== node.caseBlock) { + const updated = getMutableClone(node); + updated.caseBlock = caseBlock; + return updated; + } + return node; + } + + /** + * Visits the body of a CaseBlock to hoist declarations. + * + * @param node The node to visit. + */ + function visitCaseBlock(node: CaseBlock) { + const clauses = visitNodes(node.clauses, visitNestedNode, isCaseOrDefaultClause); + if (clauses !== node.clauses) { + const updated = getMutableClone(node); + updated.clauses = clauses; + return updated; + } + return node; + } + + /** + * Visits the body of a CaseClause to hoist declarations. + * + * @param node The clause to visit. + */ + function visitCaseClause(node: CaseClause) { + const statements = visitNodes(node.statements, visitNestedNode, isStatement); + if (statements !== node.statements) { + const updated = getMutableClone(node); + updated.statements = statements; + return updated; + } + return node; + } + + /** + * Visits the body of a DefaultClause to hoist declarations. + * + * @param node The clause to visit. + */ + function visitDefaultClause(node: DefaultClause) { + return visitEachChild(node, visitNestedNode, context); + } + + /** + * Visits the body of a TryStatement to hoist declarations. + * + * @param node The statement to visit. + */ + function visitTryStatement(node: TryStatement) { + return visitEachChild(node, visitNestedNode, context); + } + + /** + * Visits the body of a CatchClause to hoist declarations. + * + * @param node The clause to visit. + */ + function visitCatchClause(node: CatchClause) { + const block = visitNode(node.block, visitNestedNode, isBlock); + if (block !== node.block) { + const updated = getMutableClone(node); + updated.block = block; + return updated; + } + return node; + } + + /** + * Visits the body of a Block to hoist declarations. + * + * @param node The block to visit. + */ + function visitBlock(node: Block) { + return visitEachChild(node, visitNestedNode, context); + } + + // + // Substitutions + // + + /** + * Substitute the expression, if necessary. + * + * @param node The node to substitute. + */ + function substituteExpression(node: Expression): Expression { + node = previousExpressionSubstitution(node); + switch (node.kind) { + case SyntaxKind.Identifier: + return substituteExpressionIdentifier(node); + case SyntaxKind.BinaryExpression: + return substituteBinaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return substitutePostfixUnaryExpression(node); + } + return node; + } + + /** + * Substitution for identifiers exported at the top level of a module. + */ + function substituteExpressionIdentifier(node: Identifier): Expression { + const importDeclaration = resolver.getReferencedImportDeclaration(node); + if (importDeclaration) { + return createImportBinding(importDeclaration); + } + + return node; + } + + function substituteBinaryExpression(node: BinaryExpression): Expression { + if (isAssignmentOperator(node.operatorToken.kind)) { + return substituteAssignmentExpression(node); + } + + return node; + } + + function substituteAssignmentExpression(node: BinaryExpression): Expression { + setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); + + const left = node.left; + switch (left.kind) { + case SyntaxKind.Identifier: + const exportDeclaration = resolver.getReferencedExportContainer(left); + if (exportDeclaration) { + return createExportExpression(left, node); + } + break; + + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ArrayLiteralExpression: + if (hasExportedReferenceInDestructuringPattern(left)) { + return substituteDestructuring(node); + } + break; + } + + return node; + } + + function isExportedBinding(name: Identifier) { + const container = resolver.getReferencedExportContainer(name); + return container && container.kind === SyntaxKind.SourceFile; + } + + function hasExportedReferenceInDestructuringPattern(node: ObjectLiteralExpression | ArrayLiteralExpression | Identifier): boolean { + switch (node.kind) { + case SyntaxKind.Identifier: + return isExportedBinding(node); + + case SyntaxKind.ObjectLiteralExpression: + for (const property of (node).properties) { + if (hasExportedReferenceInObjectDestructuringElement(property)) { + return true; + } + } + + break; + + case SyntaxKind.ArrayLiteralExpression: + for (const element of (node).elements) { + if (hasExportedReferenceInArrayDestructuringElement(element)) { + return true; + } + } + + break; + } + + return false; + } + + function hasExportedReferenceInObjectDestructuringElement(node: ObjectLiteralElement): boolean { + if (isShorthandPropertyAssignment(node)) { + return isExportedBinding(node.name); + } + else if (isPropertyAssignment(node)) { + return hasExportedReferenceInDestructuringElement(node.initializer); + } + else { + return false; + } + } + + function hasExportedReferenceInArrayDestructuringElement(node: Expression): boolean { + if (isSpreadElementExpression(node)) { + const expression = node.expression; + return isIdentifier(expression) && isExportedBinding(expression); + } + else { + return hasExportedReferenceInDestructuringElement(node); + } + } + + function hasExportedReferenceInDestructuringElement(node: Expression): boolean { + if (isBinaryExpression(node)) { + const left = node.left; + return node.operatorToken.kind === SyntaxKind.EqualsToken + && isDestructuringPattern(left) + && hasExportedReferenceInDestructuringPattern(left); + } + else if (isIdentifier(node)) { + return isExportedBinding(node); + } + else if (isSpreadElementExpression(node)) { + const expression = node.expression; + return isIdentifier(expression) && isExportedBinding(expression); + } + else if (isDestructuringPattern(node)) { + return hasExportedReferenceInDestructuringPattern(node); + } + else { + return false; + } + } + + function isDestructuringPattern(node: Expression): node is ObjectLiteralExpression | ArrayLiteralExpression | Identifier { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.ObjectLiteralExpression + || kind === SyntaxKind.ArrayLiteralExpression; + } + + function substituteDestructuring(node: BinaryExpression) { + return flattenDestructuringAssignment(node, /*needsValue*/ true, hoistVariableDeclaration); + } + + function substitutePostfixUnaryExpression(node: PostfixUnaryExpression): Expression { + const operand = node.operand; + if (isIdentifier(operand)) { + const exportDeclaration = resolver.getReferencedExportContainer(operand); + if (exportDeclaration) { + const exportCall = createExportExpression( + operand, + createPrefix(node.operator, operand, node) + ); + + return node.operator === SyntaxKind.PlusPlusToken + ? createSubtract(exportCall, createLiteral(1)) + : createAdd(exportCall, createLiteral(1)); + } + } + return node; + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + const moduleName = getExternalModuleName(importNode); + if (moduleName.kind === SyntaxKind.StringLiteral) { + return tryRenameExternalModule(moduleName) + || getSynthesizedClone(moduleName); + } + + return undefined; + } + + /** + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. + */ + function tryRenameExternalModule(moduleName: LiteralExpression) { + if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { + return createLiteral(currentSourceFile.renamedDependencies[moduleName.text]); + } + + return undefined; + } + + function getLocalNameTextForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { + const name = getLocalNameForExternalImport(node); + return name ? name.text : undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + const namespaceDeclaration = getNamespaceDeclarationNode(node); + if (namespaceDeclaration && !isDefaultImport(node)) { + return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); + } + if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) { + return getGeneratedNameForNode(node); + } + if (node.kind === SyntaxKind.ExportDeclaration && (node).moduleSpecifier) { + return getGeneratedNameForNode(node); + } + } + + /** + * Gets a name to use for a DeclarationStatement. + * @param node The declaration statement. + */ + function getDeclarationName(node: DeclarationStatement) { + return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); + } + + function addExportStarFunction(statements: Statement[], localNames: Identifier) { + const exportStarFunction = createUniqueName("exportStar"); + const m = createIdentifier("m"); + const n = createIdentifier("n"); + const exports = createIdentifier("exports"); + let condition: Expression = createStrictInequality(n, createLiteral("default")); + if (localNames) { + condition = createLogicalAnd( + condition, + createLogicalNot(createHasOwnProperty(localNames, n)) + ); + } + + addNode(statements, + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + exportStarFunction, + [createParameter(m)], + createBlock([ + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + exports, + createObjectLiteral([]) + ) + ]) + ), + createForIn( + createVariableDeclarationList([ + createVariableDeclaration(n) + ]), + m, + createBlock([ + createIf( + condition, + createStatement( + createAssignment( + createElementAccess(exports, n), + createElementAccess(m, n) + ) + ) + ) + ]) + ), + createStatement( + createCall( + exportFunctionForFile, + [exports] + ) + ) + ]) + ) + ); + + return exportStarFunction; + } + + /** + * Creates a call to the current file's export function to export a value. + * @param name The bound name of the export. + * @param value The exported value. + */ + function createExportExpression(name: Identifier | StringLiteral, value: Expression) { + const exportName = isIdentifier(name) ? createLiteral(name.text) : name; + return createCall(exportFunctionForFile, [exportName, value]); + } + + /** + * Creates a call to the current file's export function to export a value. + * @param name The bound name of the export. + * @param value The exported value. + */ + function createExportStatement(name: Identifier | StringLiteral, value: Expression) { + return createStatement(createExportExpression(name, value)); + } + + /** + * Creates a call to the current file's export function to export a declaration. + * @param node The declaration to export. + */ + function createDeclarationExport(node: DeclarationStatement) { + const declarationName = getDeclarationName(node); + const exportName = hasModifier(node, ModifierFlags.Default) ? createLiteral("default") : declarationName; + return createExportStatement(exportName, declarationName); + } + + function createImportBinding(importDeclaration: Declaration): LeftHandSideExpression { + let importAlias: Identifier; + let name: Identifier; + if (isImportClause(importDeclaration)) { + importAlias = getGeneratedNameForNode(importDeclaration.parent); + name = createIdentifier("default"); + name.originalKeywordKind = SyntaxKind.DefaultKeyword; + } + else if (isImportSpecifier(importDeclaration)) { + importAlias = getGeneratedNameForNode(importDeclaration.parent.parent.parent); + name = importDeclaration.propertyName || importDeclaration.name; + } + + if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { + return createElementAccess(importAlias, createLiteral(name.text)); + } + else { + return createPropertyAccess(importAlias, getSynthesizedClone(name)); + } + } + + function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { + const groupIndices: Map = {}; + const dependencyGroups: DependencyGroup[] = []; + for (let i = 0; i < externalImports.length; i++) { + const externalImport = externalImports[i]; + const externalModuleName = getExternalModuleNameLiteral(externalImport); + const text = externalModuleName.text; + if (hasProperty(groupIndices, text)) { + // deduplicate/group entries in dependency list by the dependency name + const groupIndex = groupIndices[text]; + dependencyGroups[groupIndex].externalImports.push(externalImport); + continue; + } + else { + groupIndices[text] = dependencyGroups.length; + dependencyGroups.push({ + name: externalModuleName, + externalImports: [externalImport] + }); + } + } + + return dependencyGroups; + } + + function getNameOfDependencyGroup(dependencyGroup: DependencyGroup) { + return dependencyGroup.name; + } + + function recordExportName(name: Identifier) { + if (!exportedLocalNames) { + exportedLocalNames = []; + } + + exportedLocalNames.push(name); + } + + function recordExportedFunctionDeclaration(node: FunctionDeclaration) { + if (!exportedFunctionDeclarations) { + exportedFunctionDeclarations = []; + } + + exportedFunctionDeclarations.push(createDeclarationExport(node)); + } + + function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean) { + const name = node.name; + if (isIdentifier(name)) { + hoistVariableDeclaration(getSynthesizedClone(name)); + if (isExported) { + recordExportName(name); + } + } + else if (isBindingPattern(name)) { + forEach(name.elements, isExported ? hoistExportedBindingElement : hoistNonExportedBindingElement); + } + } + + function hoistExportedBindingElement(node: VariableDeclaration | BindingElement) { + hoistBindingElement(node, /*isExported*/ true); + } + + function hoistNonExportedBindingElement(node: VariableDeclaration | BindingElement) { + hoistBindingElement(node, /*isExported*/ false); + } + + function updateSourceFile(node: SourceFile, statements: Statement[]) { + const updated = getMutableClone(node); + updated.statements = createNodeArray(statements, node.statements); + return updated; + } } } \ No newline at end of file diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 990c3e66c666c..8251c0c467ef3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -92,7 +92,7 @@ namespace ts { * * @param node The node to visit. */ - function visitWithStack(node: Node, visitor: (node: Node) => Node): Node { + function visitWithStack(node: Node, visitor: (node: Node) => VisitResult): VisitResult { // Save state const savedCurrentNamespace = currentNamespace; const savedCurrentScope = currentScope; @@ -102,7 +102,7 @@ namespace ts { // Handle state changes before visiting a node. onBeforeVisitNode(node); - node = visitor(node); + const visited = visitor(node); // Restore state currentNamespace = savedCurrentNamespace; @@ -110,7 +110,7 @@ namespace ts { currentParent = savedCurrentParent; currentNode = savedCurrentNode; - return node; + return visited; } /** @@ -118,7 +118,7 @@ namespace ts { * * @param node The node to visit. */ - function visitor(node: Node): Node { + function visitor(node: Node): VisitResult { return visitWithStack(node, visitorWorker); } @@ -127,14 +127,14 @@ namespace ts { * * @param node The node to visit. */ - function visitorWorker(node: Node): Node { + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.TypeScript) { // This node is explicitly marked as TypeScript, so we should transform the node. - node = visitTypeScript(node); + return visitTypeScript(node); } else if (node.transformFlags & TransformFlags.ContainsTypeScript) { // This node contains TypeScript, so we should visit its children. - node = visitEachChild(node, visitor, context); + return visitEachChild(node, visitor, context); } return node; @@ -145,7 +145,7 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitor(node: Node): Node { + function namespaceElementVisitor(node: Node): VisitResult { return visitWithStack(node, namespaceElementVisitorWorker); } @@ -154,15 +154,15 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitorWorker(node: Node): Node { + function namespaceElementVisitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.TypeScript || isExported(node)) { // This node is explicitly marked as TypeScript, or is exported at the namespace // level, so we should transform the node. - node = visitTypeScript(node); + return visitTypeScript(node); } else if (node.transformFlags & TransformFlags.ContainsTypeScript) { // This node contains TypeScript, so we should visit its children. - node = visitEachChild(node, visitor, context); + return visitEachChild(node, visitor, context); } return node; @@ -173,7 +173,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitor(node: Node) { + function classElementVisitor(node: Node): VisitResult { return visitWithStack(node, classElementVisitorWorker); } @@ -182,7 +182,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitorWorker(node: Node) { + function classElementVisitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.Constructor: // TypeScript constructors are transformed in `transformClassDeclaration`. @@ -198,9 +198,12 @@ namespace ts { // Fallback to the default visit behavior. return visitorWorker(node); + case SyntaxKind.SemicolonClassElement: + return node; + default: - Debug.fail("Unexpected node."); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -209,8 +212,8 @@ namespace ts { * * @param node The node to visit. */ - function visitTypeScript(node: Node): Node { - if (node.flags & NodeFlags.Ambient) { + function visitTypeScript(node: Node): VisitResult { + if (hasModifier(node, ModifierFlags.Ambient)) { // TypeScript ambient declarations are elided. return undefined; } @@ -347,7 +350,7 @@ namespace ts { case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: // TypeScript type assertions are removed, but their subtrees are preserved. - return visitNode((node).expression, visitor, isExpression); + return visitAssertionExpression(node); case SyntaxKind.EnumDeclaration: // TypeScript enum declarations do not exist in ES6 and must be rewritten. @@ -370,8 +373,8 @@ namespace ts { return visitImportEqualsDeclaration(node); default: - Debug.fail("Unexpected node."); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -383,6 +386,7 @@ namespace ts { function onBeforeVisitNode(node: Node) { currentParent = currentNode; currentNode = node; + switch (node.kind) { case SyntaxKind.SourceFile: case SyntaxKind.CaseBlock: @@ -404,7 +408,7 @@ namespace ts { * * @param node The node to transform. */ - function visitClassDeclaration(node: ClassDeclaration): NodeArrayNode { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined; @@ -467,7 +471,7 @@ namespace ts { } } - return createNodeArrayNode(statements); + return statements; } /** @@ -811,7 +815,10 @@ namespace ts { // End the lexical environment. addNodes(statements, endLexicalEnvironment()); - return createBlock(statements); + return setMultiLine( + createBlock(statements, constructor ? constructor.body : undefined), + true + ); } /** @@ -851,7 +858,7 @@ namespace ts { * @param parameter The parameter node. */ function isParameterWithPropertyAssignment(parameter: ParameterDeclaration) { - return parameter.flags & NodeFlags.AccessibilityModifier + return hasModifier(parameter, ModifierFlags.AccessibilityModifier) && isIdentifier(parameter.name); } @@ -863,7 +870,7 @@ namespace ts { function transformParameterWithPropertyAssignment(node: ParameterDeclaration) { Debug.assert(isIdentifier(node.name)); - const name = cloneNode(node.name); + const name = getSynthesizedClone(node.name); return startOnNewLine( createStatement( createAssignment( @@ -910,7 +917,7 @@ namespace ts { */ function isInitializedProperty(member: ClassElement, isStatic: boolean) { return member.kind === SyntaxKind.PropertyDeclaration - && isStatic === ((member.flags & NodeFlags.Static) !== 0) + && isStatic === hasModifier(member, ModifierFlags.Static) && (member).initializer !== undefined; } @@ -922,7 +929,17 @@ namespace ts { * @param receiver The receiver on which each property should be assigned. */ function generateInitializedPropertyStatements(node: ClassExpression | ClassDeclaration, properties: PropertyDeclaration[], receiver: LeftHandSideExpression) { - return map(generateInitializedPropertyExpressions(node, properties, receiver), expressionToStatement); + const statements: Statement[] = []; + for (const property of properties) { + statements.push( + createStatement( + transformInitializedProperty(node, property, receiver), + /*location*/ property + ) + ); + } + + return statements; } /** @@ -935,8 +952,9 @@ namespace ts { function generateInitializedPropertyExpressions(node: ClassExpression | ClassDeclaration, properties: PropertyDeclaration[], receiver: LeftHandSideExpression) { const expressions: Expression[] = []; for (const property of properties) { - expressions.push(transformInitializedProperty(node, property, receiver)); + expressions.push(transformInitializedProperty(node, property, receiver, /*location*/ property)); } + return expressions; } @@ -947,12 +965,13 @@ namespace ts { * @param property The property declaration. * @param receiver The object receiving the property assignment. */ - function transformInitializedProperty(node: ClassExpression | ClassDeclaration, property: PropertyDeclaration, receiver: LeftHandSideExpression) { + function transformInitializedProperty(node: ClassExpression | ClassDeclaration, property: PropertyDeclaration, receiver: LeftHandSideExpression, location?: TextRange) { const propertyName = visitPropertyNameOfClassElement(property); const initializer = visitNode(property.initializer, visitor, isExpression); return createAssignment( createMemberAccessForPropertyName(receiver, propertyName), - initializer + initializer, + location ); } @@ -996,7 +1015,7 @@ namespace ts { */ function isDecoratedClassElement(member: ClassElement, isStatic: boolean) { return nodeOrChildIsDecorated(member) - && isStatic === ((member.flags & NodeFlags.Static) !== 0); + && isStatic === hasModifier(member, ModifierFlags.Static); } /** @@ -1534,7 +1553,7 @@ namespace ts { break; default: - Debug.fail("Cannot serialize unexpected type node."); + Debug.failBadSyntaxKind(node); break; } @@ -1714,10 +1733,7 @@ namespace ts { ); } else { - return setOriginalNode( - cloneNode(name), - name - ); + return getSynthesizedClone(name); } } @@ -1839,7 +1855,7 @@ namespace ts { * * @param node The function node. */ - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { if (shouldElideFunctionLikeDeclaration(node)) { return undefined; } @@ -1854,10 +1870,10 @@ namespace ts { ); if (isNamespaceExport(node)) { - return createNodeArrayNode([ + return [ func, createNamespaceExport(getSynthesizedClone(node.name), getSynthesizedClone(node.name)) - ]); + ]; } return func; @@ -1889,7 +1905,7 @@ namespace ts { */ function shouldElideFunctionLikeDeclaration(node: FunctionLikeDeclaration) { return node.body === undefined - || node.flags & (NodeFlags.Abstract | NodeFlags.Ambient); + || hasModifier(node, ModifierFlags.Abstract | ModifierFlags.Ambient); } /** @@ -2089,28 +2105,24 @@ namespace ts { * This function will be called when one of the following conditions are met: * - The node is exported from a TypeScript namespace. */ - function visitVariableStatement(node: VariableStatement) { - Debug.assert(isNamespaceExport(node)); + function visitVariableStatement(node: VariableStatement): Statement { + if (isNamespaceExport(node)) { + const variables = getInitializedVariables(node.declarationList); + if (variables.length === 0) { + // elide statement if there are no initialized variables. + return undefined; + } - const variables = getInitializedVariables(node.declarationList); - if (variables.length === 0) { - // elide statement if there are no initialized variables. - return undefined; + return createStatement( + inlineExpressions( + map(variables, transformInitializedVariable) + ), + /*location*/ node + ); + } + else { + return visitEachChild(node, visitor, context); } - - return createStatement( - inlineExpressions( - map(variables, transformInitializedVariable) - ) - ); - } - - function getInitializedVariables(node: VariableDeclarationList) { - return filter(node.declarations, isInitializedVariable); - } - - function isInitializedVariable(node: VariableDeclaration) { - return node.initializer !== undefined; } function transformInitializedVariable(node: VariableDeclaration): Expression { @@ -2138,7 +2150,7 @@ namespace ts { * * @param node The enum declaration node. */ - function visitEnumDeclaration(node: EnumDeclaration) { + function visitEnumDeclaration(node: EnumDeclaration): VisitResult { if (shouldElideEnumDeclaration(node)) { return undefined; } @@ -2162,7 +2174,10 @@ namespace ts { location = undefined; } - const namespaceMemberName = getNamespaceMemberName(node.name); + const name = isNamespaceExport(node) + ? getNamespaceMemberName(node.name) + : getSynthesizedClone(node.name); + currentNamespaceLocalName = getGeneratedNameForNode(node); addNode(statements, createStatement( @@ -2176,9 +2191,9 @@ namespace ts { ) ), [createLogicalOr( - namespaceMemberName, + name, createAssignment( - namespaceMemberName, + name, createObjectLiteral() ) )] @@ -2192,7 +2207,7 @@ namespace ts { createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ - createVariableDeclaration(node.name, namespaceMemberName) + createVariableDeclaration(node.name, name) ]), location ) @@ -2200,7 +2215,7 @@ namespace ts { } currentNamespaceLocalName = savedCurrentNamespaceLocalName; - return createNodeArrayNode(statements); + return statements; } /** @@ -2278,8 +2293,11 @@ namespace ts { * @param node The await expression node. */ function visitAwaitExpression(node: AwaitExpression): Expression { - return createYield( - visitNode(node.expression, visitor, isExpression), + return setOriginalNode( + createYield( + visitNode(node.expression, visitor, isExpression), + /*location*/ node + ), node ); } @@ -2312,11 +2330,29 @@ namespace ts { !(expression.kind === SyntaxKind.CallExpression && currentParent.kind === SyntaxKind.NewExpression) && !(expression.kind === SyntaxKind.FunctionExpression && currentParent.kind === SyntaxKind.CallExpression) && !(expression.kind === SyntaxKind.NumericLiteral && currentParent.kind === SyntaxKind.PropertyAccessExpression)) { - return expression; + return trackChildOfNotEmittedNode(node, expression, node.expression); } } - return createParen(expression, node); + return setOriginalNode( + createParen(expression, node), + node + ); + } + + function visitAssertionExpression(node: AssertionExpression): Expression { + const expression = visitNode((node).expression, visitor, isExpression); + return trackChildOfNotEmittedNode(node, expression, node.expression); + } + + function trackChildOfNotEmittedNode(parent: Node, child: T, original: T) { + if (!child.parent && !child.original) { + child = getMutableClone(child); + } + + setNodeEmitFlags(parent, NodeEmitFlags.IsNotEmittedNode); + setNodeEmitFlags(child, NodeEmitFlags.EmitCommentsOfNotEmittedParent); + return child; } /** @@ -2326,7 +2362,7 @@ namespace ts { * * @param node The module declaration node. */ - function visitModuleDeclaration(node: ModuleDeclaration) { + function visitModuleDeclaration(node: ModuleDeclaration): VisitResult { if (shouldElideModuleDeclaration(node)) { return undefined; } @@ -2354,16 +2390,20 @@ namespace ts { location = undefined; } + const name = isNamespaceExport(node) + ? getNamespaceMemberName(node.name) + : getSynthesizedClone(node.name); + let moduleParam: Expression = createLogicalOr( - getNamespaceMemberName(node.name), + name, createAssignment( - getNamespaceMemberName(node.name), + name, createObjectLiteral([]) ) ); if (isNamespaceExport(node)) { - moduleParam = createAssignment(cloneNode(node.name), moduleParam); + moduleParam = createAssignment(getSynthesizedClone(node.name), moduleParam); } currentNamespaceLocalName = getGeneratedNameForNode(node); @@ -2395,7 +2435,7 @@ namespace ts { ); currentNamespaceLocalName = savedCurrentNamespaceLocalName; - return createNodeArrayNode(statements); + return statements; } /** @@ -2441,7 +2481,7 @@ namespace ts { * * @param node The import equals declaration node. */ - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { Debug.assert(!isExternalModuleImportEqualsDeclaration(node)); if (shouldElideImportEqualsDeclaration(node)) { return undefined; @@ -2494,7 +2534,7 @@ namespace ts { * @param node The node to test. */ function isExported(node: Node) { - return (node.flags & NodeFlags.Export) !== 0; + return hasModifier(node, ModifierFlags.Export); } /** @@ -2522,7 +2562,7 @@ namespace ts { */ function isNamedExternalModuleExport(node: Node) { return isExternalModuleExport(node) - && (node.flags & NodeFlags.Default) === 0; + && hasModifier(node, ModifierFlags.Default); } /** @@ -2532,7 +2572,7 @@ namespace ts { */ function isDefaultExternalModuleExport(node: Node) { return isExternalModuleExport(node) - && (node.flags & NodeFlags.Default) !== 0; + && hasModifier(node, ModifierFlags.Default); } /** @@ -2572,13 +2612,10 @@ namespace ts { } function getNamespaceMemberName(name: Identifier): Expression { - name = getSynthesizedClone(name); - return currentNamespaceLocalName - ? createPropertyAccess(currentNamespaceLocalName, name) - : name; + return createPropertyAccess(currentNamespaceLocalName, getSynthesizedClone(name)); } - function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) { + function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration | EnumDeclaration) { return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); } @@ -2587,7 +2624,7 @@ namespace ts { } function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { - return member.flags & NodeFlags.Static + return hasModifier(member, ModifierFlags.Static) ? getDeclarationName(node) : getClassPrototype(node); } @@ -2662,18 +2699,20 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier): Expression { - if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses - && !nodeIsSynthesized(node) - && resolver.getNodeCheckFlags(node) & NodeCheckFlags.SelfReferenceInDecoratedClass) { - // Due to the emit for class decorators, any reference to the class from inside of the class body - // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind - // behavior of class names in ES6. + if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses) { const original = getOriginalNode(node); - const declaration = resolver.getReferencedValueDeclaration(isIdentifier(original) ? original : node); - if (declaration) { - const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; - if (classAlias) { - return cloneNode(classAlias); + if (isIdentifier(original)) { + if (resolver.getNodeCheckFlags(original) & NodeCheckFlags.SelfReferenceInDecoratedClass) { + // Due to the emit for class decorators, any reference to the class from inside of the class body + // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind + // behavior of class names in ES6. + const declaration = resolver.getReferencedValueDeclaration(original); + if (declaration) { + const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; + if (classAlias) { + return getRelocatedClone(classAlias, /*location*/ node); + } + } } } } @@ -2695,7 +2734,7 @@ namespace ts { function substituteCallExpression(node: CallExpression): Expression { const expression = node.expression; - if (isSuperPropertyOrElementAccess(expression)) { + if (isSuperProperty(expression)) { const flags = getSuperContainerAsyncMethodFlags(); if (flags) { const argumentExpression = isPropertyAccessExpression(expression) diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 889810bebec54..398c87aab2bdc 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -777,4 +777,8 @@ namespace ts { } } +if (ts.sys.developmentMode && ts.sys.tryEnableSourceMapsForHost) { + ts.sys.tryEnableSourceMapsForHost(); +} + ts.executeCommandLine(ts.sys.args); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1e4a9241579c0..38d1687acead7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -342,7 +342,7 @@ namespace ts { // Synthesized list SyntaxList, - NodeArrayNode, + // Enum value count Count, // Markers @@ -373,18 +373,9 @@ namespace ts { export const enum NodeFlags { None = 0, - Export = 1 << 0, // Declarations - Ambient = 1 << 1, // Declarations - Public = 1 << 2, // Property/Method - Private = 1 << 3, // Property/Method - Protected = 1 << 4, // Property/Method - Static = 1 << 5, // Property/Method - Readonly = 1 << 6, // Property/Method - Abstract = 1 << 7, // Class/Method/ConstructSignature - Async = 1 << 8, // Property/Method/Function - Default = 1 << 9, // Function/Class (export default declaration) - Let = 1 << 10, // Variable declaration - Const = 1 << 11, // Variable declaration + Let = 1 << 0, // Variable declaration + Const = 1 << 1, // Variable declaration + NestedNamespace = 1 << 2, // Namespace declaration Namespace = 1 << 12, // Namespace declaration ExportContext = 1 << 13, // Export context (initialized by binding) ContainsThis = 1 << 14, // Interface contains references to "this" @@ -404,8 +395,6 @@ namespace ts { ThisNodeOrAnySubNodesHasError = 1 << 28, // If this node or any of its children had an error HasAggregatedChildData = 1 << 29, // If we've computed data from children and cached it in this node - Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async, - AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const, ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, @@ -418,6 +407,26 @@ namespace ts { TypeExcludesFlags = YieldContext | AwaitContext, } + export const enum ModifierFlags { + None = 0, + Export = 1 << 0, // Declarations + Ambient = 1 << 1, // Declarations + Public = 1 << 2, // Property/Method + Private = 1 << 3, // Property/Method + Protected = 1 << 4, // Property/Method + Static = 1 << 5, // Property/Method + Readonly = 1 << 6, // Property/Method + Abstract = 1 << 7, // Class/Method/ConstructSignature + Async = 1 << 8, // Property/Method/Function + Default = 1 << 9, // Function/Class (export default declaration) + Const = 1 << 11, // Variable declaration + + HasComputedFlags = 1 << 31, // Modifier flags have been computed + + AccessibilityModifier = Public | Private | Protected, + NonPublicAccessibilityModifier = Private | Protected, + } + export const enum JsxFlags { None = 0, /** An element from a named property of the JSX.IntrinsicElements interface */ @@ -443,10 +452,11 @@ namespace ts { export interface Node extends TextRange { kind: SyntaxKind; flags: NodeFlags; + /* @internal */ modifierFlagsCache?: ModifierFlags; /* @internal */ transformFlags?: TransformFlags; /* @internal */ excludeTransformFlags?: TransformFlags; decorators?: NodeArray; // Array of decorators (in document order) - modifiers?: ModifiersArray; // Array of modifiers + modifiers?: NodeArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding) /* @internal */ original?: Node; // The original node if this is an updated node. @@ -458,33 +468,10 @@ namespace ts { /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) } - export const enum ArrayKind { - NodeArray = 1, - ModifiersArray = 2, - } - export interface NodeArray extends Array, TextRange { - arrayKind: ArrayKind; hasTrailingComma?: boolean; } - /** - * A NodeArrayNode is a transient node used during transformations to indicate that more than - * one node will substitute a single node in the source. When the source is a NodeArray (as - * part of a call to `visitNodes`), the nodes of a NodeArrayNode will be spread into the - * result array. When the source is a Node (as part of a call to `visitNode`), the NodeArrayNode - * must be converted into a compatible node via the `lift` callback. - */ - /* @internal */ - // @kind(SyntaxKind.NodeArrayNode) - export interface NodeArrayNode extends Node { - nodes: NodeArray; - } - - export interface ModifiersArray extends NodeArray { - flags: number; - } - // @kind(SyntaxKind.AbstractKeyword) // @kind(SyntaxKind.AsyncKeyword) // @kind(SyntaxKind.ConstKeyword) @@ -2751,6 +2738,8 @@ namespace ts { /* @internal */ export const enum TransformFlags { + None = 0, + // Facts // - Flags used to indicate that a node or subtree contains syntax that requires transformation. TypeScript = 1 << 0, @@ -2761,18 +2750,21 @@ namespace ts { ContainsES7 = 1 << 5, ES6 = 1 << 6, ContainsES6 = 1 << 7, + DestructuringAssignment = 1 << 8, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 8, - ContainsPropertyInitializer = 1 << 9, - ContainsLexicalThis = 1 << 10, - ContainsCapturedLexicalThis = 1 << 11, - ContainsDefaultValueAssignments = 1 << 12, - ContainsParameterPropertyAssignments = 1 << 13, - ContainsSpreadElementExpression = 1 << 14, - ContainsComputedPropertyName = 1 << 15, - ContainsBlockScopedBinding = 1 << 16, + ContainsDecorators = 1 << 9, + ContainsPropertyInitializer = 1 << 10, + ContainsLexicalThis = 1 << 11, + ContainsCapturedLexicalThis = 1 << 12, + ContainsDefaultValueAssignments = 1 << 13, + ContainsParameterPropertyAssignments = 1 << 14, + ContainsSpreadElementExpression = 1 << 15, + ContainsComputedPropertyName = 1 << 16, + ContainsBlockScopedBinding = 1 << 17, + + HasComputedFlags = 1 << 31, // Transform flags have been computed. // Assertions // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. @@ -2784,7 +2776,7 @@ namespace ts { // Scope Exclusions // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. - NodeExcludes = TypeScript | Jsx | ES7 | ES6, + NodeExcludes = TypeScript | Jsx | ES7 | ES6 | DestructuringAssignment | HasComputedFlags, ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, @@ -2808,6 +2800,8 @@ namespace ts { AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. IsNotEmittedNode = 1 << 8, // Is a node that is not emitted but whose comments should be preserved if possible. EmitCommentsOfNotEmittedParent = 1 << 9, // Emits comments of missing parent nodes. + NoSubstitution = 1 << 10, // Disables further substitution of an expression. + CapturesThis = 1 << 11, // The function captures a lexical `this` } /** Additional context provided to `visitEachChild` */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 421737553d4b8..a08e2211cdb9d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -190,7 +190,7 @@ namespace ts { // However, this node will be 'missing' in the sense that no actual source-code/tokens are // contained within it. export function nodeIsMissing(node: Node) { - if (!node) { + if (node === undefined) { return true; } @@ -323,8 +323,7 @@ namespace ts { export function isBlockScopedContainerTopLevel(node: Node): boolean { return node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.ModuleDeclaration || - isFunctionLike(node) || - isFunctionBlock(node); + isFunctionLike(node); } export function isGlobalScopeAugmentation(module: ModuleDeclaration): boolean { @@ -347,30 +346,41 @@ namespace ts { return false; } + export function isBlockScope(node: Node, parentNode: Node) { + switch (node.kind) { + case SyntaxKind.SourceFile: + case SyntaxKind.CaseBlock: + case SyntaxKind.CatchClause: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.Constructor: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return true; + + case SyntaxKind.Block: + // function block is not considered block-scope container + // see comment in binder.ts: bind(...), case for SyntaxKind.Block + return parentNode && !isFunctionLike(parentNode); + } + + return false; + } + // Gets the nearest enclosing block scope container that has the provided node // as a descendant, that is not the provided node. export function getEnclosingBlockScopeContainer(node: Node): Node { let current = node.parent; while (current) { - if (isFunctionLike(current)) { + if (isBlockScope(current, current.parent)) { return current; } - switch (current.kind) { - case SyntaxKind.SourceFile: - case SyntaxKind.CaseBlock: - case SyntaxKind.CatchClause: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - return current; - case SyntaxKind.Block: - // function block is not considered block-scope container - // see comment in binder.ts: bind(...), case for SyntaxKind.Block - if (!isFunctionLike(current.parent)) { - return current; - } - } current = current.parent; } @@ -481,6 +491,25 @@ namespace ts { return node; } + export function getCombinedModifierFlags(node: Node): ModifierFlags { + node = walkUpBindingElementsAndPatterns(node); + let flags = getModifierFlags(node); + if (node.kind === SyntaxKind.VariableDeclaration) { + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableDeclarationList) { + flags |= getModifierFlags(node); + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableStatement) { + flags |= getModifierFlags(node); + } + + return flags; + } + // Returns the node flags for this node and all relevant parent nodes. This is done so that // nodes like variable declarations and binding elements can returned a view of their flags // that includes the modifiers from their container. i.e. flags like export/declare aren't @@ -509,7 +538,8 @@ namespace ts { } export function isConst(node: Node): boolean { - return !!(getCombinedNodeFlags(node) & NodeFlags.Const); + return !!(getCombinedNodeFlags(node) & NodeFlags.Const) + || !!(getCombinedModifierFlags(node) & ModifierFlags.Const); } export function isLet(node: Node): boolean { @@ -925,19 +955,20 @@ namespace ts { /** * Determines whether a node is a property or element access expression for super. */ - export function isSuperPropertyOrElementAccess(node: Node): node is (PropertyAccessExpression | ElementAccessExpression) { + export function isSuperProperty(node: Node): node is (PropertyAccessExpression | ElementAccessExpression) { return (node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression) && (node).expression.kind === SyntaxKind.SuperKeyword; } - /** - * Determines whether a node is a call to either `super`, or a super property or element access. - */ + export function isSuperPropertyCall(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression + && isSuperProperty((node).expression); + } + export function isSuperCall(node: Node): node is CallExpression { return node.kind === SyntaxKind.CallExpression - && ((node).expression.kind === SyntaxKind.SuperKeyword - || isSuperPropertyOrElementAccess((node).expression)); + && (node).expression.kind === SyntaxKind.SuperKeyword; } export function getEntityNameFromTypeNode(node: TypeNode): EntityName | Expression { @@ -1217,6 +1248,23 @@ namespace ts { } } + export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + + const importClause = (node).importClause; + if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + return importClause.namedBindings; + } + } + + export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + return node.kind === SyntaxKind.ImportDeclaration + && (node).importClause + && !!(node).importClause.name; + } + export function hasQuestionToken(node: Node) { if (node) { switch (node.kind) { @@ -1362,7 +1410,7 @@ namespace ts { export function isInAmbientContext(node: Node): boolean { while (node) { - if (node.flags & NodeFlags.Ambient || (node.kind === SyntaxKind.SourceFile && (node as SourceFile).isDeclarationFile)) { + if (hasModifier(node, ModifierFlags.Ambient) || (node.kind === SyntaxKind.SourceFile && (node as SourceFile).isDeclarationFile)) { return true; } node = node.parent; @@ -1530,7 +1578,7 @@ namespace ts { } export function isAsyncFunctionLike(node: Node): boolean { - return isFunctionLike(node) && (node.flags & NodeFlags.Async) !== 0 && !isAccessor(node); + return isFunctionLike(node) && hasModifier(node, ModifierFlags.Async) && !isAccessor(node); } export function isStringOrNumericLiteral(kind: SyntaxKind): boolean { @@ -1638,11 +1686,13 @@ namespace ts { * @param parent The parent for the cloned node. */ export function cloneEntityName(node: EntityName, parent?: Node): EntityName { - const clone = cloneNode(node, node, node.flags, parent); + const clone = getMutableClone(node); + clone.parent = parent; if (isQualifiedName(clone)) { const { left, right } = clone; clone.left = cloneEntityName(left, clone); - clone.right = cloneNode(right, right, right.flags, parent); + clone.right = getMutableClone(right); + clone.right.parent = clone; } return clone; @@ -2273,7 +2323,7 @@ namespace ts { else { forEach(declarations, (member: Declaration) => { if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) - && (member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) { + && hasModifier(member, ModifierFlags.Static) === hasModifier(accessor, ModifierFlags.Static)) { const memberName = getPropertyNameForPropertyNameNode(member.name); const accessorName = getPropertyNameForPropertyNameNode(accessor.name); if (memberName === accessorName) { @@ -2506,21 +2556,49 @@ namespace ts { return currentLineIndent; } - export function modifierToFlag(token: SyntaxKind): NodeFlags { + export function hasModifiers(node: Node) { + return getModifierFlags(node) !== ModifierFlags.None; + } + + export function hasModifier(node: Node, flags: ModifierFlags) { + return (getModifierFlags(node) & flags) !== 0; + } + + export function getModifierFlags(node: Node): ModifierFlags { + if (node.modifierFlagsCache & ModifierFlags.HasComputedFlags) { + return node.modifierFlagsCache & ~ModifierFlags.HasComputedFlags; + } + + let flags = ModifierFlags.None; + if (node.modifiers) { + for (const modifier of node.modifiers) { + flags |= modifierToFlag(modifier.kind); + } + } + + if (node.flags & NodeFlags.NestedNamespace) { + flags |= ModifierFlags.Export; + } + + node.modifierFlagsCache = flags | ModifierFlags.HasComputedFlags; + return flags; + } + + export function modifierToFlag(token: SyntaxKind): ModifierFlags { switch (token) { - case SyntaxKind.StaticKeyword: return NodeFlags.Static; - case SyntaxKind.PublicKeyword: return NodeFlags.Public; - case SyntaxKind.ProtectedKeyword: return NodeFlags.Protected; - case SyntaxKind.PrivateKeyword: return NodeFlags.Private; - case SyntaxKind.AbstractKeyword: return NodeFlags.Abstract; - case SyntaxKind.ExportKeyword: return NodeFlags.Export; - case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; - case SyntaxKind.ConstKeyword: return NodeFlags.Const; - case SyntaxKind.DefaultKeyword: return NodeFlags.Default; - case SyntaxKind.AsyncKeyword: return NodeFlags.Async; - case SyntaxKind.ReadonlyKeyword: return NodeFlags.Readonly; + case SyntaxKind.StaticKeyword: return ModifierFlags.Static; + case SyntaxKind.PublicKeyword: return ModifierFlags.Public; + case SyntaxKind.ProtectedKeyword: return ModifierFlags.Protected; + case SyntaxKind.PrivateKeyword: return ModifierFlags.Private; + case SyntaxKind.AbstractKeyword: return ModifierFlags.Abstract; + case SyntaxKind.ExportKeyword: return ModifierFlags.Export; + case SyntaxKind.DeclareKeyword: return ModifierFlags.Ambient; + case SyntaxKind.ConstKeyword: return ModifierFlags.Const; + case SyntaxKind.DefaultKeyword: return ModifierFlags.Default; + case SyntaxKind.AsyncKeyword: return ModifierFlags.Async; + case SyntaxKind.ReadonlyKeyword: return ModifierFlags.Readonly; } - return 0; + return ModifierFlags.None; } export function isAssignmentOperator(token: SyntaxKind): boolean { @@ -2580,7 +2658,7 @@ namespace ts { } export function getLocalSymbolForExportDefault(symbol: Symbol) { - return symbol && symbol.valueDeclaration && (symbol.valueDeclaration.flags & NodeFlags.Default) ? symbol.valueDeclaration.localSymbol : undefined; + return symbol && symbol.valueDeclaration && hasModifier(symbol.valueDeclaration, ModifierFlags.Default) ? symbol.valueDeclaration.localSymbol : undefined; } export function hasJavaScriptFileExtension(fileName: string) { @@ -2733,21 +2811,6 @@ namespace ts { return carriageReturnLineFeed; } - export const enum TextRangeCollapse { - CollapseToStart, - CollapseToEnd, - } - - export function collapseTextRange(range: TextRange, collapse: TextRangeCollapse) { - if (range.pos === range.end) { - return range; - } - - return collapse === TextRangeCollapse.CollapseToStart - ? { pos: range.pos, end: range.pos } - : { pos: range.end, end: range.end }; - } - /** * Tests whether a node and its subtree is simple enough to have its position * information ignored when emitting source maps in a destructuring assignment. @@ -2810,6 +2873,119 @@ namespace ts { return false; } + export function formatSyntaxKind(kind: SyntaxKind): string { + const syntaxKindEnum = (ts).SyntaxKind; + if (syntaxKindEnum) { + for (const name in syntaxKindEnum) { + if (syntaxKindEnum[name] === kind) { + return kind.toString() + " (" + name + ")"; + } + } + } + else { + return kind.toString(); + } + } + + export const enum TextRangeCollapse { + CollapseToStart, + CollapseToEnd, + } + + export function collapseTextRange(range: TextRange, collapse: TextRangeCollapse) { + if (range.pos === range.end) { + return range; + } + + return collapse === TextRangeCollapse.CollapseToStart + ? { pos: range.pos, end: range.pos } + : { pos: range.end, end: range.end }; + } + + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver) { + const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; + const exportSpecifiers: Map = {}; + let exportEquals: ExportAssignment = undefined; + let hasExportStars = false; + for (const node of sourceFile.statements) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + if (!(node).importClause || + resolver.isReferencedAliasDeclaration((node).importClause, /*checkChildren*/ true)) { + // import "mod" + // import x from "mod" where x is referenced + // import * as x from "mod" where x is referenced + // import { x, y } from "mod" where at least one import is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ImportEqualsDeclaration: + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(node)) { + // import x = require("mod") where x is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ExportDeclaration: + if ((node).moduleSpecifier) { + if (!(node).exportClause) { + // export * from "mod" + externalImports.push(node); + hasExportStars = true; + } + else if (resolver.isValueAliasDeclaration(node)) { + // export { x, y } from "mod" where at least one export is a value symbol + externalImports.push(node); + } + } + else { + // export { x, y } + for (const specifier of (node).exportClause.elements) { + const name = (specifier.propertyName || specifier.name).text; + if (!exportSpecifiers[name]) { + exportSpecifiers[name] = [specifier]; + } + else { + exportSpecifiers[name].push(specifier); + } + } + } + break; + + case SyntaxKind.ExportAssignment: + if ((node).isExportEquals && !exportEquals) { + // export = x + exportEquals = node; + } + break; + } + } + + return { externalImports, exportSpecifiers, exportEquals, hasExportStars }; + } + + export function copyPrologueDirectives(from: Statement[], to: Statement[]): number { + for (let i = 0; i < from.length; i++) { + if (isPrologueDirective(from[i])) { + addNode(to, from[i]); + } + else { + return i; + } + } + + return from.length; + } + + export function getInitializedVariables(node: VariableDeclarationList) { + return filter(node.declarations, isInitializedVariable); + } + + function isInitializedVariable(node: VariableDeclaration) { + return node.initializer !== undefined; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that @@ -2818,11 +2994,8 @@ namespace ts { // Node Arrays export function isNodeArray(array: T[]): array is NodeArray { - return (>array).arrayKind === ArrayKind.NodeArray; - } - - export function isModifiersArray(array: Modifier[]): array is ModifiersArray { - return (array).arrayKind === ArrayKind.ModifiersArray; + return array.hasOwnProperty("pos") + && array.hasOwnProperty("end"); } // Literals @@ -2940,7 +3113,8 @@ namespace ts { || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.IndexSignature; + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.SemicolonClassElement; } export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement { @@ -3268,6 +3442,10 @@ namespace ts { return node.kind === SyntaxKind.JsxSpreadAttribute; } + export function isJsxAttribute(node: Node): node is JsxAttribute { + return node.kind === SyntaxKind.JsxAttribute; + } + // Clauses export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { @@ -3300,13 +3478,6 @@ namespace ts { export function isEnumMember(node: Node): node is EnumMember { return node.kind === SyntaxKind.EnumMember; } - - - // Synthesized - - export function isNodeArrayNode(node: Node): node is NodeArrayNode { - return node.kind === SyntaxKind.NodeArrayNode; - } } namespace ts { @@ -3542,6 +3713,6 @@ namespace ts { } export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean { - return node.flags & NodeFlags.AccessibilityModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); + return hasModifier(node, ModifierFlags.AccessibilityModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 05f8e77558662..051c020558a34 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -3,7 +3,7 @@ /* @internal */ namespace ts { - export type OneOrMany = T | NodeArrayNode; + export type VisitResult = T | T[]; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -134,7 +134,7 @@ namespace ts { { name: "arguments", test: isExpression }, ], [SyntaxKind.NewExpression]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForNew }, { name: "typeArguments", test: isTypeNode }, { name: "arguments", test: isExpression }, ], @@ -448,7 +448,7 @@ namespace ts { * @param f The callback function * @param initial The initial value to supply to the reduction. */ - export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T) { + export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T): T { if (node === undefined) { return undefined; } @@ -478,7 +478,7 @@ namespace ts { * @param optional An optional value indicating whether the Node is itself optional. * @param lift An optional callback to execute to lift a NodeArrayNode into a valid Node. */ - export function visitNode(node: T, visitor: (node: Node) => Node, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { + export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { return visitNodeWorker(node, visitor, test, optional, lift, /*parenthesize*/ undefined, /*parentNode*/ undefined); } @@ -493,32 +493,38 @@ namespace ts { * @param parenthesize A callback used to parenthesize the node if needed. * @param parentNode A parentNode for the node. */ - function visitNodeWorker(node: Node, visitor: (node: Node) => Node, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): Node { + function visitNodeWorker(node: Node, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional: boolean, lift: (node: Node[]) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): Node { if (node === undefined) { return undefined; } - let visited = visitor(node); + const visited = visitor(node); if (visited === node) { return node; } - if (visited !== undefined && isNodeArrayNode(visited)) { - visited = (lift || extractSingleNode)((>visited).nodes); - } + let visitedNode: Node; + if (visited === undefined) { + if (!optional) { + Debug.failNotOptional(); + } - if (parenthesize !== undefined && visited !== undefined) { - visited = parenthesize(visited, parentNode); + return undefined; + } + else if (isArray(visited)) { + visitedNode = (lift || extractSingleNode)(visited); + } + else { + visitedNode = visited; } - if (visited === undefined) { - Debug.assert(optional, "Node not optional."); - return undefined; + if (parenthesize !== undefined) { + visitedNode = parenthesize(visitedNode, parentNode); } - Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); - aggregateTransformFlags(visited); - return visited; + Debug.assertNode(visitedNode, test); + aggregateTransformFlags(visitedNode); + return visitedNode; } /** @@ -530,7 +536,7 @@ namespace ts { * @param start An optional value indicating the starting offset at which to start visiting. * @param count An optional value indicating the maximum number of nodes to visit. */ - export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test: (node: Node) => boolean, start?: number, count?: number): TArray { + export function visitNodes>(nodes: TArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, start?: number, count?: number): TArray { return visitNodesWorker(nodes, visitor, test, /*parenthesize*/ undefined, /*parentNode*/ undefined, start, count); } @@ -543,12 +549,12 @@ namespace ts { * @param start An optional value indicating the starting offset at which to start visiting. * @param count An optional value indicating the maximum number of nodes to visit. */ - function visitNodesWorker(nodes: NodeArray, visitor: (node: Node) => Node, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray { + function visitNodesWorker(nodes: NodeArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray { if (nodes === undefined) { return undefined; } - let updated: Node[]; + let updated: NodeArray; // Ensure start and count have valid values const length = nodes.length; @@ -560,9 +566,12 @@ namespace ts { count = length - start; } - // If we are not visiting all of the original nodes, we must always create a new array. if (start > 0 || count < length) { - updated = []; + // If we are not visiting all of the original nodes, we must always create a new array. + // Since this is a fragment of a node array, we do not copy over the previous location + // and will only copy over `hasTrailingComma` if we are including the last element. + updated = createNodeArray([], /*location*/ undefined, + /*hasTrailingComma*/ nodes.hasTrailingComma && start + count === length); } // Visit each original node. @@ -572,20 +581,14 @@ namespace ts { if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { // Ensure we have a copy of `nodes`, up to the current index. - updated = nodes.slice(0, i); + updated = createNodeArray(nodes.slice(0, i), /*location*/ nodes, nodes.hasTrailingComma); } addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node); } } - if (updated !== undefined) { - return isModifiersArray(nodes) - ? createModifiersArray(updated, nodes) - : createNodeArray(updated, nodes, nodes.hasTrailingComma); - } - - return nodes; + return updated || nodes; } /** @@ -595,8 +598,8 @@ namespace ts { * @param visitor The callback used to visit each child. * @param context A lexical environment context for the visitor. */ - export function visitEachChild(node: T, visitor: (node: Node) => Node, context: LexicalEnvironment): T; - export function visitEachChild(node: T & Map, visitor: (node: Node) => Node, context: LexicalEnvironment): T { + export function visitEachChild(node: T, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T; + export function visitEachChild(node: T & Map, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T { if (node === undefined) { return undefined; } @@ -611,17 +614,12 @@ namespace ts { const edgeTraversalPath = nodeEdgeTraversalMap[node.kind]; if (edgeTraversalPath) { - let modifiers: NodeFlags; for (const edge of edgeTraversalPath) { const value = >node[edge.name]; if (value !== undefined) { let visited: Node | NodeArray; if (isArray(value)) { const visitedArray = visitNodesWorker(value, visitor, edge.test, edge.parenthesize, node, 0, value.length); - if (isModifiersArray(visitedArray)) { - modifiers = visitedArray.flags; - } - visited = visitedArray; } else { @@ -630,13 +628,7 @@ namespace ts { if (updated !== undefined || visited !== value) { if (updated === undefined) { - updated = getMutableNode(node); - updated.flags &= ~NodeFlags.Modifier; - } - - if (modifiers) { - updated.flags |= modifiers; - modifiers = undefined; + updated = getMutableClone(node); } if (visited !== value) { @@ -671,9 +663,8 @@ namespace ts { * * @param to The destination array. * @param from The source Node or NodeArrayNode. - * @param test The node test used to validate each node. */ - export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { + export function addNode(to: T[], from: VisitResult, startOnNewLine?: boolean): void { addNodeWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false); } @@ -682,38 +673,37 @@ namespace ts { * * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. - * @param test The node test used to validate each node. */ - export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { + export function addNodes(to: T[], from: VisitResult[], startOnNewLine?: boolean): void { addNodesWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false); } - function addNodeWorker(to: Node[], from: OneOrMany, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) { + function addNodeWorker(to: Node[], from: VisitResult, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { if (to && from) { - if (isNodeArrayNode(from)) { - addNodesWorker(to, from.nodes, startOnNewLine, test, parenthesize, parentNode, isVisiting); - return; + if (isArray(from)) { + addNodesWorker(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); } + else { + const node = parenthesize !== undefined + ? parenthesize(from, parentNode) + : from; - if (parenthesize !== undefined) { - from = parenthesize(from, parentNode); - } + Debug.assertNode(node, test); - Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + if (startOnNewLine) { + node.startsOnNewLine = true; + } - if (startOnNewLine) { - from.startsOnNewLine = true; - } + if (isVisiting) { + aggregateTransformFlags(node); + } - if (isVisiting) { - aggregateTransformFlags(from); + to.push(node); } - - to.push(from); } } - function addNodesWorker(to: Node[], from: OneOrMany[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) { + function addNodesWorker(to: Node[], from: VisitResult[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { if (to && from) { for (const node of from) { addNodeWorker(to, node, startOnNewLine, test, parenthesize, parentNode, isVisiting); @@ -754,9 +744,9 @@ namespace ts { * @param node The SourceFile node. * @param declarations The generated lexical declarations. */ - export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) { + export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]): SourceFile { if (declarations !== undefined && declarations.length) { - const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(mutableNode.statements, declarations); return mutableNode; } @@ -770,10 +760,10 @@ namespace ts { * @param node The ModuleDeclaration node. * @param declarations The generated lexical declarations. */ - export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) { + export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]): ModuleDeclaration { Debug.assert(node.body.kind === SyntaxKind.ModuleBlock); if (declarations !== undefined && declarations.length) { - const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + const mutableNode = getMutableClone(node); mutableNode.body = mergeBlockLexicalEnvironment(node.body, declarations); return mutableNode; } @@ -787,10 +777,10 @@ namespace ts { * @param node The function-like node. * @param declarations The generated lexical declarations. */ - function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) { + function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]): FunctionLikeDeclaration { Debug.assert(node.body !== undefined); if (declarations !== undefined && declarations.length) { - const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + const mutableNode = getMutableClone(node); mutableNode.body = mergeConciseBodyLexicalEnvironment(mutableNode.body, declarations); return mutableNode; } @@ -804,7 +794,7 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]) { + export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]): FunctionBody { if (declarations !== undefined && declarations.length > 0) { return mergeBlockLexicalEnvironment(body, declarations); } @@ -818,7 +808,7 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]) { + export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]): ConciseBody { if (declarations !== undefined && declarations.length > 0) { if (isBlock(body)) { return mergeBlockLexicalEnvironment(body, declarations); @@ -840,8 +830,8 @@ namespace ts { * @param node The block into which to merge lexical declarations. * @param declarations The lexical declarations to merge. */ - function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]) { - const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]): T { + const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(node.statements, declarations); return mutableNode; } @@ -852,7 +842,7 @@ namespace ts { * @param statements The node array to concatentate with the supplied lexical declarations. * @param declarations The lexical declarations to merge. */ - function mergeStatements(statements: NodeArray, declarations: Statement[]) { + function mergeStatements(statements: NodeArray, declarations: Statement[]): NodeArray { return createNodeArray(concatenate(statements, declarations), /*location*/ statements); } @@ -861,7 +851,7 @@ namespace ts { * * @param nodes The NodeArray. */ - export function liftToBlock(nodes: NodeArray) { + export function liftToBlock(nodes: Node[]): Block { Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block."); return createBlock(>nodes); } @@ -871,9 +861,9 @@ namespace ts { * * @param nodes The NodeArray. */ - function extractSingleNode(nodes: NodeArray) { + function extractSingleNode(nodes: Node[]): Node { Debug.assert(nodes.length <= 1, "Too many nodes written to output."); - return nodes.length > 0 ? nodes[0] : undefined; + return singleOrUndefined(nodes); } /** @@ -892,15 +882,15 @@ namespace ts { */ function aggregateTransformFlagsForNode(node: Node): TransformFlags { if (node === undefined) { - return 0; + return TransformFlags.None; } - - if (node.transformFlags === undefined) { + else if (node.transformFlags & TransformFlags.HasComputedFlags) { + return node.transformFlags & ~node.excludeTransformFlags; + } + else { const subtreeFlags = aggregateTransformFlagsForSubtree(node); return computeTransformFlagsForNode(node, subtreeFlags); } - - return node.transformFlags & ~node.excludeTransformFlags; } /** @@ -909,12 +899,12 @@ namespace ts { function aggregateTransformFlagsForSubtree(node: Node): TransformFlags { // We do not transform ambient declarations or types, so there is no need to // recursively aggregate transform flags. - if (node.flags & NodeFlags.Ambient || isTypeNode(node)) { - return 0; + if (hasModifier(node, ModifierFlags.Ambient) || isTypeNode(node)) { + return TransformFlags.None; } // Aggregate the transform flags of each child. - return reduceEachChild(node, aggregateTransformFlagsForChildNode, 0); + return reduceEachChild(node, aggregateTransformFlagsForChildNode, TransformFlags.None); } /** @@ -924,4 +914,43 @@ namespace ts { function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, child: Node): TransformFlags { return transformFlags | aggregateTransformFlagsForNode(child); } + + export namespace Debug { + export function failNotOptional(message?: string) { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert(false, message || "Node not optional."); + } + } + + export function failBadSyntaxKind(node: Node, message?: string) { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert(false, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`); + } + } + + export function assertNode(node: Node, test: (node: Node) => boolean, message?: string): void { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert( + test === undefined || test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`); + }; + } + + function getFunctionName(func: Function) { + if (typeof func !== "function") { + return ""; + } + else if (func.hasOwnProperty("name")) { + return (func).name; + } + else { + const text = Function.prototype.toString.call(func); + const match = /^function\s+([\w\$]+)\s*\(/.exec(text); + return match ? match[1] : ""; + } + } + } } \ No newline at end of file diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 6f20093c7daed..dbcd6c489300f 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -395,7 +395,7 @@ namespace ts.BreakpointResolver { // Breakpoint is possible in variableDeclaration only if there is initialization // or its declaration from 'for of' if (variableDeclaration.initializer || - (variableDeclaration.flags & NodeFlags.Export) || + hasModifier(variableDeclaration, ModifierFlags.Export) || variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement) { return textSpanFromVariableDeclaration(variableDeclaration); } @@ -413,7 +413,7 @@ namespace ts.BreakpointResolver { function canHaveSpanInParameterDeclaration(parameter: ParameterDeclaration): boolean { // Breakpoint is possible on parameter only if it has initializer, is a rest parameter, or has public or private modifier return !!parameter.initializer || parameter.dotDotDotToken !== undefined || - !!(parameter.flags & NodeFlags.Public) || !!(parameter.flags & NodeFlags.Private); + hasModifier(parameter, ModifierFlags.Public | ModifierFlags.Private); } function spanInParameterDeclaration(parameter: ParameterDeclaration): TextSpan { @@ -439,7 +439,7 @@ namespace ts.BreakpointResolver { } function canFunctionHaveSpanInWholeDeclaration(functionDeclaration: FunctionLikeDeclaration) { - return !!(functionDeclaration.flags & NodeFlags.Export) || + return hasModifier(functionDeclaration, ModifierFlags.Export) || (functionDeclaration.parent.kind === SyntaxKind.ClassDeclaration && functionDeclaration.kind !== SyntaxKind.Constructor); } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f62c6cb1700dd..04821b35f4ac2 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -130,7 +130,7 @@ namespace ts.NavigationBar { return topLevelNodes; } - + function sortNodes(nodes: Node[]): Node[] { return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => { if (n1.name && n2.name) { @@ -147,7 +147,7 @@ namespace ts.NavigationBar { } }); } - + function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void { nodes = sortNodes(nodes); @@ -178,8 +178,8 @@ namespace ts.NavigationBar { function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) { if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) { - // A function declaration is 'top level' if it contains any function declarations - // within it. + // A function declaration is 'top level' if it contains any function declarations + // within it. if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) { // Proper function declarations can only have identifier names if (forEach((functionDeclaration.body).statements, @@ -198,7 +198,7 @@ namespace ts.NavigationBar { return false; } - + function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] { let items: ts.NavigationBarItem[] = []; @@ -258,7 +258,7 @@ namespace ts.NavigationBar { if (isBindingPattern((node).name)) { break; } - if ((node.flags & NodeFlags.Modifier) === 0) { + if (!hasModifiers(node)) { return undefined; } return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberVariableElement); @@ -395,19 +395,19 @@ namespace ts.NavigationBar { let result: string[] = []; result.push(moduleDeclaration.name.text); - + while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) { moduleDeclaration = moduleDeclaration.body; result.push(moduleDeclaration.name.text); - } + } return result.join("."); } function createModuleItem(node: ModuleDeclaration): NavigationBarItem { let moduleName = getModuleName(node); - + let childItems = getItemsWorker(getChildNodes((getInnermostModule(node).body).statements), createChildItem); return getNavigationBarItem(moduleName, diff --git a/src/services/services.ts b/src/services/services.ts index 61b68f78dd8d0..e6e306efbc100 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -962,7 +962,7 @@ namespace ts { case SyntaxKind.Parameter: // Only consider properties defined as constructor parameters - if (!(node.flags & NodeFlags.AccessibilityModifier)) { + if (!(getModifierFlags(node) & ModifierFlags.AccessibilityModifier)) { break; } // fall through @@ -2655,7 +2655,7 @@ namespace ts { case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement; case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement; case SyntaxKind.EnumMember: return ScriptElementKind.variableElement; - case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement; + case SyntaxKind.Parameter: return (getModifierFlags(node) & ModifierFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement; case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportSpecifier: case SyntaxKind.ImportClause: @@ -5046,14 +5046,14 @@ namespace ts { } const keywords: Node[] = []; - const modifierFlag: NodeFlags = getFlagFromModifier(modifier); + const modifierFlag: ModifierFlags = getFlagFromModifier(modifier); let nodes: Node[]; switch (container.kind) { case SyntaxKind.ModuleBlock: case SyntaxKind.SourceFile: // Container is either a class declaration or the declaration is a classDeclaration - if (modifierFlag & NodeFlags.Abstract) { + if (modifierFlag & ModifierFlags.Abstract) { nodes = ((declaration).members).concat(declaration); } else { @@ -5070,7 +5070,7 @@ namespace ts { // If we're an accessibility modifier, we're in an instance member and should search // the constructor's parameter list for instance members as well. - if (modifierFlag & NodeFlags.AccessibilityModifier) { + if (modifierFlag & ModifierFlags.AccessibilityModifier) { const constructor = forEach((container).members, member => { return member.kind === SyntaxKind.Constructor && member; }); @@ -5079,7 +5079,7 @@ namespace ts { nodes = nodes.concat(constructor.parameters); } } - else if (modifierFlag & NodeFlags.Abstract) { + else if (modifierFlag & ModifierFlags.Abstract) { nodes = nodes.concat(container); } break; @@ -5088,7 +5088,7 @@ namespace ts { } forEach(nodes, node => { - if (node.modifiers && node.flags & modifierFlag) { + if (getModifierFlags(node) & modifierFlag) { forEach(node.modifiers, child => pushKeywordIf(keywords, child, modifier)); } }); @@ -5098,19 +5098,19 @@ namespace ts { function getFlagFromModifier(modifier: SyntaxKind) { switch (modifier) { case SyntaxKind.PublicKeyword: - return NodeFlags.Public; + return ModifierFlags.Public; case SyntaxKind.PrivateKeyword: - return NodeFlags.Private; + return ModifierFlags.Private; case SyntaxKind.ProtectedKeyword: - return NodeFlags.Protected; + return ModifierFlags.Protected; case SyntaxKind.StaticKeyword: - return NodeFlags.Static; + return ModifierFlags.Static; case SyntaxKind.ExportKeyword: - return NodeFlags.Export; + return ModifierFlags.Export; case SyntaxKind.DeclareKeyword: - return NodeFlags.Ambient; + return ModifierFlags.Ambient; case SyntaxKind.AbstractKeyword: - return NodeFlags.Abstract; + return ModifierFlags.Abstract; default: Debug.fail(); } @@ -5564,7 +5564,7 @@ namespace ts { // If this is private property or method, the scope is the containing class if (symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)) { - const privateDeclaration = forEach(symbol.getDeclarations(), d => (d.flags & NodeFlags.Private) ? d : undefined); + const privateDeclaration = forEach(symbol.getDeclarations(), d => (getModifierFlags(d) & ModifierFlags.Private) ? d : undefined); if (privateDeclaration) { return getAncestor(privateDeclaration, SyntaxKind.ClassDeclaration); } @@ -5819,7 +5819,7 @@ namespace ts { return undefined; } // Whether 'super' occurs in a static context within a class. - let staticFlag = NodeFlags.Static; + let staticFlag = ModifierFlags.Static; switch (searchSpaceNode.kind) { case SyntaxKind.PropertyDeclaration: @@ -5829,7 +5829,7 @@ namespace ts { case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - staticFlag &= searchSpaceNode.flags; + staticFlag &= getModifierFlags(searchSpaceNode); searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class break; default: @@ -5854,7 +5854,7 @@ namespace ts { // If we have a 'super' container, we must have an enclosing class. // Now make sure the owning class is the same as the search-space // and has the same static qualifier as the original 'super's owner. - if (container && (NodeFlags.Static & container.flags) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) { + if (container && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) { references.push(getReferenceEntryFromNode(node)); } }); @@ -5867,7 +5867,7 @@ namespace ts { let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false); // Whether 'this' occurs in a static context within a class. - let staticFlag = NodeFlags.Static; + let staticFlag = ModifierFlags.Static; switch (searchSpaceNode.kind) { case SyntaxKind.MethodDeclaration: @@ -5881,7 +5881,7 @@ namespace ts { case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - staticFlag &= searchSpaceNode.flags; + staticFlag &= getModifierFlags(searchSpaceNode); searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class break; case SyntaxKind.SourceFile: @@ -5953,7 +5953,7 @@ namespace ts { case SyntaxKind.ClassDeclaration: // Make sure the container belongs to the same class // and has the appropriate static modifier from the original container. - if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (container.flags & NodeFlags.Static) === staticFlag) { + if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) { result.push(getReferenceEntryFromNode(node)); } break; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index afdc85fffd805..9933e8465c603 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -521,15 +521,15 @@ namespace ts { } export function getNodeModifiers(node: Node): string { - let flags = getCombinedNodeFlags(node); + let flags = getCombinedModifierFlags(node); let result: string[] = []; - if (flags & NodeFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier); - if (flags & NodeFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier); - if (flags & NodeFlags.Public) result.push(ScriptElementKindModifier.publicMemberModifier); - if (flags & NodeFlags.Static) result.push(ScriptElementKindModifier.staticModifier); - if (flags & NodeFlags.Abstract) result.push(ScriptElementKindModifier.abstractModifier); - if (flags & NodeFlags.Export) result.push(ScriptElementKindModifier.exportedModifier); + if (flags & ModifierFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier); + if (flags & ModifierFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier); + if (flags & ModifierFlags.Public) result.push(ScriptElementKindModifier.publicMemberModifier); + if (flags & ModifierFlags.Static) result.push(ScriptElementKindModifier.staticModifier); + if (flags & ModifierFlags.Abstract) result.push(ScriptElementKindModifier.abstractModifier); + if (flags & ModifierFlags.Export) result.push(ScriptElementKindModifier.exportedModifier); if (isInAmbientContext(node)) result.push(ScriptElementKindModifier.ambientModifier); return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none; @@ -615,7 +615,7 @@ namespace ts { // [a,b,c] from: // [a, b, c] = someExpression; if (node.parent.kind === SyntaxKind.BinaryExpression && - (node.parent).left === node && + (node.parent).left === node && (node.parent).operatorToken.kind === SyntaxKind.EqualsToken) { return true; } @@ -629,7 +629,7 @@ namespace ts { // [a, b, c] of // [x, [a, b, c] ] = someExpression - // or + // or // {x, a: {a, b, c} } = someExpression if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) { return true; diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.leadingAsterisk.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.leadingAsterisk.json index fbc795c1cb81e..3fe305d4106fd 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.leadingAsterisk.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.leadingAsterisk.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 22, - "arrayKind": 1 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noLeadingAsterisk.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noLeadingAsterisk.json index fbc795c1cb81e..3fe305d4106fd 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noLeadingAsterisk.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noLeadingAsterisk.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 22, - "arrayKind": 1 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json index 118fedcc381d8..7947de57a58f2 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json @@ -21,7 +21,6 @@ }, "length": 1, "pos": 8, - "end": 15, - "arrayKind": 1 + "end": 15 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noType.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noType.json index 98453fb802e2d..7a4b97b133672 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noType.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noType.json @@ -21,7 +21,6 @@ }, "length": 1, "pos": 8, - "end": 13, - "arrayKind": 1 + "end": 13 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json index d0c036bea7425..746c4056f833c 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json @@ -37,7 +37,6 @@ }, "length": 1, "pos": 8, - "end": 29, - "arrayKind": 1 + "end": 29 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json index e05c296e10b8f..ff4bba576d240 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTag1.json @@ -37,7 +37,6 @@ }, "length": 1, "pos": 8, - "end": 29, - "arrayKind": 1 + "end": 29 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json index f731abb0a8e7b..23264572fedaa 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName1.json @@ -38,7 +38,6 @@ }, "length": 1, "pos": 8, - "end": 31, - "arrayKind": 1 + "end": 31 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json index e230dfcd46cee..ceb123b6b34c7 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagBracketedName2.json @@ -38,7 +38,6 @@ }, "length": 1, "pos": 8, - "end": 36, - "arrayKind": 1 + "end": 36 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType1.json index 44173787d277a..19bab08a77b4e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType1.json @@ -37,7 +37,6 @@ }, "length": 1, "pos": 8, - "end": 29, - "arrayKind": 1 + "end": 29 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json index 224eba3501e33..5da9084897e13 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType2.json @@ -37,7 +37,6 @@ }, "length": 1, "pos": 8, - "end": 29, - "arrayKind": 1 + "end": 29 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json index 0a78384e3e798..293d2a4c2124e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json @@ -27,7 +27,6 @@ }, "length": 1, "pos": 8, - "end": 18, - "arrayKind": 1 + "end": 18 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag1.json index bac9b2a02ef3f..a8425d833ae8e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag1.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 24, - "arrayKind": 1 + "end": 24 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag2.json index 6f7fb02b529d5..af4fd91964d13 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag2.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 24, - "arrayKind": 1 + "end": 24 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnsTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnsTag1.json index d64b56b0a2116..ceca9584a2d80 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnsTag1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnsTag1.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 25, - "arrayKind": 1 + "end": 25 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json index 6be165c0c7661..2804a704ba7d5 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json @@ -32,13 +32,11 @@ }, "length": 1, "pos": 17, - "end": 19, - "arrayKind": 1 + "end": 19 } }, "length": 1, "pos": 8, - "end": 19, - "arrayKind": 1 + "end": 19 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json index 85485b2514308..a55761a52b79e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json @@ -43,13 +43,11 @@ }, "length": 2, "pos": 17, - "end": 21, - "arrayKind": 1 + "end": 21 } }, "length": 1, "pos": 8, - "end": 21, - "arrayKind": 1 + "end": 21 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json index e846f5daadac5..64e6e206e1115 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json @@ -43,13 +43,11 @@ }, "length": 2, "pos": 17, - "end": 22, - "arrayKind": 1 + "end": 22 } }, "length": 1, "pos": 8, - "end": 22, - "arrayKind": 1 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json index e846f5daadac5..64e6e206e1115 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json @@ -43,13 +43,11 @@ }, "length": 2, "pos": 17, - "end": 22, - "arrayKind": 1 + "end": 22 } }, "length": 1, "pos": 8, - "end": 22, - "arrayKind": 1 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json index 245ed62698066..9b5a4a6c79006 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json @@ -43,13 +43,11 @@ }, "length": 2, "pos": 17, - "end": 23, - "arrayKind": 1 + "end": 23 } }, "length": 1, "pos": 8, - "end": 23, - "arrayKind": 1 + "end": 23 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json index 323770c644e4b..d5c1c997b021d 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag6.json @@ -43,13 +43,11 @@ }, "length": 2, "pos": 17, - "end": 23, - "arrayKind": 1 + "end": 23 } }, "length": 1, "pos": 8, - "end": 23, - "arrayKind": 1 + "end": 23 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json index 95c96531bdf09..da4650770666b 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json @@ -69,7 +69,6 @@ }, "length": 2, "pos": 8, - "end": 55, - "arrayKind": 1 + "end": 55 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json index 93b84f00324d5..c995aa8bf22bf 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTagOnSameLine.json @@ -37,7 +37,6 @@ }, "length": 1, "pos": 8, - "end": 29, - "arrayKind": 1 + "end": 29 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typeTag.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typeTag.json index fbc795c1cb81e..3fe305d4106fd 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typeTag.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typeTag.json @@ -31,7 +31,6 @@ }, "length": 1, "pos": 8, - "end": 22, - "arrayKind": 1 + "end": 22 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionReturnType1.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionReturnType1.json index e02b9ab7dd908..209b9440a8762 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionReturnType1.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionReturnType1.json @@ -25,7 +25,6 @@ }, "length": 2, "pos": 10, - "end": 25, - "arrayKind": 1 + "end": 25 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType1.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType1.json index 94fe0ec2978de..d3be94a76ffab 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType1.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType1.json @@ -5,7 +5,6 @@ "parameters": { "length": 0, "pos": 10, - "end": 10, - "arrayKind": 1 + "end": 10 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType2.json index e02b9ab7dd908..209b9440a8762 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.functionType2.json @@ -25,7 +25,6 @@ }, "length": 2, "pos": 10, - "end": 25, - "arrayKind": 1 + "end": 25 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType1.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType1.json index 929f0046cffc8..ab9bb050a8996 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType1.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType1.json @@ -5,7 +5,6 @@ "members": { "length": 0, "pos": 2, - "end": 2, - "arrayKind": 1 + "end": 2 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType2.json index 8e6eb1fa11a10..73ff5dbfa6076 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType2.json @@ -16,7 +16,6 @@ }, "length": 1, "pos": 2, - "end": 5, - "arrayKind": 1 + "end": 5 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType3.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType3.json index ce5dedcaf56c2..0ffac6cba8f40 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType3.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType3.json @@ -21,7 +21,6 @@ }, "length": 1, "pos": 2, - "end": 13, - "arrayKind": 1 + "end": 13 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType4.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType4.json index 1192b2a3a3ccb..f49c2fd22ae7f 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType4.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType4.json @@ -27,7 +27,6 @@ }, "length": 2, "pos": 2, - "end": 10, - "arrayKind": 1 + "end": 10 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType5.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType5.json index b31585c8e3716..4474dba8e21f6 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType5.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType5.json @@ -32,7 +32,6 @@ }, "length": 2, "pos": 2, - "end": 18, - "arrayKind": 1 + "end": 18 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType6.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType6.json index 81ee5b53451e1..a88005b0cec91 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType6.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType6.json @@ -32,7 +32,6 @@ }, "length": 2, "pos": 2, - "end": 18, - "arrayKind": 1 + "end": 18 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType7.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType7.json index 595798b1a8380..e69f9a746823e 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType7.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType7.json @@ -37,7 +37,6 @@ }, "length": 2, "pos": 2, - "end": 26, - "arrayKind": 1 + "end": 26 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType8.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType8.json index b4d00c873ce3f..3570193109715 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType8.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.recordType8.json @@ -17,7 +17,6 @@ }, "length": 1, "pos": 2, - "end": 10, - "arrayKind": 1 + "end": 10 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.topLevelNoParenUnionType.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.topLevelNoParenUnionType.json index dd1b017da00b4..dd9ef74f8c616 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.topLevelNoParenUnionType.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.topLevelNoParenUnionType.json @@ -15,7 +15,6 @@ }, "length": 2, "pos": 1, - "end": 14, - "arrayKind": 1 + "end": 14 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType0.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType0.json index dbb6f35ab0026..2b5580f762237 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType0.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType0.json @@ -5,7 +5,6 @@ "types": { "length": 0, "pos": 2, - "end": 2, - "arrayKind": 1 + "end": 2 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType1.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType1.json index 1ab9530626dc5..f741169be4b73 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType1.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType1.json @@ -10,7 +10,6 @@ }, "length": 1, "pos": 2, - "end": 8, - "arrayKind": 1 + "end": 8 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType2.json index aaea91528d7be..939eec1cb0c58 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType2.json @@ -15,7 +15,6 @@ }, "length": 2, "pos": 2, - "end": 15, - "arrayKind": 1 + "end": 15 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType3.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType3.json index a685de8662208..50b0a702a84aa 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType3.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.tupleType3.json @@ -20,7 +20,6 @@ }, "length": 3, "pos": 2, - "end": 23, - "arrayKind": 1 + "end": 23 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference1.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference1.json index 970c6fe1b1304..282bc8c8e6469 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference1.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference1.json @@ -16,7 +16,6 @@ }, "length": 1, "pos": 4, - "end": 10, - "arrayKind": 1 + "end": 10 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference2.json index 913ba27897d8b..570db90795670 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.typeReference2.json @@ -21,7 +21,6 @@ }, "length": 2, "pos": 4, - "end": 17, - "arrayKind": 1 + "end": 17 } } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionType.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionType.json index aae1d6bc1691f..f9301d05cc505 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionType.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionType.json @@ -15,7 +15,6 @@ }, "length": 2, "pos": 2, - "end": 15, - "arrayKind": 1 + "end": 15 } } \ No newline at end of file