diff --git a/Jakefile.js b/Jakefile.js index 889d7cf6299..1f6120ef64a 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -31,9 +31,9 @@ if (process.env.path !== undefined) { } var compilerSources = [ + "types.ts", "core.ts", "sys.ts", - "types.ts", "scanner.ts", "factory.ts", "factory.generated.ts", @@ -60,6 +60,8 @@ var servicesSources = [ "sys.ts", "types.ts", "scanner.ts", + "factory.ts", + "factory.generated.ts", "parser.ts", "utilities.ts", "binder.ts", diff --git a/scripts/processTypes.ts b/scripts/processTypes.ts index 0ba68803e7d..f8928d58ae0 100644 --- a/scripts/processTypes.ts +++ b/scripts/processTypes.ts @@ -404,8 +404,6 @@ function generateFactory(outputFile: string) { let writer = createLineWrappingTextWriter(host.getNewLine(), columnWrap); writer.write(`// `); writer.writeLine(); - writer.write(`/// `); - writer.writeLine(); writer.write(`/// `); writer.writeLine(); writer.write(`namespace ts {`); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 29d7e6b43eb..7c1000b7a84 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2814,9 +2814,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return; } - let parentNode = nodeStack.getParent(); emitToken(SyntaxKind.OpenBraceToken, node.pos); increaseIndent(); + + let parentNode = nodeStack.getParent(); scopeEmitStart(parentNode); if (node.kind === SyntaxKind.ModuleBlock) { Debug.assert(parentNode.kind === SyntaxKind.ModuleDeclaration); diff --git a/src/compiler/factory.generated.ts b/src/compiler/factory.generated.ts index 24c4d4b41fc..56073b6de1c 100644 --- a/src/compiler/factory.generated.ts +++ b/src/compiler/factory.generated.ts @@ -1,5 +1,4 @@ // -/// /// namespace ts { export namespace factory { @@ -2889,6 +2888,9 @@ namespace ts { export function isEnumMember(node: Node): node is EnumMember { return node && node.kind === SyntaxKind.EnumMember; } + export function isSourceFile(node: Node): node is SourceFile { + return node && node.kind === SyntaxKind.SourceFile; + } export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression { return node && node.kind === SyntaxKind.JSDocTypeExpression; } @@ -3187,6 +3189,7 @@ namespace ts { case SyntaxKind.JSDocReturnTag: case SyntaxKind.JSDocTypeTag: case SyntaxKind.JSDocParameterTag: + case SyntaxKind.SourceFile: return true; } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index bde56323a04..1276fea83b3 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -10,7 +10,7 @@ namespace ts { export function createNode(kind: SyntaxKind): T { return factory.createNode(kind); } - + // @internal export namespace factory { export function setNodeFlags(node: T, flags: NodeFlags): T { @@ -129,6 +129,16 @@ namespace ts { return node; } + export function updateSourceFile(node: SourceFile, statements: NodeArray, endOfFileToken: Node): SourceFile { + if (statements !== node.statements || endOfFileToken !== node.endOfFileToken) { + let newNode = createSourceFile(); + newNode.statements = statements; + newNode.endOfFileToken = endOfFileToken; + return updateFrom(node, newNode); + } + return node; + } + export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { let node = factory.createNumericLiteral(String(value), location, flags); return node; diff --git a/src/compiler/transform.generated.ts b/src/compiler/transform.generated.ts index 0b479aded50..2a284b835bf 100644 --- a/src/compiler/transform.generated.ts +++ b/src/compiler/transform.generated.ts @@ -600,6 +600,11 @@ namespace ts.transform { node, visitNode((node).name, visitor), visitNode((node).initializer, visitor))); + case SyntaxKind.SourceFile: + return write(factory.updateSourceFile( + node, + >visitNodes((node).statements, visitor), + (node).endOfFileToken)); case SyntaxKind.JSDocTypeExpression: return write(factory.updateJSDocTypeExpression( node, diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts index ed1e2bbf062..999bcc1a76a 100644 --- a/src/compiler/transform.ts +++ b/src/compiler/transform.ts @@ -4,14 +4,6 @@ const FORCE_TRANSFORMS = false; /* @internal */ namespace ts { - - // Flags enum to track count of temp variables and a few dedicated names - const enum TempFlags { - Auto = 0x00000000, // No preferred name - CountMask = 0x0FFFFFFF, // Temp variable counter - _i = 0x10000000, // Use/preference flag for '_i' - } - /** * Computes the transform flags for a node, given the transform flags of its subtree * @param node The node to analyze @@ -261,8 +253,23 @@ namespace ts { _compilerOptions: CompilerOptions, _currentSourceFile: SourceFile, _resolver: EmitResolver, _generatedNameSet: Map, _nodeToGeneratedName: string[]) { return transform.runTransformationChain(statements, chain, _compilerOptions, _currentSourceFile, _resolver, _generatedNameSet, _nodeToGeneratedName); } - + + export type Visitor = (input: Node, output: (node: Node) => void) => void; + + export const enum VisitorFlags { + NewLexicalEnvironment = 1 << 1, + PreserveStack = 1 << 2, + ReturnUndefinedIfEmpty = 1 << 3, + } + export namespace transform { + // Flags enum to track count of temp variables and a few dedicated names + const enum TempFlags { + Auto = 0x00000000, // No preferred name + CountMask = 0x0FFFFFFF, // Temp variable counter + _i = 0x10000000, // Use/preference flag for '_i' + } + let transformationRunning: boolean; let transformFlags: TransformFlags; let generatedNameSet: Map; @@ -277,10 +284,7 @@ namespace ts { let resolver: EmitResolver; // node stack - let nodeStackSize: number; - let ancestorStack: Node[]; - let parentNode: Node; - let currentNode: Node; + let nodeStack: NodeStack; // single node transform let updatedNode: Node; @@ -315,9 +319,8 @@ namespace ts { generatedNameSet = _generatedNameSet; nodeToGeneratedName = _nodeToGeneratedName; nodeToGeneratedIdentifier = []; - ancestorStack = []; - nodeStackSize = 1; - currentNode = _currentSourceFile; + nodeStack = createNodeStack(); + nodeStack.pushNode(_currentSourceFile); transformationRunning = true; } @@ -329,18 +332,14 @@ namespace ts { generatedNameSet = undefined; nodeToGeneratedName = undefined; nodeToGeneratedIdentifier = undefined; - ancestorStack = undefined; - nodeStackSize = undefined; - currentNode = undefined; + nodeStack = undefined; transformationRunning = false; } - - // Return the next available name in the pattern _a ... _z, _0, _1, ... // TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name. // Note that names generated by makeTempVariableName and makeUniqueName will never conflict. - export function makeTempVariableName(flags: TempFlags): string { + function makeTempVariableName(flags: TempFlags): string { if (flags && !(tempFlags & flags)) { let name = flags === TempFlags._i ? "_i" : "_n"; if (isUniqueName(name)) { @@ -458,21 +457,6 @@ namespace ts { return makeUniqueName("class"); } - function pushNode(node: Node): void { - nodeStackSize++; - if (nodeStackSize > 2) { - ancestorStack.push(parentNode); - } - parentNode = currentNode; - currentNode = node; - } - - function popNode(): void { - currentNode = parentNode; - parentNode = nodeStackSize > 2 ? ancestorStack.pop() : undefined; - nodeStackSize--; - } - export function getEmitResolver(): EmitResolver { return resolver; } @@ -481,32 +465,22 @@ namespace ts { return compilerOptions; } + export function createParentNavigator(): ParentNavigator { + return nodeStack.createParentNavigator(); + } + export function getCurrentNode(): Node { - return currentNode; + return nodeStack.getNode(); } export function getParentNode(): Node { - return parentNode; + return nodeStack.getParent(); } - export function peekNode(offset: number): Node { - switch (offset) { - case 0: return currentNode; - case 1: return parentNode; - default: return nodeStackSize > 2 ? ancestorStack[nodeStackSize - 1 - offset] : undefined; - } - } - export function findAncestorNode(match: (node: Node) => node is T): T; export function findAncestorNode(match: (node: Node) => boolean): Node; export function findAncestorNode(match: (node: Node) => boolean) { - for (let i = 1; i < nodeStackSize; i++) { - let node = peekNode(i); - if (match(node)) { - return node; - } - } - return undefined; + return nodeStack.findAncestorNode(match); } export function getDeclarationName(node: DeclarationStatement): Identifier; @@ -698,12 +672,12 @@ namespace ts { updatedNodes.push(node); } - function readNodeArray(returnUndefinedIfEmpty?: boolean): NodeArray { + function readNodeArray(flags: VisitorFlags): NodeArray { if (updatedNodes) { return factory.createNodeArray(updatedNodes, /*location*/ >originalNodes); } else if (offsetWritten !== originalNodes.length) { - if (offsetWritten === 0 && returnUndefinedIfEmpty) { + if (offsetWritten === 0 && (flags & VisitorFlags.ReturnUndefinedIfEmpty)) { return undefined; } else { @@ -715,31 +689,42 @@ namespace ts { } } - type Visitor = (input: Node, output: (node: Node) => void) => void; - - function pipeOne(input: Node, output: (node: Node) => void, visitor: Visitor): void { - pushNode(input); - visitor(input, output); - popNode(); - } - - function pipeMany(input: Node[], output: (node: Node) => void, visitor: Visitor): void { - // For perf reasons, we push `undefined` as the current node and set it to the correct - // value for each iteration of the loop below. This avoids excessive push and pop - // operations on `ancestorStack`. - pushNode(/*node*/ undefined); - - // Visit each input node - for (let i = 0, l = input.length; i < l; ++i) { - currentNode = input[i]; - visitor(currentNode, output); + function pipeOne(input: Node, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags): void { + if (!(flags & VisitorFlags.PreserveStack)) { + nodeStack.pushNode(input); + visitor(input, output); + nodeStack.popNode(); + } + else { + visitor(input, output); } - - // For the perf reasons mentioned above, we pop the current node at the end of the loop. - popNode(); } - function pipeOneOrMany(input: T, output: (node: Node) => void, visitor: Visitor, newLexicalEnvironment: boolean, pipe: (input: T, output: (node: Node) => void, visitor: Visitor) => void): void { + function pipeMany(input: Node[], output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags): void { + if (!(flags & VisitorFlags.PreserveStack)) { + // For perf reasons, we push `undefined` as the current node and set it to the correct + // value for each iteration of the loop below. This avoids excessive push and pop + // operations on `ancestorStack`. + nodeStack.pushNode(/*node*/ undefined); + + // Visit each input node + for (let i = 0, l = input.length; i < l; ++i) { + let currentNode = input[i]; + nodeStack.setNode(currentNode); + visitor(currentNode, output); + } + + // For the perf reasons mentioned above, we pop the current node at the end of the loop. + nodeStack.popNode(); + } + else { + for (let node of input) { + visitor(node, output); + } + } + } + + function pipeOneOrMany(input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags, pipe: (input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags) => void): void { if (!input) { return; } @@ -751,7 +736,7 @@ namespace ts { // If we are starting a new lexical environment, we need to reinitialize the lexical // environment state as well - if (newLexicalEnvironment) { + if (flags & VisitorFlags.NewLexicalEnvironment) { savedTempFlags = tempFlags; savedHoistedVariableDeclarations = hoistedVariableDeclarations; savedHoistedFunctionDeclarations = hoistedFunctionDeclarations; @@ -761,11 +746,11 @@ namespace ts { hoistedFunctionDeclarations = undefined; } - pipe(input, output, visitor); + pipe(input, output, visitor, flags & ~VisitorFlags.NewLexicalEnvironment); // If we established a new lexical environment, we need to write any hoisted variables or // function declarations to the end of the output. - if (newLexicalEnvironment) { + if (flags & VisitorFlags.NewLexicalEnvironment) { if (hoistedVariableDeclarations) { output(factory.createVariableStatement2( factory.createVariableDeclarationList( @@ -793,8 +778,8 @@ namespace ts { * @param visitor The callback to execute as we visit each node in the source. * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. */ - export function pipeNode(input: Node, output: (node: Node) => void, visitor: Visitor, newLexicalEnvironment?: boolean): void { - pipeOneOrMany(input, output, visitor, newLexicalEnvironment, pipeOne); + export function pipeNode(input: Node, output: (node: Node) => void, visitor: Visitor, flags?: VisitorFlags): void { + pipeOneOrMany(input, output, visitor, flags, pipeOne); } /** @@ -804,11 +789,11 @@ namespace ts { * @param visitor The callback to execute as we visit each node in the source. * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. */ - export function pipeNodes(input: Node[], output: (node: Node) => void, visitor: Visitor, newLexicalEnvironment?: boolean): void { - pipeOneOrMany(input, output, visitor, newLexicalEnvironment, pipeMany); + export function pipeNodes(input: Node[], output: (node: Node) => void, visitor: Visitor, flags?: VisitorFlags): void { + pipeOneOrMany(input, output, visitor, flags, pipeMany); } - function emitOneOrMany(input: T, output: Node[], visitor: Visitor, newLexicalEnvironment: boolean, pipe: (input: T, output: (node: Node) => void, visitor: Visitor, newLexicalEnvironment: boolean) => void): void { + function emitOneOrMany(input: T, output: Node[], visitor: Visitor, flags: VisitorFlags, pipe: (input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags) => void): void { // Exit early if we have nothing to do if (!input) { return; @@ -826,7 +811,7 @@ namespace ts { offsetWritten = 0; writeNodeToNodeArrayFastOrSlow = writeNodeToNodeArrayFast; - pipe(input, writeNodeToNodeArray, visitor, newLexicalEnvironment); + pipe(input, writeNodeToNodeArray, visitor, flags | VisitorFlags.PreserveStack); // Restore previous environment originalNodes = savedOriginalNodes; @@ -842,8 +827,8 @@ namespace ts { * @param visitor The callback to execute as we visit each node in the source. * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. */ - export function emitNode(input: Node, output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, newLexicalEnvironment?: boolean): void { - emitOneOrMany(input, output, visitor, newLexicalEnvironment, pipeOne); + export function emitNode(input: Node, output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, flags?: VisitorFlags): void { + emitOneOrMany(input, output, visitor, flags, pipeOne); } /** @@ -853,13 +838,15 @@ namespace ts { * @param visitor The callback to execute as we visit each node in the source. * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. */ - export function emitNodes(input: Node[], output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, newLexicalEnvironment?: boolean): void { - emitOneOrMany(input, output, visitor, newLexicalEnvironment, pipeMany); + export function emitNodes(input: Node[], output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, flags?: VisitorFlags): void { + emitOneOrMany(input, output, visitor, flags, pipeMany); } - export function visitNode(node: T, visitor: (input: Node, write: (node: Node) => void) => void): T { + export function visitNode(node: T, visitor: (input: Node, write: (node: Node) => void) => void, flags?: VisitorFlags): T; + export function visitNode(node: TIn, visitor: (input: TIn, write: (node: TOut) => void) => void, flags?: VisitorFlags): TOut; + export function visitNode(node: TIn, visitor: (input: TIn, write: (node: TOut) => void) => void, flags?: VisitorFlags): TOut { if (!node) { - return node; + return undefined; } let savedUpdatedNode = updatedNode; @@ -867,16 +854,16 @@ namespace ts { updatedNode = undefined; writeNodeFastOrSlow = writeNodeSlow; - pipeNode(node, writeNode, visitor); + pipeNode(node, writeNode, visitor, flags); - let visited = readNode(); + let visited = readNode(); updatedNode = savedUpdatedNode; writeNodeFastOrSlow = savedWriteOneNode; return visited; } - export function visitStatement(node: Statement, visitor: (input: Node, write: (node: Node) => void) => void) { + export function visitStatement(node: Statement, visitor: (input: Node, write: (node: Node) => void) => void, flags?: VisitorFlags) { if (!node) { return node; } @@ -889,7 +876,7 @@ namespace ts { updatedBlock = undefined; writeStatementFastOrSlow = writeStatementSlow; - pipeNode(node, writeStatement, visitor); + pipeNode(node, writeStatement, visitor, flags); let visited = readStatement(); updatedStatement = savedUpdatedStatement; @@ -899,10 +886,9 @@ namespace ts { return visited; } - export function visitNodes(nodes: Statement[], visitor: (input: Node, output: (node: Node) => void) => void, newLexicalEnvironment: boolean): NodeArray; - export function visitNodes(nodes: T[], visitor: (input: Node, output: (node: Node) => void) => void): NodeArray; - export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, newLexicalEnvironment?: boolean, returnUndefinedIfEmpty?: boolean): NodeArray; - export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, newLexicalEnvironment?: boolean, returnUndefinedIfEmpty?: boolean): NodeArray { + export function visitNodes(nodes: T[], visitor: (input: Node, output: (node: Node) => void) => void, flags?: VisitorFlags): NodeArray; + export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, flags?: VisitorFlags): NodeArray; + export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, flags?: VisitorFlags): NodeArray { // Exit early if we have nothing to do if (!nodes) { return undefined; @@ -921,9 +907,9 @@ namespace ts { writeNodeToNodeArrayFastOrSlow = writeNodeToNodeArraySlow; // Pipe each node from input into output - pipeNodes(originalNodes, writeNodeToNodeArray, visitor, newLexicalEnvironment); + pipeNodes(originalNodes, writeNodeToNodeArray, visitor, flags); - let visited = readNodeArray(returnUndefinedIfEmpty); + let visited = readNodeArray(flags); // Restore previous environment originalNodes = savedOriginalNodes; @@ -973,13 +959,13 @@ namespace ts { case SyntaxKind.Block: return factory.updateBlock( node, - visitNodes((node).statements, visitor, /*newLexicalEnvironment*/ true) + visitNodes((node).statements, visitor, VisitorFlags.NewLexicalEnvironment) ); case SyntaxKind.ModuleBlock: return factory.updateModuleBlock( node, - visitNodes((node).statements, visitor, /*newLexicalEnvironment*/ true) + visitNodes((node).statements, visitor, VisitorFlags.NewLexicalEnvironment) ); default: diff --git a/src/compiler/transforms/es5.ts b/src/compiler/transforms/es5.ts index 21e5dc434d4..b26b05ec300 100644 --- a/src/compiler/transforms/es5.ts +++ b/src/compiler/transforms/es5.ts @@ -2,7 +2,7 @@ /*@internal*/ namespace ts.transform { export function toES5(statements: NodeArray) { - return visitNodes(statements, transformNode, /*newLexicalEnvironment*/ true); + return visitNodes(statements, transformNode, VisitorFlags.NewLexicalEnvironment); } /** @@ -153,7 +153,7 @@ namespace ts.transform { let body = factory.createBlock([]); if (constructor) { - emitNode(constructor, body.statements, transformConstructor, /*newLexicalEnvironment*/ true); + emitNode(constructor, body.statements, transformConstructor, VisitorFlags.NewLexicalEnvironment); } else if (baseTypeNode) { let superCall = createDefaultSuperCall(); @@ -453,14 +453,14 @@ namespace ts.transform { function rewriteFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { let parameters = visitNodes(node.parameters, transformNode); let body = factory.createBlock([], node.body); - emitNode(node, body.statements, transformFunctionBody, /*newLexicalEnvironment*/ true); + emitNode(node, body.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); return factory.createFunctionExpression2(name, parameters, body, location); } function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { let parameters = visitNodes(node.parameters, transformNode); let body = factory.createBlock([], node.body); - emitNode(node, body.statements, transformFunctionBody, /*newLexicalEnvironment*/ true); + emitNode(node, body.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); write(factory.createFunctionDeclaration2(node.name, parameters, body, /*location*/ node)); } @@ -469,7 +469,7 @@ namespace ts.transform { let parameters = visitNodes(node.parameters, transformNode); let newBody = factory.createBlock([], /*location*/ isBlock(node.body) ? node.body : undefined); - emitNode(node, newBody.statements, transformFunctionBody, /*newLexicalEnvironment*/ true); + emitNode(node, newBody.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); let func = createfn(name, parameters, newBody, location); return func; @@ -526,7 +526,7 @@ namespace ts.transform { } function transformThisKeyword(node: LeftHandSideExpression): LeftHandSideExpression { - let container = getThisContainer(node, /*includeArrowFunctions*/ true); + let container = getThisContainer(transform, /*includeArrowFunctions*/ true); if (isArrowFunction(container)) { let thisName = factory.createIdentifier("_this"); return thisName; diff --git a/src/compiler/transforms/es6.ts b/src/compiler/transforms/es6.ts index ee725760316..c04cd7d607a 100644 --- a/src/compiler/transforms/es6.ts +++ b/src/compiler/transforms/es6.ts @@ -9,7 +9,7 @@ namespace ts.transform { resolver = getEmitResolver(); compilerOptions = getCompilerOptions(); languageVersion = compilerOptions.target || ScriptTarget.ES3; - return visitNodes(statements, transformNode, /*newLexicalEnvironment*/ true); + return visitNodes(statements, transformNode, VisitorFlags.NewLexicalEnvironment); } /** @@ -74,24 +74,6 @@ namespace ts.transform { } switch (node.kind) { - // case SyntaxKind.Block: - // case SyntaxKind.ModuleBlock: - // case SyntaxKind.CaseClause: - // case SyntaxKind.DefaultClause: - // // These nodes contain statement lists which may need to be expanded to include multiple statements... - // // See NodeArraywrite in transform.ts for approach - - // case SyntaxKind.DoStatement: - // case SyntaxKind.WhileStatement: - // case SyntaxKind.ForStatement: - // case SyntaxKind.ForInStatement: - // case SyntaxKind.ForOfStatement: - // case SyntaxKind.IfStatement: - // case SyntaxKind.LabeledStatement: - // case SyntaxKind.WithStatement: - // // These nodes contain single statement nodes that may need to be switched to a block if a child node needs multiple statements... - // // See Statementwrite in transform.ts for approach - case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: @@ -100,6 +82,7 @@ namespace ts.transform { case SyntaxKind.ConstKeyword: case SyntaxKind.DeclareKeyword: // TypeScript accessibility modifiers are elided. + return; case SyntaxKind.ArrayType: case SyntaxKind.TupleType: @@ -118,31 +101,37 @@ namespace ts.transform { case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: // TypeScript type nodes are elided. + return; case SyntaxKind.IndexSignature: // TypeScript index signatures are elided. + return; case SyntaxKind.Decorator: // TypeScript decorators are elided. They will be emitted as part of transformClassDeclaration. + return; case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: // TypeScript type-only declarations are elided + return; case SyntaxKind.PropertyDeclaration: // TypeScript property declarations are elided. + return; case SyntaxKind.IndexSignature: // TypeScript index signatures are elided. + return; case SyntaxKind.Constructor: // TypeScript constructors are elided. The constructor of a class will be // reordered to the start of the member list in `transformClassDeclaration`. + return; case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: // TypeScript interfaces and type aliases are elided. - return; case SyntaxKind.ClassDeclaration: @@ -233,9 +222,9 @@ namespace ts.transform { // TypeScript 'await' expressions must be transformed. return transformAwaitExpression(node, write); - // case SyntaxKind.VariableStatement: - // // TypeScript namespace exports for variable statements must be transformed. - // return transformVariableStatement(node, write); + case SyntaxKind.VariableStatement: + // TypeScript namespace exports for variable statements must be transformed. + return transformVariableStatement(node, write); case SyntaxKind.ModuleDeclaration: // TypeScript namespace declarations must be transformed. @@ -362,7 +351,7 @@ namespace ts.transform { let name = getDeclarationName(node); Debug.assert(isIdentifier(name)); - if (getCombinedNodeFlags(node) & NodeFlags.Export) { + if (getCombinedNodeFlags(transform) & NodeFlags.Export) { let container = getContainingModuleName(); let propExpr = factory.createPropertyAccessExpression2(container, name); return propExpr; @@ -788,16 +777,27 @@ namespace ts.transform { } function transformVariableDeclarationList(node: VariableDeclarationList, write: (node: Statement) => void) { - let expressions = visitNodes(node.declarations, transformVariableDeclaration, /*newLexicalEnvironment*/ false, /*returnUndefinedIfEmpty*/ true); - if (expressions) { + let expressions: Expression[] = []; + emitNodes(node.declarations, expressions, transformVariableDeclaration); + + if (expressions.length) { let exprStmt = factory.createExpressionStatement(factory.inlineExpressions(expressions)); write(exprStmt); } } function transformVariableDeclaration(node: VariableDeclaration, write: (node: Expression) => void) { - if (isBindingPattern(node.name)) { - Debug.fail("Transform not yet supported."); + if (!node.initializer) { + return; + } + + let name = node.name; + if (isBindingPattern(name)) { + let expr = visitNode(name, transformBindingPatternToExpression); + let initializer = visitNode(node.initializer, transformNode); + let assignExpr = factory.createAssignmentExpression(expr, initializer); + let parenExpr = factory.createParenthesizedExpression(assignExpr); + write(parenExpr); } else { let name = getModuleMemberName(node); @@ -806,6 +806,27 @@ namespace ts.transform { write(assignExpr); } } + + function transformBindingPatternToExpression(node: BindingPattern, write: (node: Expression) => void) { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + return transformObjectBindingPatternToExpression(node, write); + + case SyntaxKind.ArrayBindingPattern: + return transformArrayBindingPatternToExpression(node, write); + } + } + + function transformObjectBindingPatternToExpression(node: ObjectBindingPattern, write: (node: Expression) => void) { + let properties: ObjectLiteralElement[] = []; + + write(factory.createObjectLiteralExpression2(properties)); + } + + function transformArrayBindingPatternToExpression(node: ArrayBindingPattern, write: (node: Expression) => void) { + let elements: Expression[] = []; + write(factory.createArrayLiteralExpression(elements)); + } function transformModuleDeclaration(node: ModuleDeclaration, write: (node: Statement) => void) { if (!shouldEmitModuleDeclaration(node)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d6323e42d03..513b1d80be1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1830,8 +1830,10 @@ namespace ts { } // Source files are declarations when they are external modules. - // @kind(SyntaxKind.SourceFile) - // @factoryhidden + // @kind(SyntaxKind.SourceFile, { create: false, update: false }) + // @factoryhidden("decorators") + // @factoryhidden("modifiers") + // @factoryhidden("name") export interface SourceFile extends Declaration { statements: NodeArray; endOfFileToken: Node; @@ -1861,6 +1863,7 @@ namespace ts { languageVersion: ScriptTarget; // The first node that causes this file to be an external module + // @factoryhidden /* @internal */ externalModuleIndicator: Node; /* @internal */ isDefaultLib: boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3fa30cddb18..a5f52303230 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -391,7 +391,7 @@ namespace ts { return flags; } - + export function isConst(navigable: ParentNavigable): boolean { return !!(getCombinedNodeFlags(navigable) & NodeFlags.Const); } @@ -717,7 +717,7 @@ namespace ts { break; case SyntaxKind.Decorator: // Decorators are always applied outside of the body of a class or method. - if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) { + if (isParameter(nav.getParent()) && isClassElement(nav.getGrandparent())) { // If the decorator's parent is a Parameter, we resolve the this container from // the grandparent class declaration. nav.moveToParent(); @@ -773,7 +773,8 @@ namespace ts { break; case SyntaxKind.Decorator: // Decorators are always applied outside of the body of a class or method. - if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) { + if (isParameter(nav.getParent()) + && isClassElement(nav.getGrandparent())) { // If the decorator's parent is a Parameter, we resolve the this container from // the grandparent class declaration. nav.moveToParent(); diff --git a/src/services/services.ts b/src/services/services.ts index 27bc4ae36cb..cb5860d8c9f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -225,7 +225,7 @@ namespace ts { public getText(sourceFile?: SourceFile): string { return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); } - + private addSyntheticNodes(nodes: Node[], pos: number, end: number): number { scanner.setTextPos(pos); while (pos < end) {